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
185185 set_property(DIRECTORY PROPERTY EP_PREFIX ${EXTERNAL_PROJECT_PREFIX})
186186 setup_externals_binary_dir()
187187
188−# setup options to grab external project dependencies
189−option(GET_BULLET "Get Bullet library automatically as external project" 1)
190−option(GET_GLM "Get GLM library automatically as external project" 1)
191−option(GET_GVERB "Get Gverb library automatically as external project" 1)
192−option(GET_TBB "Get Threading Building Blocks library automatically as external project" 1)
193−option(GET_LIBOVR "Get LibOVR library automatically as external project" 1)
194−option(GET_VHACD "Get V-HACD library automatically as external project" 1)
195−option(GET_POLYVOX "Get polyvox library automatically as external project" 1)
196−option(GET_OPENVR "Get OpenVR library automatically as external project" 1)
197−option(GET_BOOSTCONFIG "Get Boost-config library automatically as external project" 1)
198−option(GET_OGLPLUS "Get OGLplus library automatically as external project" 1)
199−option(GET_GLEW "Get GLEW library automatically as external project" 1)
200−
201188 option(USE_NSIGHT "Attempt to find the nSight libraries" 1)
202189
203−option(GET_SDL2 "Get SDL2 library automatically as external project" 0)
204190
205191 if (WIN32)
206192 add_paths_to_fixup_libs("${QT_DIR}/bin")
assignment-client/CMakeLists.txt+2−2
@@ -1,12 +1,12 @@
11 set(TARGET_NAME assignment-client)
22
3−setup_hifi_project(Core Gui Network Script Widgets WebSockets)
3+setup_hifi_project(Core Gui Network Script Quick Widgets WebSockets)
44
55 # link in the shared libraries
66 link_hifi_libraries(
77 audio avatars octree environment gpu model fbx entities
88 networking animation shared script-engine embedded-webserver
9− physics
9+ controllers physics
1010 )
1111
1212 include_application_version()
cmake/externals/faceshift/CMakeLists.txt+30−0
@@ -0,0 +1,30 @@
1+set(EXTERNAL_NAME faceshift)
2+
3+include(ExternalProject)
4+ExternalProject_Add(
5+ ${EXTERNAL_NAME}
6+ URL https://hifi-public.s3.amazonaws.com/dependencies/faceshift.zip
7+ CMAKE_ARGS ${ANDROID_CMAKE_ARGS} -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
8+ BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build
9+ LOG_DOWNLOAD 1
10+ LOG_CONFIGURE 1
11+ LOG_BUILD 1
12+)
13+
14+# URL_MD5 1bdcb8a0b8d5b1ede434cc41efade41d
15+
16+# Hide this external target (for ide users)
17+set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
18+
19+ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR)
20+
21+string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
22+set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR}/include CACHE FILEPATH "Path to Faceshift include directory")
23+
24+if (WIN32)
25+ set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/lib/Debug/faceshift.lib CACHE FILEPATH "Faceshift libraries")
26+ set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/Release/faceshift.lib CACHE FILEPATH "Faceshift libraries")
27+elseif (APPLE)
28+ set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/lib/Debug/libfaceshift.a CACHE FILEPATH "Faceshift libraries")
29+ set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/Release/libfaceshift.a CACHE FILEPATH "Faceshift libraries")
30+endif()
cmake/externals/sdl2/CMakeLists.txt+9−21
@@ -47,25 +47,13 @@ if (APPLE) · 46 unmodified lines
4747 ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR)
4848 set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIR ${SOURCE_DIR}/SDL2.framework/Headers CACHE PATH "Location of SDL2 include directory")
4949 set(${EXTERNAL_NAME_UPPER}_LIBRARY_TEMP ${SOURCE_DIR}/SDL2.framework/SDL2 CACHE STRING "Path to SDL2 library")
50+elseif (WIN32)
51+ ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR)
52+ set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIR ${SOURCE_DIR}/include CACHE PATH "Location of SDL2 include directory")
53+ set(${EXTERNAL_NAME_UPPER}_LIBRARY_TEMP ${SOURCE_DIR}/lib/x86/SDL2.lib CACHE FILEPATH "Path to SDL2 library")
54+ set(${EXTERNAL_NAME_UPPER}_DLL_PATH ${SOURCE_DIR}/lib/x86 CACHE PATH "Location of SDL2 DLL")
5055 else ()
51− if (WIN32)
52− ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR)
53− set(_ROOT_DIR ${SOURCE_DIR})
54− set(_INCLUDE_DIR ${_ROOT_DIR}/include)
55− set(_LIB_DIR "${SOURCE_DIR}/lib/x86")
56− set(_LIB_EXT "lib")
57−
58− set(${EXTERNAL_NAME_UPPER}_DLL_PATH ${_LIB_DIR} CACHE PATH "Location of SDL2 DLL")
59− else ()
60− ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR)
61− set(_ROOT_DIR ${INSTALL_DIR})
62− set(_INCLUDE_DIR ${_ROOT_DIR}/include/SDL2)
63−
64− set(_LIB_DIR ${INSTALL_DIR}/lib)
65− set(_LIB_EXT "so")
66− set(_LIB_PREFIX "lib")
67− endif ()
68−
69− set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIR ${_INCLUDE_DIR} CACHE PATH "Location of SDL2 include directory")
70− set(${EXTERNAL_NAME_UPPER}_LIBRARY_TEMP ${_LIB_DIR}/${_LIB_PREFIX}SDL2.${_LIB_EXT} CACHE FILEPATH "Path to SDL2 library")
71−endif ()
56+ ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR)
57+ set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIR ${INSTALL_DIR}/include/SDL2 CACHE PATH "Location of SDL2 include directory")
58+ set(${EXTERNAL_NAME_UPPER}_LIBRARY_TEMP ${INSTALL_DIR}/lib/libSDL2.so CACHE FILEPATH "Path to SDL2 library")
59+endif ()
cmake/externals/sixense/CMakeLists.txt+11−25
@@ -1,30 +1,20 @@
11 include(ExternalProject)
2−include(SelectLibraryConfigurations)
32
4−set(EXTERNAL_NAME Sixense)
3+set(EXTERNAL_NAME sixense)
54
65 string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
76
87 ExternalProject_Add(
98 ${EXTERNAL_NAME}
10− URL ./SixenseSDK_062612.zip
11− URL_MD5 10cc8dc470d2ac1244a88cf04bc549cc
9+ URL http://hifi-public.s3.amazonaws.com/dependencies/SixenseSDK_071615.zip
10+ URL_MD5 752a3901f334124e9cffc2ba4136ef7d
1211 CONFIGURE_COMMAND ""
1312 BUILD_COMMAND ""
1413 INSTALL_COMMAND ""
1514 LOG_DOWNLOAD 1
1615 )
1716
18−if (APPLE)
19− find_library(SIXENSE_LIBRARY_RELEASE lib/osx_x64/release_dll/libsixense_x64.dylib HINTS ${SIXENSE_SEARCH_DIRS})
20− find_library(SIXENSE_LIBRARY_DEBUG lib/osx_x64/debug_dll/libsixensed_x64.dylib HINTS ${SIXENSE_SEARCH_DIRS})
21−elseif (UNIX)
22− find_library(SIXENSE_LIBRARY_RELEASE lib/linux_x64/release/libsixense_x64.so HINTS ${SIXENSE_SEARCH_DIRS})
23− # find_library(SIXENSE_LIBRARY_DEBUG lib/linux_x64/debug/libsixensed_x64.so HINTS ${SIXENSE_SEARCH_DIRS})
24−elseif (WIN32)
25−endif ()
26−
27−
17+set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
2818
2919 ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR)
3020
@@ -39,22 +29,18 @@ if (WIN32) · 8 unmodified lines
3929 set(ARCH_DIR "Win32")
4030 set(ARCH_SUFFIX "")
4131 endif()
42−
43− # FIXME need to account for different architectures
44− set(${EXTERNAL_NAME_UPPER}_LIBRARIES "${SOURCE_DIR}/lib/${ARCH_DIR}/release_dll/sixense${ARCH_SUFFIX}.lib" CACHE TYPE INTERNAL)
45− add_paths_to_fixup_libs(${SOURCE_DIR}/bin/win32)
32+
33+ set(${EXTERNAL_NAME_UPPER}_LIBRARIES "${SOURCE_DIR}/lib/${ARCH_DIR}/VS2013/release_dll/sixense${ARCH_SUFFIX}.lib" CACHE TYPE INTERNAL)
34+ add_paths_to_fixup_libs("${SOURCE_DIR}/bin/${ARCH_DIR}/VS2013/release_dll")
4635
4736 elseif(APPLE)
4837
49− # FIXME need to account for different architectures
50− set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/lib/osx_x64/release_dll/libsixense_x64.dylib CACHE TYPE INTERNAL)
51− add_paths_to_fixup_libs(${SOURCE_DIR}/bin/osx32)
38+ set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/lib/osx_x64/release_dll/libsixense_x64.dylib CACHE TYPE INTERNAL)
5239
5340 elseif(NOT ANDROID)
5441
55− # FIXME need to account for different architectures
56− set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/lib/linux_x64/release/libsixense_x64.so CACHE TYPE INTERNAL)
57− add_paths_to_fixup_libs(${SOURCE_DIR}/bin/linux32)
58−
42+ # FIXME need to account for different architectures
43+ set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/lib/linux_x64/release/libsixense_x64.so CACHE TYPE INTERNAL)
44+
5945 endif()
6046
cmake/macros/AddDependencyExternalProjects.cmake+1−1
@@ -16,7 +16,7 @@ macro(ADD_DEPENDENCY_EXTERNAL_PROJECTS) · 15 unmodified lines
1616 string(TOUPPER ${_PROJ_NAME} _PROJ_NAME_UPPER)
1717
1818 # 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})
2020 # have we already detected we can't have this as external project on this OS?
2121 if (NOT DEFINED ${_PROJ_NAME_UPPER}_EXTERNAL_PROJECT OR ${_PROJ_NAME_UPPER}_EXTERNAL_PROJECT)
2222 # 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+#
8+macro(TARGET_FACESHIFT)
9+ add_dependency_external_projects(faceshift)
10+ find_package(Faceshift REQUIRED)
11+ target_include_directories(${TARGET_NAME} PRIVATE ${FACESHIFT_INCLUDE_DIRS})
12+ target_link_libraries(${TARGET_NAME} ${FACESHIFT_LIBRARIES})
13+ add_definitions(-DHAVE_FACESHIFT)
14+endmacro()
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+#
8+macro(TARGET_SDL2)
9+ add_dependency_external_projects(sdl2)
10+ find_package(SDL2 REQUIRED)
11+ target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${SDL2_INCLUDE_DIR})
12+ target_link_libraries(${TARGET_NAME} ${SDL2_LIBRARY})
13+ add_definitions(-DHAVE_SDL2)
14+endmacro()
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+#
8+macro(TARGET_SIXENSE)
9+ add_dependency_external_projects(sixense)
10+ find_package(Sixense REQUIRED)
11+ target_include_directories(${TARGET_NAME} PRIVATE ${SIXENSE_INCLUDE_DIRS})
12+ target_link_libraries(${TARGET_NAME} ${SIXENSE_LIBRARIES})
13+ add_definitions(-DHAVE_SIXENSE)
14+endmacro()
cmake/modules/FindFaceshift.cmake+0−23
@@ -18,32 +18,9 @@ · 17 unmodified lines
1818 # See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
1919 #
2020
21−include("${MACRO_DIR}/HifiLibrarySearchHints.cmake")
22−hifi_library_search_hints("faceshift")
23−
24−find_path(FACESHIFT_INCLUDE_DIRS fsbinarystream.h PATH_SUFFIXES include HINTS ${FACESHIFT_SEARCH_DIRS})
25−
26−if (APPLE)
27− set(ARCH_DIR "MacOS")
28−elseif (UNIX)
29− set(ARCH_DIR "UNIX")
30−elseif (WIN32)
31− if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
32− set(ARCH_DIR "x64")
33− else()
34− set(ARCH_DIR "Win32")
35− endif()
36−endif ()
37−
38−find_library(FACESHIFT_LIBRARY_RELEASE NAME faceshift PATH_SUFFIXES lib/${ARCH_DIR} HINTS ${FACESHIFT_SEARCH_DIRS})
39−find_library(FACESHIFT_LIBRARY_DEBUG NAME faceshiftd PATH_SUFFIXES lib/${ARCH_DIR} HINTS ${FACESHIFT_SEARCH_DIRS})
40−
4121 include(SelectLibraryConfigurations)
4222 select_library_configurations(FACESHIFT)
4323
44−set(FACESHIFT_LIBRARIES ${FACESHIFT_LIBRARY})
45−
4624 include(FindPackageHandleStandardArgs)
4725 find_package_handle_standard_args(Faceshift DEFAULT_MSG FACESHIFT_INCLUDE_DIRS FACESHIFT_LIBRARIES)
48−
4926 mark_as_advanced(FACESHIFT_INCLUDE_DIRS FACESHIFT_LIBRARIES FACESHIFT_SEARCH_DIRS)
cmake/modules/FindSixense.cmake+1−44
@@ -18,49 +18,6 @@ · 17 unmodified lines
1818 # See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
1919 #
2020
21−include("${MACRO_DIR}/HifiLibrarySearchHints.cmake")
22−hifi_library_search_hints("sixense")
23−
24−find_path(SIXENSE_INCLUDE_DIRS sixense.h PATH_SUFFIXES include HINTS ${SIXENSE_SEARCH_DIRS})
25−
26−if (APPLE)
27− find_library(SIXENSE_LIBRARY_RELEASE lib/osx_x64/release_dll/libsixense_x64.dylib HINTS ${SIXENSE_SEARCH_DIRS})
28− find_library(SIXENSE_LIBRARY_DEBUG lib/osx_x64/debug_dll/libsixensed_x64.dylib HINTS ${SIXENSE_SEARCH_DIRS})
29−elseif (UNIX)
30− find_library(SIXENSE_LIBRARY_RELEASE lib/linux_x64/release/libsixense_x64.so HINTS ${SIXENSE_SEARCH_DIRS})
31− # find_library(SIXENSE_LIBRARY_DEBUG lib/linux_x64/debug/libsixensed_x64.so HINTS ${SIXENSE_SEARCH_DIRS})
32−elseif (WIN32)
33− if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
34− set(ARCH_DIR "x64")
35− set(ARCH_SUFFIX "_x64")
36− else()
37− set(ARCH_DIR "Win32")
38− set(ARCH_SUFFIX "")
39− endif()
40−
41− find_library(SIXENSE_LIBRARY_RELEASE "lib/${ARCH_DIR}/release_dll/sixense${ARCH_SUFFIX}.lib" HINTS ${SIXENSE_SEARCH_DIRS})
42− find_library(SIXENSE_LIBRARY_DEBUG "lib/${ARCH_DIR}/debug_dll/sixensed.lib" HINTS ${SIXENSE_SEARCH_DIRS})
43−
44− find_path(SIXENSE_DEBUG_DLL_PATH "sixensed${ARCH_SUFFIX}.dll" PATH_SUFFIXES bin/${ARCH_DIR}/debug_dll HINTS ${SIXENSE_SEARCH_DIRS})
45− find_path(SIXENSE_RELEASE_DLL_PATH "sixense${ARCH_SUFFIX}.dll" PATH_SUFFIXES bin/${ARCH_DIR}/release_dll HINTS ${SIXENSE_SEARCH_DIRS})
46− find_path(SIXENSE_DEVICE_DLL_PATH DeviceDLL.dll PATH_SUFFIXES samples/${ARCH_DIR}/sixense_simple3d HINTS ${SIXENSE_SEARCH_DIRS})
47−endif ()
48−
49−include(SelectLibraryConfigurations)
50−select_library_configurations(SIXENSE)
51−
52−set(SIXENSE_REQUIREMENTS SIXENSE_INCLUDE_DIRS SIXENSE_LIBRARIES)
53−if (WIN32)
54− list(APPEND SIXENSE_REQUIREMENTS SIXENSE_DEBUG_DLL_PATH SIXENSE_RELEASE_DLL_PATH SIXENSE_DEVICE_DLL_PATH)
55−endif ()
56−
57−set(SIXENSE_LIBRARIES "${SIXENSE_LIBRARY}")
58−
5921 include(FindPackageHandleStandardArgs)
60−find_package_handle_standard_args(Sixense DEFAULT_MSG ${SIXENSE_REQUIREMENTS})
61−
62−if (WIN32)
63− add_paths_to_fixup_libs(${SIXENSE_DEBUG_DLL_PATH} ${SIXENSE_RELEASE_DLL_PATH} ${SIXENSE_DEVICE_DLL_PATH})
64−endif ()
65−
22+find_package_handle_standard_args(Sixense DEFAULT_MSG SIXENSE_INCLUDE_DIRS SIXENSE_LIBRARIES)
6623 mark_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
2121 hifi_library_search_hints("iViewHMD")
2222
2323 find_path(IVIEWHMD_INCLUDE_DIRS iViewHMDAPI.h PATH_SUFFIXES include HINTS ${IVIEWHMD_SEARCH_DIRS})
24− find_library(IVIEWHMD_LIBRARIES NAMES iViewHMDAPI PATH_SUFFIXES libs HINTS ${IVIEWHMD_SEARCH_DIRS})
25− find_path(IVIEWHMD_API_DLL_PATH iViewHMDAPI.dll PATH_SUFFIXES libs HINTS ${IVIEWHMD_SEARCH_DIRS})
24+ find_library(IVIEWHMD_LIBRARIES NAMES iViewHMDAPI PATH_SUFFIXES libs/x86 HINTS ${IVIEWHMD_SEARCH_DIRS})
25+ find_path(IVIEWHMD_API_DLL_PATH iViewHMDAPI.dll PATH_SUFFIXES libs/x86 HINTS ${IVIEWHMD_SEARCH_DIRS})
2626 list(APPEND IVIEWHMD_REQUIREMENTS IVIEWHMD_INCLUDE_DIRS IVIEWHMD_LIBRARIES IVIEWHMD_API_DLL_PATH)
2727
28− set(IVIEWHMD_DLLS
29− avcodec-53.dll
30− avformat-53.dll
31− avutil-51.dll
32− libboost_filesystem-mgw45-mt-1_49.dll
33− libboost_system-mgw45-mt-1_49.dll
34− libboost_thread-mgw45-mt-1_49.dll
35− libgcc_s_dw2-1.dll
36− libiViewNG-LibCore.dll
37− libopencv_calib3d244.dll
38− libopencv_core244.dll
39− libopencv_features2d244.dll
40− libopencv_flann244.dll
41− libopencv_highgui244.dll
42− libopencv_imgproc244.dll
43− libopencv_legacy244.dll
44− libopencv_ml244.dll
45− libopencv_video244.dll
46− libstdc++-6.dll
47− opencv_core220.dll
48− opencv_highgui220.dll
49− opencv_imgproc220.dll
50− swscale-2.dll
51− )
52−
53− foreach(IVIEWHMD_DLL ${IVIEWHMD_DLLS})
54− find_path(IVIEWHMD_DLL_PATH ${IVIEWHMD_DLL} PATH_SUFFIXES 3rdParty HINTS ${IVIEWHMD_SEARCH_DIRS})
55− list(APPEND IVIEWHMD_REQUIREMENTS IVIEWHMD_DLL_PATH)
56− list(APPEND IVIEWHMD_DLL_PATHS ${IVIEWHMD_DLL_PATH})
57− endforeach()
28+ find_path(IVIEWHMD_DLL_PATH_3RD_PARTY libiViewNG.dll PATH_SUFFIXES 3rdParty HINTS ${IVIEWHMD_SEARCH_DIRS})
29+ list(APPEND IVIEWHMD_REQUIREMENTS IVIEWHMD_DLL_PATH_3RD_PARTY)
5830
5931 include(FindPackageHandleStandardArgs)
6032 find_package_handle_standard_args(IVIEWHMD DEFAULT_MSG ${IVIEWHMD_REQUIREMENTS})
6133
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})
6335
6436 mark_as_advanced(IVIEWHMD_INCLUDE_DIRS IVIEWHMD_LIBRARIES IVIEWHMD_SEARCH_DIRS)
6537
examples/clap.js+2−2
@@ -71,8 +71,8 @@ function maybePlaySound(deltaTime) { · 70 unmodified lines
7171 var palm2Position = MyAvatar.getRightPalmPosition();
7272 var distanceBetween = Vec3.length(Vec3.subtract(palm1Position, palm2Position));
7373
74− var palm1Velocity = Controller.getSpatialControlVelocity(1);
75− var palm2Velocity = Controller.getSpatialControlVelocity(3);
74+ var palm1Velocity = Controller.getPoseValue(Controller.Standard.LeftHand).velocity;
75+ var palm2Velocity = Controller.getPoseValue(Controller.Standard.RightHand).velocity;
7676 var closingVelocity = Vec3.length(Vec3.subtract(palm1Velocity, palm2Velocity));
7777
7878 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() {
16+return {
17+ "name": "example",
18+ "channels": [
19+ { "from": "Keyboard.W", "to": "Actions.LONGITUDINAL_FORWARD" },
20+ { "from": "Keyboard.S", "to": "Actions.LONGITUDINAL_BACKWARD" },
21+
22+ { "from": "Keyboard.Left", "to": "Actions.LATERAL_LEFT" },
23+ { "from": "Keyboard.Right", "to": "Actions.LATERAL_RIGHT" },
24+
25+ { "from": "Keyboard.A", "to": "Actions.YAW_LEFT" },
26+ { "from": "Keyboard.D", "to": "Actions.YAW_RIGHT" },
27+
28+ { "from": "Keyboard.C", "to": "Actions.VERTICAL_DOWN" },
29+ { "from": "Keyboard.E", "to": "Actions.VERTICAL_UP" },
30+ {
31+ "from": "Standard.LX",
32+ "filters": [ {
33+ "type": "scale",
34+ "params": [2.0],
35+ }
36+ ],
37+ "to": "Actions.LATERAL_LEFT",
38+ }, {
39+ "from": "Keyboard.B",
40+ "to": "Actions.Yaw"
41+ }
42+ ]
43+}
44+}
45+*/
46+mySecondMapping = function() {
47+return {
48+ "name": "example2",
49+ "channels": [
50+ { "from": "Standard.LY", "to": "Actions.TranslateZ" },
51+ { "from": "Standard.LX", "to": "Actions.Yaw" },
52+ ]
53+}
54+}
55+
56+//Script.include('mapping-test0.json');
57+/*var myFirstMappingJSON = myFirstMapping();
58+print('myFirstMappingJSON' + JSON.stringify(myFirstMappingJSON));
59+
60+var mapping = Controller.parseMapping(JSON.stringify(myFirstMappingJSON));
61+
62+
63+Controller.enableMapping("example3");
64+
65+var mySecondMappingJSON = mySecondMapping();
66+print('mySecondMappingJSON' + JSON.stringify(mySecondMappingJSON));
67+
68+var mapping2 = Controller.parseMapping(JSON.stringify(mySecondMappingJSON));
69+mapping2.enable();
70+
71+Controller.enableMapping("example2");
72+*/
73+var mapping3 = Controller.loadMapping(Script.resolvePath("example3.json"));
74+Controller.enableMapping("example3");
75+
76+/*
77+Object.keys(Controller.Standard).forEach(function (input) {
78+ print("Controller.Standard." + input + ":" + Controller.Standard[input]);
79+});
80+
81+Object.keys(Controller.Hardware).forEach(function (deviceName) {
82+ Object.keys(Controller.Hardware[deviceName]).forEach(function (input) {
83+ print("Controller.Hardware." + deviceName + "." + input + ":" + Controller.Hardware[deviceName][input]);
84+ });
85+});
86+
87+Object.keys(Controller.Actions).forEach(function (actionName) {
88+ print("Controller.Actions." + actionName + ":" + Controller.Actions[actionName]);
89+});
90+*/
91+
92+
93+Controller.hardwareChanged.connect(function () {
94+ print("hardwareChanged ---------------------------------------------------");
95+ Object.keys(Controller.Hardware).forEach(function (deviceName) {
96+ Object.keys(Controller.Hardware[deviceName]).forEach(function (input) {
97+ print("Controller.Hardware." + deviceName + "." + input + ":" + Controller.Hardware[deviceName][input]);
98+ });
99+ });
100+ print("-------------------------------------------------------------------");
101+});
examples/controllers/handControllerGrab.js+30−13
@@ -148,6 +148,7 @@ function MyController(hand, triggerAction) { · 147 unmodified lines
148148 this.state = STATE_OFF;
149149 this.pointer = null; // entity-id of line object
150150 this.triggerValue = 0; // rolling average of trigger value
151+ this.rawTriggerValue = 0;
151152
152153 var _this = this;
153154
@@ -244,12 +245,17 @@ function MyController(hand, triggerAction) { · 90 unmodified lines
244245 this.pointer = null;
245246 };
246247
247− this.updateSmoothedTrigger = function() {
248− var triggerValue = Controller.getActionValue(this.triggerAction);
248+ this.eitherTrigger = function (value) {
249+ _this.rawTriggerValue = value;
250+ };
251+
252+ this.updateSmoothedTrigger = function () {
253+ var triggerValue = this.rawTriggerValue;
249254 // smooth out trigger value
250255 this.triggerValue = (this.triggerValue * TRIGGER_SMOOTH_RATIO) +
251256 (triggerValue * (1.0 - TRIGGER_SMOOTH_RATIO));
252− }
257+
258+ };
253259
254260 this.triggerSmoothedSqueezed = function() {
255261 return this.triggerValue > TRIGGER_ON_VALUE;
@@ -259,8 +265,8 @@ function MyController(hand, triggerAction) { · 3 unmodified lines
259265 return this.triggerValue < TRIGGER_OFF_VALUE;
260266 };
261267
262− this.triggerSqueezed = function() {
263− var triggerValue = Controller.getActionValue(this.triggerAction);
268+ this.triggerSqueezed = function() {
269+ var triggerValue = this.rawTriggerValue;
264270 return triggerValue > TRIGGER_ON_VALUE;
265271 };
266272
@@ -402,8 +408,9 @@ function MyController(hand, triggerAction) { · 135 unmodified lines
402408
403409 this.distanceHolding = function() {
404410
405− var handControllerPosition = Controller.getSpatialControlPosition(this.palm);
406− var handRotation = Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(this.palm));
411+ var handControllerPosition = (this.hand === RIGHT_HAND) ? MyAvatar.rightHandPosition : MyAvatar.leftHandPosition;
412+ var controllerHandInput = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand;
413+ var handRotation = Quat.multiply(MyAvatar.orientation, Controller.getPoseValue(controllerHandInput).rotation);
407414 var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, ["position", "rotation",
408415 "gravity", "ignoreForCollisions"]);
409416 var now = Date.now();
@@ -452,8 +459,9 @@ function MyController(hand, triggerAction) { · 42 unmodified lines
452459 }
453460
454461 var handPosition = this.getHandPosition();
455− var handControllerPosition = Controller.getSpatialControlPosition(this.palm);
456− var handRotation = Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(this.palm));
462+ var handControllerPosition = (this.hand === RIGHT_HAND) ? MyAvatar.rightHandPosition : MyAvatar.leftHandPosition;
463+ var controllerHandInput = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand;
464+ var handRotation = Quat.multiply(MyAvatar.orientation, Controller.getPoseValue(controllerHandInput).rotation);
457465 var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, ["position", "rotation"]);
458466
459467 this.lineOn(handPosition, Vec3.subtract(grabbedProperties.position, handPosition), INTERSECT_COLOR);
@@ -595,7 +603,7 @@ function MyController(hand, triggerAction) { · 135 unmodified lines
595603
596604 }
597605
598− this.currentHandControllerTipPosition = Controller.getSpatialControlPosition(this.tip);
606+ this.currentHandControllerTipPosition = (this.hand === RIGHT_HAND) ? MyAvatar.rightHandTipPosition : MyAvatar.leftHandTipPosition;;
599607
600608 this.currentObjectTime = Date.now();
601609 };
@@ -613,7 +621,7 @@ function MyController(hand, triggerAction) { · 11 unmodified lines
613621 // of it's actual offset, let's try imparting a velocity which is at a fixed radius
614622 // from the palm.
615623
616− var handControllerPosition = Controller.getSpatialControlPosition(this.tip);
624+ var handControllerPosition = (this.hand === RIGHT_HAND) ? MyAvatar.rightHandPosition : MyAvatar.leftHandPosition;
617625 var now = Date.now();
618626
619627 var deltaPosition = Vec3.subtract(handControllerPosition, this.currentHandControllerTipPosition); // meters
@@ -856,8 +864,16 @@ function MyController(hand, triggerAction) { · 236 unmodified lines
856864 };
857865 }
858866
859−var rightController = new MyController(RIGHT_HAND, Controller.findAction("RIGHT_HAND_CLICK"));
860−var leftController = new MyController(LEFT_HAND, Controller.findAction("LEFT_HAND_CLICK"));
867+var rightController = new MyController(RIGHT_HAND, Controller.Standard.RT);
868+var leftController = new MyController(LEFT_HAND, Controller.Standard.LT);
869+
870+var MAPPING_NAME = "com.highfidelity.handControllerGrab";
871+
872+var mapping = Controller.newMapping(MAPPING_NAME);
873+mapping.from([Controller.Standard.RB, Controller.Standard.RT]).to(rightController.eitherTrigger);
874+mapping.from([Controller.Standard.LB, Controller.Standard.LT]).to(leftController.eitherTrigger);
875+Controller.enableMapping(MAPPING_NAME);
876+
861877
862878 function update() {
863879 rightController.update();
@@ -867,6 +883,7 @@ function update() { · 3 unmodified lines
867883 function cleanup() {
868884 rightController.cleanup();
869885 leftController.cleanup();
886+ Controller.disableMapping(MAPPING_NAME);
870887 }
871888
872889 Script.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+
13+function makeSphere(color) {
14+ var SPHERE_SIZE = 0.05;
15+ var sphere = Overlays.addOverlay("sphere", {
16+ position: { x: 0, y: 0, z: 0 },
17+ size: SPHERE_SIZE,
18+ color: color,
19+ alpha: 1.0,
20+ solid: true,
21+ visible: true,
22+ });
23+
24+ return sphere;
25+}
26+
27+
28+var NUM_HANDS = 2;
29+var NUM_SPHERES_PER_HAND = 2;
30+var LEFT_HAND = 0;
31+var RIGHT_HAND = 1;
32+
33+var COLORS = [ { red: 255, green: 0, blue: 0 }, { red: 0, green: 0, blue: 255 } ];
34+
35+
36+function index(handNum, indexNum) {
37+ return handNum * NUM_HANDS + indexNum;
38+}
39+
40+var app = {};
41+
42+
43+function setup() {
44+ app.spheres = new Array();
45+
46+ for (var h = 0; h < NUM_HANDS; h++) {
47+ for (var s = 0; s < NUM_SPHERES_PER_HAND; s++) {
48+ var i = index(h, s);
49+ app.spheres[i] = makeSphere(COLORS[h]);
50+ print("Added Sphere num " + i + " = " + JSON.stringify(app.spheres[i]));
51+ }
52+ }
53+}
54+
55+function updateHand(handNum, deltaTime) {
56+ var pose;
57+ var handName = "right";
58+ if (handNum == LEFT_HAND) {
59+ pose = MyAvatar.getLeftHandPose();
60+ handName = "left";
61+ } else {
62+ pose = MyAvatar.getRightHandPose();
63+ handName = "right";
64+ }
65+
66+ if (pose.valid) {
67+ //print(handName + " hand moving" + JSON.stringify(pose));
68+ Overlays.editOverlay(app.spheres[index(handNum, 0)], {
69+ position: pose.translation,
70+ visible: true,
71+ });
72+ var vpos = Vec3.sum(Vec3.multiply(10 * deltaTime, pose.velocity), pose.translation);
73+ Overlays.editOverlay(app.spheres[index(handNum, 1)], {
74+ position: vpos,
75+ visible: true,
76+ });
77+ } else {
78+ Overlays.editOverlay(app.spheres[index(handNum, 0)], {
79+ visible: false
80+ });
81+
82+ Overlays.editOverlay(app.spheres[index(handNum, 1)], {
83+ visible: false
84+ });
85+ }
86+}
87+
88+function update(deltaTime) {
89+ updateHand(LEFT_HAND, deltaTime);
90+ updateHand(RIGHT_HAND, deltaTime);
91+}
92+
93+function scriptEnding() {
94+ print("Removing spheres = " + JSON.stringify(app.spheres));
95+ for (var i = 0; i < app.spheres.length; i++) {
96+ Overlays.deleteOverlay(app.spheres[i]);
97+ }
98+}
99+
100+setup();
101+Script.update.connect(update);
102+Script.scriptEnding.connect(scriptEnding);
examples/controllers/hydra/airGuitar.js+107−69
@@ -32,6 +32,8 @@ var guitarModel = HIFI_PUBLIC_BUCKET + "models/attachments/guitar.fst"; · 31 unmodified lines
3232
3333 // Load sounds that will be played
3434
35+var heyManWave = SoundCache.getSound("https://hifi-public.s3.amazonaws.com/sounds/KenDoll_1%2303.wav");
36+
3537 var chords = new Array();
3638 // Nylon string guitar
3739 chords[1] = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Guitars/Guitar+-+Nylon+A.raw");
@@ -56,97 +58,128 @@ var NUM_GUITARS = 3; · 18 unmodified lines
5658 var guitarSelector = NUM_CHORDS;
5759 var whichChord = 1;
5860
59−var leftHanded = true;
61+var leftHanded = false;
62+var strumHand, chordHand, strumTrigger, chordTrigger;
6063 if (leftHanded) {
61− var strumHand = 0;
62− var chordHand = 1;
64+ strumHand = Controller.Standard.LeftHand;
65+ chordHand = Controller.Standard.RightHand;
66+ strumTrigger = Controller.Standard.LT;
67+ chordTrigger = Controller.Standard.RT;
68+ changeGuitar = Controller.Standard.RB;
69+ chord1 = Controller.Standard.X;
70+ chord2 = Controller.Standard.Y;
71+ chord3 = Controller.Standard.A;
72+ chord4 = Controller.Standard.B;
6373 } else {
64− var strumHand = 1;
65− var chordHand = 0;
74+ strumHand = Controller.Standard.RightHand;
75+ chordHand = Controller.Standard.LeftHand;
76+ strumTrigger = Controller.Standard.RT;
77+ chordTrigger = Controller.Standard.LT;
78+ changeGuitar = Controller.Standard.LB;
79+ chord1 = Controller.Standard.DU; // these may not be correct, maybe we should map directly to Hydra??
80+ chord2 = Controller.Standard.DD;
81+ chord3 = Controller.Standard.DL;
82+ chord4 = Controller.Standard.DR;
6683 }
6784
6885 var lastPosition = { x: 0.0,
6986 y: 0.0,
7087 z: 0.0 };
7188
72−var soundPlaying = false;
89+var audioInjector = null;
7390 var selectorPressed = false;
7491 var position;
7592
76−MyAvatar.attach(guitarModel, "Hips", {x: -0.2, y: 0.0, z: 0.1}, Quat.fromPitchYawRollDegrees(90, 00, 90), 1.0);
93+MyAvatar.attach(guitarModel, "Hips", {x: leftHanded ? -0.2 : 0.2, y: 0.0, z: 0.1}, Quat.fromPitchYawRollDegrees(90, 00, leftHanded ? 75 : -75), 1.0);
7794
7895 function checkHands(deltaTime) {
79− for (var palm = 0; palm < 2; palm++) {
80− var palmVelocity = Controller.getSpatialControlVelocity(palm * 2 + 1);
81− var volume = length(palmVelocity) / 5.0;
82− var position = Controller.getSpatialControlPosition(palm * 2 + 1);
83− var myPelvis = MyAvatar.position;
84− var trigger = Controller.getTriggerValue(strumHand);
85− var chord = Controller.getTriggerValue(chordHand);
86−
87− if (volume > 1.0) volume = 1.0;
88− if ((chord > 0.1) && soundPlaying.isPlaying) {
89− // If chord finger trigger pulled, stop current chord
90− print("stopped sound");
91− soundPlaying.stop();
92− }
96+ var strumVelocity = Controller.getPoseValue(strumHand).velocity;
97+ var volume = length(strumVelocity) / 5.0;
9398
94− var BUTTON_COUNT = 6;
95−
96− // Change guitars if button FWD (5) pressed
97− if (Controller.isButtonPressed(chordHand * BUTTON_COUNT + 5)) {
98− if (!selectorPressed) {
99− guitarSelector += NUM_CHORDS;
100− if (guitarSelector >= NUM_CHORDS * NUM_GUITARS) {
101− guitarSelector = 0;
102− }
103− selectorPressed = true;
104− }
105− } else {
106− selectorPressed = false;
107− }
99+ if (volume == 0.0) {
100+ volume = 1.0;
101+ }
102+
103+ var strumHandPosition = leftHanded ? MyAvatar.leftHandPosition : MyAvatar.rightHandPosition;
104+ var myPelvis = MyAvatar.position;
105+ var strumming = Controller.getValue(strumTrigger);
106+ var chord = Controller.getValue(chordTrigger);
107+
108+ if (volume > 1.0) volume = 1.0;
109+ if ((chord > 0.1) && audioInjector && audioInjector.isPlaying) {
110+ // If chord finger trigger pulled, stop current chord
111+ print("stopping chord because cord trigger pulled");
112+ audioInjector.stop();
113+ }
108114
109− if (Controller.isButtonPressed(chordHand * BUTTON_COUNT + 1)) {
110− whichChord = 1;
111− } else if (Controller.isButtonPressed(chordHand * BUTTON_COUNT + 2)) {
112− whichChord = 2;
113− } else if (Controller.isButtonPressed(chordHand * BUTTON_COUNT + 3)) {
114− whichChord = 3;
115− } else if (Controller.isButtonPressed(chordHand * BUTTON_COUNT + 4)) {
116− whichChord = 4;
115+ // Change guitars if button FWD (5) pressed
116+ if (Controller.getValue(changeGuitar)) {
117+ if (!selectorPressed) {
118+ print("changeGuitar:" + changeGuitar);
119+ guitarSelector += NUM_CHORDS;
120+ if (guitarSelector >= NUM_CHORDS * NUM_GUITARS) {
121+ guitarSelector = 0;
122+ }
123+ print("new guitarBase: " + guitarSelector);
124+ stopAudio(true);
125+ selectorPressed = true;
117126 }
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;
118147
119− if (palm == strumHand) {
120−
121− var STRUM_HEIGHT_ABOVE_PELVIS = 0.10;
122− var strumTriggerHeight = myPelvis.y + STRUM_HEIGHT_ABOVE_PELVIS;
123− //printVector(position);
124− if ( ( ((position.y < strumTriggerHeight) && (lastPosition.y >= strumTriggerHeight)) ||
125− ((position.y > strumTriggerHeight) && (lastPosition.y <= strumTriggerHeight)) ) && (trigger > 0.1) ){
126− // If hand passes downward or upward through 'strings', and finger trigger pulled, play
127− playChord(position, volume);
128− }
129− lastPosition = Controller.getSpatialControlPosition(palm * 2 + 1);
130− }
148+ var strumNowAbove = strumHandPosition.y > strummingHeight;
149+ var strumNowBelow = strumHandPosition.y <= strummingHeight;
150+ var strumWasAbove = lastPosition.y > strummingHeight;
151+ var strumWasBelow = lastPosition.y <= strummingHeight;
152+ var strumUp = strumNowAbove && strumWasBelow;
153+ var strumDown = strumNowBelow && strumWasAbove;
154+
155+ if ((strumUp || strumDown) && (strumming > 0.1)) {
156+ // If hand passes downward or upward through 'strings', and finger trigger pulled, play
157+ playChord(strumHandPosition, volume);
131158 }
159+ lastPosition = strumHandPosition;
132160 }
133161
134−function playChord(position, volume) {
135− if (soundPlaying.isPlaying) {
162+function stopAudio(killInjector) {
163+ if (audioInjector && audioInjector.isPlaying) {
136164 print("stopped sound");
137− soundPlaying.stop();
165+ audioInjector.stop();
166+ }
167+ if (killInjector) {
168+ audioInjector = null;
169+ }
170+}
171+
172+
173+function playChord(position, volume) {
174+ stopAudio();
175+ print("Played sound: " + whichChord + " at volume " + volume);
176+ if (!audioInjector) {
177+ var index = guitarSelector + whichChord;
178+ var chord = chords[guitarSelector + whichChord];
179+ audioInjector = Audio.playSound(chord, { position: position, volume: volume });
180+ } else {
181+ audioInjector.restart();
138182 }
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−
150183 }
151184
152185 function keyPressEvent(event) {
@@ -154,15 +187,19 @@ function keyPressEvent(event) { · 1 unmodified line
154187 keyVolume = 0.4;
155188 if (event.text == "1") {
156189 whichChord = 1;
190+ stopAudio(true);
157191 playChord(MyAvatar.position, keyVolume);
158192 } else if (event.text == "2") {
159193 whichChord = 2;
194+ stopAudio(true);
160195 playChord(MyAvatar.position, keyVolume);
161196 } else if (event.text == "3") {
162197 whichChord = 3;
198+ stopAudio(true);
163199 playChord(MyAvatar.position, keyVolume);
164200 } else if (event.text == "4") {
165201 whichChord = 4;
202+ stopAudio(true);
166203 playChord(MyAvatar.position, keyVolume);
167204 }
168205 }
@@ -170,6 +207,7 @@ function keyPressEvent(event) { · 1 unmodified line
170207 function scriptEnding() {
171208 MyAvatar.detachOne(guitarModel);
172209 }
210+
173211 // Connect a call back that happens every frame
174212 Script.update.connect(checkHands);
175213 Script.scriptEnding.connect(scriptEnding);
examples/controllers/hydra/drumStick.js+3−2
@@ -43,7 +43,8 @@ strokeSpeed[1] = 0.0; · 42 unmodified lines
4343
4444 function checkSticks(deltaTime) {
4545 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;
4748 var speed = length(palmVelocity);
4849
4950 const TRIGGER_SPEED = 0.30; // Lower this value to let you 'drum' more gently
@@ -64,7 +65,7 @@ function checkSticks(deltaTime) { · 14 unmodified lines
6465 if ((palmVelocity.y > 0.0) || (speed < STOP_SPEED)) {
6566 state[palm] = 0;
6667
67− var options = { position: Controller.getSpatialControlPosition(palm * 2 + 1) };
68+ var options = { position: handPose.translation };
6869
6970 if (strokeSpeed[palm] > 1.0) { strokeSpeed[palm] = 1.0; }
7071 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
130130 this.trigger = trigger;
131131 this.holdingFrisbee = false;
132132 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+
134137 this.grabButtonPressed = function() {
135138 return (
136− Controller.isButtonPressed(this.forwardButton) ||
137− Controller.isButtonPressed(this.button3) ||
138− Controller.getTriggerValue(this.trigger) > 0.5
139+ Controller.getValue(this.forwardButton) ||
140+ Controller.getValue(this.button3) ||
141+ Controller.getValue(this.trigger) > 0.5
139142 )
140143 };
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+
142148 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;
144151 q = Quat.multiply(MyAvatar.orientation, q);
145152 return {x: q.x, y: q.y, z: q.z, w: q.w};
146153 };
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+ };
148157 }
149158
150159 function MouseControl(button) {
151160 this.button = button;
152161 }
153162
154−var leftHand = new Hand("LEFT", LEFT_PALM, LEFT_TIP, LEFT_BUTTON_FWD, LEFT_BUTTON_3, 0);
155−var rightHand = new Hand("RIGHT", RIGHT_PALM, RIGHT_TIP, RIGHT_BUTTON_FWD, RIGHT_BUTTON_3, 1);
163+var leftHand = new Hand("LEFT", LEFT_PALM, LEFT_TIP, Controller.Standard.LB, Controller.Standard.LeftPrimaryThumb, Controller.Standard.LT);
164+var rightHand = new Hand("RIGHT", RIGHT_PALM, RIGHT_TIP, Controller.Standard.RB, Controller.Standard.RightPrimaryThumb, Controller.Standard.RT);
156165
157166 var leftMouseControl = new MouseControl("LEFT");
158167 var middleMouseControl = new MouseControl("MIDDLE");
@@ -302,12 +311,7 @@ function initToolBar() { · 143 unmodified lines
302311 }
303312
304313 function hydraCheck() {
305− var numberOfButtons = Controller.getNumberOfButtons();
306− var numberOfTriggers = Controller.getNumberOfTriggers();
307− var numberOfSpatialControls = Controller.getNumberOfSpatialControls();
308− var controllersPerTrigger = numberOfSpatialControls / numberOfTriggers;
309− hydrasConnected = (numberOfButtons == 12 && numberOfTriggers == 2 && controllersPerTrigger == 2);
310− return true;//hydrasConnected;
314+ return Controller.Hardware.Hydra !== undefined;
311315 }
312316
313317 function checkController(deltaTime) {
examples/controllers/hydra/gun.js+336−304
@@ -15,24 +15,64 @@ · 14 unmodified lines
1515 // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
1616 //
1717
18+// FIXME kickback functionality was removed because the joint setting interface in
19+// MyAvatar has apparently changed, breaking it.
20+
21+Script.include("../../libraries/utils.js");
22+Script.include("../../libraries/constants.js");
23+Script.include("../../libraries/toolBars.js");
24+
1825 HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
1926
20−var RED = { red: 255, green: 0, blue: 0 };
2127 var LASER_WIDTH = 2;
22−
28+var POSE_CONTROLS = [ Controller.Standard.LeftHand, Controller.Standard.RightHand ];
29+var TRIGGER_CONTROLS = [ Controller.Standard.LT, Controller.Standard.RT ];
30+var MIN_THROWER_DELAY = 1000;
31+var MAX_THROWER_DELAY = 1000;
32+var RELOAD_INTERVAL = 5;
33+var GUN_MODEL = HIFI_PUBLIC_BUCKET + "cozza13/gun/m1911-handgun+1.fbx?v=4";
34+var BULLET_VELOCITY = 10.0;
35+var GUN_OFFSETS = [ {
36+ x: -0.04,
37+ y: 0.26,
38+ z: 0.04
39+}, {
40+ x: 0.04,
41+ y: 0.26,
42+ z: 0.04
43+} ];
44+
45+var GUN_ORIENTATIONS = [ Quat.fromPitchYawRollDegrees(0, 90, 90), Quat.fromPitchYawRollDegrees(0, -90, 270) ];
46+
47+var BARREL_OFFSETS = [ {
48+ x: -0.12,
49+ y: 0.12,
50+ z: 0.04
51+}, {
52+ x: 0.12,
53+ y: 0.12,
54+ z: 0.04
55+} ];
56+
57+var mapping = Controller.newMapping();
58+var validPoses = [ false, false ];
59+var barrelVectors = [ 0, 0 ];
60+var barrelTips = [ 0, 0 ];
2361 var pointer = [];
62+
2463 pointer.push(Overlays.addOverlay("line3d", {
25− start: { x: 0, y: 0, z: 0 },
26− end: { x: 0, y: 0, z: 0 },
27− color: RED,
64+ start: ZERO_VECTOR,
65+ end: ZERO_VECTOR,
66+ color: COLORS.RED,
2867 alpha: 1,
2968 visible: true,
3069 lineWidth: LASER_WIDTH
3170 }));
71+
3272 pointer.push(Overlays.addOverlay("line3d", {
33− start: { x: 0, y: 0, z: 0 },
34− end: { x: 0, y: 0, z: 0 },
35− color: RED,
73+ start: ZERO_VECTOR,
74+ end: ZERO_VECTOR,
75+ color: COLORS.RED,
3676 alpha: 1,
3777 visible: true,
3878 lineWidth: LASER_WIDTH
@@ -42,135 +82,116 @@ function getRandomFloat(min, max) { · 3 unmodified lines
4282 return Math.random() * (max - min) + min;
4383 }
4484
45−var lastX = 0;
46−var lastY = 0;
47−var yawFromMouse = 0;
48−var pitchFromMouse = 0;
49−var isMouseDown = false;
50−
51−var MIN_THROWER_DELAY = 1000;
52−var MAX_THROWER_DELAY = 1000;
53−var LEFT_BUTTON_3 = 3;
54−var RELOAD_INTERVAL = 5;
55−
56−var KICKBACK_ANGLE = 15;
57−var elbowKickAngle = 0.0;
58−var rotationBeforeKickback;
59−
6085 var showScore = false;
61−
62−
63−// Load some sound to use for loading and firing
86+// Load some sound to use for loading and firing
6487 var fireSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Guns/GUN-SHOT2.raw");
6588 var loadSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Guns/Gun_Reload_Weapon22.raw");
6689 var impactSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Guns/BulletImpact2.raw");
6790 var targetHitSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Space%20Invaders/hit.raw");
6891 var targetLaunchSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Space%20Invaders/shoot.raw");
6992
70−var gunModel = "https://s3.amazonaws.com/hifi-public/cozza13/gun/m1911-handgun+1.fbx?v=4";
71−
7293 var audioOptions = {
73− volume: 0.9
94+ volume: 0.9
7495 }
7596
7697 var shotsFired = 0;
77−var shotTime = new Date();
78−
79−var activeControllers = 0;
80−
81−// initialize our controller triggers
82−var triggerPulled = new Array();
83−var numberOfTriggers = Controller.getNumberOfTriggers();
84−for (t = 0; t < numberOfTriggers; t++) {
85− triggerPulled[t] = false;
86−}
87−
88−var isLaunchButtonPressed = false;
89−var score = 0;
98+var shotTime = new Date();
99+var isLaunchButtonPressed = false;
100+var score = 0;
90101
91102 var bulletID = false;
92103 var targetID = false;
93104
94−// Create overlay buttons and reticle
95−
105+// Create overlay buttons and reticle
96106 var BUTTON_SIZE = 32;
97107 var PADDING = 3;
98108 var NUM_BUTTONS = 3;
99−
100109 var screenSize = Controller.getViewportDimensions();
101110 var startX = screenSize.x / 2 - (NUM_BUTTONS * (BUTTON_SIZE + PADDING)) / 2;
102−Script.include(["../../libraries/toolBars.js"]);
103−var toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL, "highfidelity.gun.toolbar", function (screenSize) {
111+var toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL, "highfidelity.gun.toolbar", function(screenSize) {
104112 return {
105113 x: startX,
106114 y: (screenSize.y - (BUTTON_SIZE + PADDING)),
107115 };
108116 });
109−var reticle = Overlays.addOverlay("image", {
110− x: screenSize.x / 2 - (BUTTON_SIZE / 2),
111− y: screenSize.y / 2 - (BUTTON_SIZE / 2),
112− width: BUTTON_SIZE,
113− height: BUTTON_SIZE,
114− imageURL: HIFI_PUBLIC_BUCKET + "images/gun/crosshairs.svg",
115− alpha: 1
116− });
117117
118118 var offButton = toolBar.addOverlay("image", {
119− width: BUTTON_SIZE,
120− height: BUTTON_SIZE,
121− imageURL: HIFI_PUBLIC_BUCKET + "images/gun/close.svg",
122− alpha: 1
123− });
119+ width: BUTTON_SIZE,
120+ height: BUTTON_SIZE,
121+ imageURL: HIFI_PUBLIC_BUCKET + "images/gun/close.svg",
122+ alpha: 1
123+});
124124
125125 startX += BUTTON_SIZE + PADDING;
126126 var platformButton = toolBar.addOverlay("image", {
127− x: startX,
128− y: screenSize.y - (BUTTON_SIZE + PADDING),
129− width: BUTTON_SIZE,
130− height: BUTTON_SIZE,
131− imageURL: HIFI_PUBLIC_BUCKET + "images/gun/platform-targets.svg",
132− alpha: 1
133− });
127+ x: startX,
128+ y: screenSize.y - (BUTTON_SIZE + PADDING),
129+ width: BUTTON_SIZE,
130+ height: BUTTON_SIZE,
131+ imageURL: HIFI_PUBLIC_BUCKET + "images/gun/platform-targets.svg",
132+ alpha: 1
133+});
134134
135135 startX += BUTTON_SIZE + PADDING;
136136 var gridButton = toolBar.addOverlay("image", {
137− x: startX,
138− y: screenSize.y - (BUTTON_SIZE + PADDING),
139− width: BUTTON_SIZE,
140− height: BUTTON_SIZE,
141− imageURL: HIFI_PUBLIC_BUCKET + "images/gun/floating-targets.svg",
142− alpha: 1
143− });
137+ x: startX,
138+ y: screenSize.y - (BUTTON_SIZE + PADDING),
139+ width: BUTTON_SIZE,
140+ height: BUTTON_SIZE,
141+ imageURL: HIFI_PUBLIC_BUCKET + "images/gun/floating-targets.svg",
142+ alpha: 1
143+});
144144
145145 if (showScore) {
146146 var text = Overlays.addOverlay("text", {
147− x: screenSize.x / 2 - 100,
148− y: screenSize.y / 2 - 50,
149− width: 150,
150− height: 50,
151− color: { red: 0, green: 0, blue: 0},
152− textColor: { red: 255, green: 0, blue: 0},
153− topMargin: 4,
154− leftMargin: 4,
155− text: "Score: " + score
156− });
147+ x: screenSize.x / 2 - 100,
148+ y: screenSize.y / 2 - 50,
149+ width: 150,
150+ height: 50,
151+ color: {
152+ red: 0,
153+ green: 0,
154+ blue: 0
155+ },
156+ textColor: {
157+ red: 255,
158+ green: 0,
159+ blue: 0
160+ },
161+ topMargin: 4,
162+ leftMargin: 4,
163+ text: "Score: " + score
164+ });
157165 }
158166
159−var BULLET_VELOCITY = 10.0;
160−
161167 function entityCollisionWithEntity(entity1, entity2, collision) {
162168 if (entity2 === targetID) {
163169 score++;
164170 if (showScore) {
165− Overlays.editOverlay(text, { text: "Score: " + score } );
171+ Overlays.editOverlay(text, {
172+ text: "Score: " + score
173+ });
166174 }
167175
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!
169178 Script.setTimeout(deleteBulletAndTarget, 500);
170179
171180 // Turn the target and the bullet white
172− Entities.editEntity(entity1, { color: { red: 255, green: 255, blue: 255 }});
173− Entities.editEntity(entity2, { color: { red: 255, green: 255, blue: 255 }});
181+ Entities.editEntity(entity1, {
182+ color: {
183+ red: 255,
184+ green: 255,
185+ blue: 255
186+ }
187+ });
188+ Entities.editEntity(entity2, {
189+ color: {
190+ red: 255,
191+ green: 255,
192+ blue: 255
193+ }
194+ });
174195
175196 // play the sound near the camera so the shooter can hear it
176197 audioOptions.position = Vec3.sum(Camera.getPosition(), Quat.getFront(Camera.getOrientation()));
@@ -188,41 +209,45 @@ function shootBullet(position, velocity, grenade) { · 11 unmodified lines
188209
189210 var bVelocity = grenade ? Vec3.multiply(GRENADE_VELOCITY, Vec3.normalize(velocity)) : velocity;
190211 var bSize = grenade ? GRENADE_SIZE : BULLET_SIZE;
191− var bGravity = grenade ? GRENADE_GRAVITY : BULLET_GRAVITY;
192−
193− bulletID = Entities.addEntity(
194− { type: "Sphere",
195− position: position,
196− dimensions: { x: bSize, y: bSize, z: bSize },
197− color: { red: 0, green: 0, blue: 0 },
198− velocity: bVelocity,
199− lifetime: BULLET_LIFETIME,
200− gravity: { x: 0, y: bGravity, z: 0 },
201− damping: 0.01,
202− density: 8000,
203− ignoreCollisions: false,
204− collisionsWillMove: true
205− });
212+ var bGravity = grenade ? GRENADE_GRAVITY : BULLET_GRAVITY;
213+
214+ bulletID = Entities.addEntity({
215+ type: "Sphere",
216+ position: position,
217+ dimensions: {
218+ x: bSize,
219+ y: bSize,
220+ z: bSize
221+ },
222+ color: {
223+ red: 0,
224+ green: 0,
225+ blue: 0
226+ },
227+ velocity: bVelocity,
228+ lifetime: BULLET_LIFETIME,
229+ gravity: {
230+ x: 0,
231+ y: bGravity,
232+ z: 0
233+ },
234+ damping: 0.01,
235+ density: 8000,
236+ ignoreCollisions: false,
237+ collisionsWillMove: true
238+ });
239+
206240 Script.addEventHandler(bulletID, "collisionWithEntity", entityCollisionWithEntity);
207241
208− // Play firing sounds
209− audioOptions.position = position;
242+ // Play firing sounds
243+ audioOptions.position = position;
210244 Audio.playSound(fireSound, audioOptions);
211245 shotsFired++;
212246 if ((shotsFired % RELOAD_INTERVAL) == 0) {
213247 Audio.playSound(loadSound, audioOptions);
214248 }
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;
225249 }
250+
226251 function shootTarget() {
227252 var TARGET_SIZE = 0.50;
228253 var TARGET_GRAVITY = 0.0;
@@ -232,95 +257,152 @@ function shootTarget() { · 3 unmodified lines
232257 var DISTANCE_TO_LAUNCH_FROM = 5.0;
233258 var ANGLE_RANGE_FOR_LAUNCH = 20.0;
234259 var camera = Camera.getPosition();
235−
236− var targetDirection = Quat.angleAxis(getRandomFloat(-ANGLE_RANGE_FOR_LAUNCH, ANGLE_RANGE_FOR_LAUNCH), { x:0, y:1, z:0 });
260+
261+ var targetDirection = Quat.angleAxis(getRandomFloat(-ANGLE_RANGE_FOR_LAUNCH, ANGLE_RANGE_FOR_LAUNCH), {
262+ x: 0,
263+ y: 1,
264+ z: 0
265+ });
237266 targetDirection = Quat.multiply(Camera.getOrientation(), targetDirection);
238267 var forwardVector = Quat.getFront(targetDirection);
239−
268+
240269 var newPosition = Vec3.sum(camera, Vec3.multiply(forwardVector, DISTANCE_TO_LAUNCH_FROM));
241270
242271 var velocity = Vec3.multiply(forwardVector, TARGET_FWD_VELOCITY);
243272 velocity.y += TARGET_UP_VELOCITY;
244273
245− targetID = Entities.addEntity(
246− { type: "Box",
247− position: newPosition,
248− dimensions: { x: TARGET_SIZE * (0.5 + Math.random()), y: TARGET_SIZE * (0.5 + Math.random()), z: TARGET_SIZE * (0.5 + Math.random()) / 4.0 },
249− color: { red: Math.random() * 255, green: Math.random() * 255, blue: Math.random() * 255 },
250− velocity: velocity,
251− gravity: { x: 0, y: TARGET_GRAVITY, z: 0 },
252− lifetime: TARGET_LIFETIME,
253− rotation: Camera.getOrientation(),
254− damping: 0.1,
255− density: 100.0,
256− collisionsWillMove: true });
257−
258− // Record start time
274+ targetID = Entities.addEntity({
275+ type: "Box",
276+ position: newPosition,
277+ dimensions: {
278+ x: TARGET_SIZE * (0.5 + Math.random()),
279+ y: TARGET_SIZE * (0.5 + Math.random()),
280+ z: TARGET_SIZE * (0.5 + Math.random()) / 4.0
281+ },
282+ color: {
283+ red: Math.random() * 255,
284+ green: Math.random() * 255,
285+ blue: Math.random() * 255
286+ },
287+ velocity: velocity,
288+ gravity: {
289+ x: 0,
290+ y: TARGET_GRAVITY,
291+ z: 0
292+ },
293+ lifetime: TARGET_LIFETIME,
294+ rotation: Camera.getOrientation(),
295+ damping: 0.1,
296+ density: 100.0,
297+ collisionsWillMove: true
298+ });
299+
300+ // Record start time
259301 shotTime = new Date();
260302
261303 // Play target shoot sound
262− audioOptions.position = newPosition;
304+ audioOptions.position = newPosition;
263305 Audio.playSound(targetLaunchSound, audioOptions);
264306 }
265307
266308 function makeGrid(type, scale, size) {
267− var separation = scale * 2;
309+ var separation = scale * 2;
268310 var pos = Vec3.sum(Camera.getPosition(), Vec3.multiply(10.0 * scale * separation, Quat.getFront(Camera.getOrientation())));
269311 var x, y, z;
270− var GRID_LIFE = 60.0;
271− var dimensions;
312+ var GRID_LIFE = 60.0;
313+ var dimensions;
272314
273315 for (x = 0; x < size; x++) {
274316 for (y = 0; y < size; y++) {
275317 for (z = 0; z < size; z++) {
276−
277− dimensions = { x: separation/2.0 * (0.5 + Math.random()), y: separation/2.0 * (0.5 + Math.random()), z: separation/2.0 * (0.5 + Math.random()) / 4.0 };
278−
279− Entities.addEntity(
280− { type: type,
281− position: { x: pos.x + x * separation, y: pos.y + y * separation, z: pos.z + z * separation },
282− dimensions: dimensions,
283− color: { red: Math.random() * 255, green: Math.random() * 255, blue: Math.random() * 255 },
284− velocity: { x: 0, y: 0, z: 0 },
285− gravity: { x: 0, y: 0, z: 0 },
286− lifetime: GRID_LIFE,
287− rotation: Camera.getOrientation(),
288− damping: 0.1,
289− density: 100.0,
290− collisionsWillMove: true });
318+
319+ dimensions = {
320+ x: separation / 2.0 * (0.5 + Math.random()),
321+ y: separation / 2.0 * (0.5 + Math.random()),
322+ z: separation / 2.0 * (0.5 + Math.random()) / 4.0
323+ };
324+
325+ Entities.addEntity({
326+ type: type,
327+ position: {
328+ x: pos.x + x * separation,
329+ y: pos.y + y * separation,
330+ z: pos.z + z * separation
331+ },
332+ dimensions: dimensions,
333+ color: {
334+ red: Math.random() * 255,
335+ green: Math.random() * 255,
336+ blue: Math.random() * 255
337+ },
338+ velocity: {
339+ x: 0,
340+ y: 0,
341+ z: 0
342+ },
343+ gravity: {
344+ x: 0,
345+ y: 0,
346+ z: 0
347+ },
348+ lifetime: GRID_LIFE,
349+ rotation: Camera.getOrientation(),
350+ damping: 0.1,
351+ density: 100.0,
352+ collisionsWillMove: true
353+ });
291354 }
292355 }
293356 }
294357 }
295358
296359 function makePlatform(gravity, scale, size) {
297− var separation = scale * 2;
360+ var separation = scale * 2;
298361 var pos = Vec3.sum(Camera.getPosition(), Vec3.multiply(10.0 * scale * separation, Quat.getFront(Camera.getOrientation())));
299362 pos.y -= separation * size;
300363 var x, y, z;
301− var TARGET_LIFE = 60.0;
364+ var TARGET_LIFE = 60.0;
302365 var INITIAL_GAP = 0.5;
303− var dimensions;
366+ var dimensions;
304367
305368 for (x = 0; x < size; x++) {
306369 for (y = 0; y < size; y++) {
307370 for (z = 0; z < size; z++) {
308371
309− dimensions = { x: separation/2.0, y: separation, z: separation/2.0 };
310−
311− Entities.addEntity(
312− { type: "Box",
313− position: { x: pos.x - (separation * size / 2.0) + x * separation,
314− y: pos.y + y * (separation + INITIAL_GAP),
315− z: pos.z - (separation * size / 2.0) + z * separation },
316− dimensions: dimensions,
317− color: { red: Math.random() * 255, green: Math.random() * 255, blue: Math.random() * 255 },
318− velocity: { x: 0, y: 0.05, z: 0 },
319− gravity: { x: 0, y: gravity, z: 0 },
320− lifetime: TARGET_LIFE,
321− damping: 0.1,
322− density: 100.0,
323− collisionsWillMove: true });
372+ dimensions = {
373+ x: separation / 2.0,
374+ y: separation,
375+ z: separation / 2.0
376+ };
377+
378+ Entities.addEntity({
379+ type: "Box",
380+ position: {
381+ x: pos.x - (separation * size / 2.0) + x * separation,
382+ y: pos.y + y * (separation + INITIAL_GAP),
383+ z: pos.z - (separation * size / 2.0) + z * separation
384+ },
385+ dimensions: dimensions,
386+ color: {
387+ red: Math.random() * 255,
388+ green: Math.random() * 255,
389+ blue: Math.random() * 255
390+ },
391+ velocity: {
392+ x: 0,
393+ y: 0.05,
394+ z: 0
395+ },
396+ gravity: {
397+ x: 0,
398+ y: gravity,
399+ z: 0
400+ },
401+ lifetime: TARGET_LIFE,
402+ damping: 0.1,
403+ density: 100.0,
404+ collisionsWillMove: true
405+ });
324406 }
325407 }
326408 }
@@ -328,9 +410,21 @@ function makePlatform(gravity, scale, size) { · 1 unmodified line
328410 // Make a floor for this stuff to fall onto
329411 Entities.addEntity({
330412 type: "Box",
331− position: { x: pos.x, y: pos.y - separation / 2.0, z: pos.z },
332− dimensions: { x: 2.0 * separation * size, y: separation / 2.0, z: 2.0 * separation * size },
333− color: { red: 100, green: 100, blue: 100 },
413+ position: {
414+ x: pos.x,
415+ y: pos.y - separation / 2.0,
416+ z: pos.z
417+ },
418+ dimensions: {
419+ x: 2.0 * separation * size,
420+ y: separation / 2.0,
421+ z: 2.0 * separation * size
422+ },
423+ color: {
424+ red: 100,
425+ green: 100,
426+ blue: 100
427+ },
334428 lifetime: TARGET_LIFE
335429 });
336430
@@ -340,153 +434,79 @@ function keyPressEvent(event) { · 3 unmodified lines
340434 // if our tools are off, then don't do anything
341435 if (event.text == "t") {
342436 var time = MIN_THROWER_DELAY + Math.random() * MAX_THROWER_DELAY;
343− Script.setTimeout(shootTarget, time);
437+ Script.setTimeout(shootTarget, time);
344438 } else if ((event.text == ".") || (event.text == "SPACE")) {
345439 shootFromMouse(false);
346440 } else if (event.text == ",") {
347441 shootFromMouse(true);
348442 } else if (event.text == "r") {
349443 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"));
355444 }
356445 }
357446
358447 function playLoadSound() {
359− audioOptions.position = Vec3.sum(Camera.getPosition(), Quat.getFront(Camera.getOrientation()));
448+ audioOptions.position = MyAvatar.leftHandPose.translation;
360449 Audio.playSound(loadSound, audioOptions);
361− // Raise arm to firing posture
362− takeFiringPose();
363−}
364−
365−function clearPose() {
366− MyAvatar.clearJointData("LeftForeArm");
367− MyAvatar.clearJointData("LeftArm");
368− MyAvatar.clearJointData("LeftHand");
369450 }
370451
371452 function deleteBulletAndTarget() {
372453 Entities.deleteEntity(bulletID);
373454 Entities.deleteEntity(targetID);
374− bulletID = false;
375− targetID = false;
376−}
377−
378−function takeFiringPose() {
379− clearPose();
380− if (Controller.getNumberOfSpatialControls() == 0) {
381− MyAvatar.setJointData("LeftForeArm", {x: -0.251919, y: -0.0415449, z: 0.499487, w: 0.827843});
382− MyAvatar.setJointData("LeftArm", { x: 0.470196, y: -0.132559, z: 0.494033, w: 0.719219});
383− MyAvatar.setJointData("LeftHand", { x: -0.0104815, y: -0.110551, z: -0.352111, w: 0.929333});
384− }
455+ bulletID = false;
456+ targetID = false;
385457 }
386458
387−MyAvatar.attach(gunModel, "RightHand", {x:0.04, y: 0.22, z: 0.02}, Quat.fromPitchYawRollDegrees(-172, -85, 79), 0.40);
388−MyAvatar.attach(gunModel, "LeftHand", {x:-0.04, y: 0.22, z: 0.02}, Quat.fromPitchYawRollDegrees(-172, 85, -79), 0.40);
389−
390−// Give a bit of time to load before playing sound
391−Script.setTimeout(playLoadSound, 2000);
392−
393459 function update(deltaTime) {
394− if (activeControllers == 0) {
395− if (Controller.getNumberOfSpatialControls() > 0) {
396− activeControllers = Controller.getNumberOfSpatialControls();
397− clearPose();
398− }
399− }
400−
401− var KICKBACK_DECAY_RATE = 0.125;
402− if (elbowKickAngle > 0.0) {
403− if (elbowKickAngle > 0.5) {
404− var newAngle = elbowKickAngle * KICKBACK_DECAY_RATE;
405− elbowKickAngle -= newAngle;
406− var armRotation = MyAvatar.getJointRotation("LeftForeArm");
407− armRotation = Quat.multiply(armRotation, Quat.fromPitchYawRollDegrees(0.0, 0.0, -newAngle));
408− MyAvatar.setJointData("LeftForeArm", armRotation);
409− } else {
410− MyAvatar.setJointData("LeftForeArm", rotationBeforeKickback);
411− if (Controller.getNumberOfSpatialControls() > 0) {
412− clearPose();
413− }
414− elbowKickAngle = 0.0;
415− }
416− }
417−
418−
419− // check for trigger press
420−
421− var numberOfTriggers = 2;
422− var controllersPerTrigger = 2;
423−
424− if (numberOfTriggers == 2 && controllersPerTrigger == 2) {
425− for (var t = 0; t < 2; t++) {
426− var shootABullet = false;
427− var triggerValue = Controller.getTriggerValue(t);
428− if (triggerPulled[t]) {
429− // must release to at least 0.1
430− if (triggerValue < 0.1) {
431− triggerPulled[t] = false; // unpulled
432− }
433− } else {
434− // must pull to at least
435− if (triggerValue > 0.5) {
436− triggerPulled[t] = true; // pulled
437− shootABullet = true;
438− }
439− }
440− var palmController = t * controllersPerTrigger;
441− var palmPosition = Controller.getSpatialControlPosition(palmController);
442− var fingerTipController = palmController + 1;
443− var fingerTipPosition = Controller.getSpatialControlPosition(fingerTipController);
444− var laserTip = Vec3.sum(Vec3.multiply(100.0, Vec3.subtract(fingerTipPosition, palmPosition)), palmPosition);
445−
446− // Update Lasers
447− Overlays.editOverlay(pointer[t], {
448− start: palmPosition,
449− end: laserTip,
450− alpha: 1
460+ // FIXME we should also expose MyAvatar.handPoses[2], MyAvatar.tipPoses[2]
461+ var tipPoses = [ MyAvatar.leftHandTipPose, MyAvatar.rightHandTipPose ];
462+
463+ for (var side = 0; side < 2; side++) {
464+ // First check if the controller is valid
465+ var controllerPose = Controller.getPoseValue(POSE_CONTROLS[side]);
466+ validPoses[side] = controllerPose.valid;
467+ if (!controllerPose.valid) {
468+ Overlays.editOverlay(pointer[side], {
469+ visible: false
451470 });
452−
453− if (shootABullet) {
454−
455− var palmToFingerTipVector =
456− { x: (fingerTipPosition.x - palmPosition.x),
457− y: (fingerTipPosition.y - palmPosition.y),
458− z: (fingerTipPosition.z - palmPosition.z) };
459−
460− // just off the front of the finger tip
461− var position = { x: fingerTipPosition.x + palmToFingerTipVector.x/2,
462− y: fingerTipPosition.y + palmToFingerTipVector.y/2,
463− z: fingerTipPosition.z + palmToFingerTipVector.z/2};
464−
465− var velocity = Vec3.multiply(BULLET_VELOCITY, Vec3.normalize(palmToFingerTipVector));
466−
467− shootBullet(position, velocity, false);
468− }
471+ continue;
469472 }
470− }
471−}
472473
473−function shootFromMouse(grenade) {
474− var DISTANCE_FROM_CAMERA = 1.0;
475− var camera = Camera.getPosition();
476− var forwardVector = Quat.getFront(Camera.getOrientation());
477− var newPosition = Vec3.sum(camera, Vec3.multiply(forwardVector, DISTANCE_FROM_CAMERA));
478− var velocity = Vec3.multiply(forwardVector, BULLET_VELOCITY);
479− shootBullet(newPosition, velocity, grenade);
474+ // Need to adjust the laser
475+ var tipPose = tipPoses[side];
476+ var handRotation = tipPoses[side].rotation;
477+ var barrelOffset = Vec3.multiplyQbyV(handRotation, BARREL_OFFSETS[side]);
478+ barrelTips[side] = Vec3.sum(tipPose.translation, barrelOffset);
479+ barrelVectors[side] = Vec3.multiplyQbyV(handRotation, {
480+ x: 0,
481+ y: 1,
482+ z: 0
483+ });
484+
485+ var laserTip = Vec3.sum(Vec3.multiply(100.0, barrelVectors[side]), barrelTips[side]);
486+ // Update Lasers
487+ Overlays.editOverlay(pointer[side], {
488+ start: barrelTips[side],
489+ end: laserTip,
490+ alpha: 1,
491+ visible: true
492+ });
493+ }
480494 }
481495
482−function mouseReleaseEvent(event) {
483− // position
484− isMouseDown = false;
496+function triggerChanged(side, value) {
497+ var pressed = (value != 0);
498+ if (pressed) {
499+ var position = barrelTips[side];
500+ var velocity = Vec3.multiply(BULLET_VELOCITY, Vec3.normalize(barrelVectors[side]));
501+ shootBullet(position, velocity, false);
502+ }
485503 }
486504
487505 function mousePressEvent(event) {
488− var clickedText = false;
489− var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y});
506+ var clickedOverlay = Overlays.getOverlayAtPoint({
507+ x: event.x,
508+ y: event.y
509+ });
490510 if (clickedOverlay == offButton) {
491511 Script.stop();
492512 } else if (clickedOverlay == platformButton) {
@@ -494,25 +514,37 @@ function mousePressEvent(event) { · 1 unmodified line
494514 makePlatform(-9.8, 1.0, platformSize);
495515 } else if (clickedOverlay == gridButton) {
496516 makeGrid("Box", 1.0, 3);
497− }
517+ }
498518 }
499519
500520 function scriptEnding() {
501− Overlays.deleteOverlay(reticle);
521+ mapping.disable();
502522 toolBar.cleanup();
503− Overlays.deleteOverlay(pointer[0]);
504− Overlays.deleteOverlay(pointer[1]);
523+ for (var i = 0; i < pointer.length; ++i) {
524+ Overlays.deleteOverlay(pointer[i]);
525+ }
505526 Overlays.deleteOverlay(text);
506− MyAvatar.detachOne(gunModel);
507− MyAvatar.detachOne(gunModel);
527+ MyAvatar.detachOne(GUN_MODEL);
528+ MyAvatar.detachOne(GUN_MODEL);
508529 clearPose();
509530 }
510531
532+MyAvatar.attach(GUN_MODEL, "LeftHand", GUN_OFFSETS[0], GUN_ORIENTATIONS[0], 0.40);
533+MyAvatar.attach(GUN_MODEL, "RightHand", GUN_OFFSETS[1], GUN_ORIENTATIONS[1], 0.40);
534+
535+// Give a bit of time to load before playing sound
536+Script.setTimeout(playLoadSound, 2000);
537+
538+mapping.from(Controller.Standard.LT).hysteresis(0.1, 0.5).to(function(value) {
539+ triggerChanged(0, value);
540+});
541+
542+mapping.from(Controller.Standard.RT).hysteresis(0.1, 0.5).to(function(value) {
543+ triggerChanged(1, value);
544+});
545+mapping.enable();
546+
511547 Script.scriptEnding.connect(scriptEnding);
512548 Script.update.connect(update);
513−Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
514549 Controller.mousePressEvent.connect(mousePressEvent);
515550 Controller.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−
21−var damping = 0.9;
22−var position = { x: MyAvatar.position.x, y: MyAvatar.position.y, z: MyAvatar.position.z };
23−var joysticksCaptured = false;
24−var THRUST_CONTROLLER = 0;
25−var VIEW_CONTROLLER = 1;
26−var INITIAL_THRUST_MULTIPLIER = 1.0;
27−var THRUST_INCREASE_RATE = 1.05;
28−var MAX_THRUST_MULTIPLIER = 75.0;
29−var thrustMultiplier = INITIAL_THRUST_MULTIPLIER;
30−var grabDelta = { x: 0, y: 0, z: 0};
31−var grabStartPosition = { x: 0, y: 0, z: 0};
32−var grabDeltaVelocity = { x: 0, y: 0, z: 0};
33−var grabStartRotation = { x: 0, y: 0, z: 0, w: 1};
34−var grabCurrentRotation = { x: 0, y: 0, z: 0, w: 1};
35−var grabbingWithRightHand = false;
36−var wasGrabbingWithRightHand = false;
37−var grabbingWithLeftHand = false;
38−var wasGrabbingWithLeftHand = false;
39−
40−var EPSILON = 0.000001;
41−var velocity = { x: 0, y: 0, z: 0};
42−var THRUST_MAG_UP = 100.0;
43−var THRUST_MAG_DOWN = 100.0;
44−var THRUST_MAG_FWD = 150.0;
45−var THRUST_MAG_BACK = 100.0;
46−var THRUST_MAG_LATERAL = 150.0;
47−var THRUST_JUMP = 120.0;
48−
49−var YAW_MAG = 100.0;
50−var PITCH_MAG = 100.0;
51−var THRUST_MAG_HAND_JETS = THRUST_MAG_FWD;
52−var JOYSTICK_YAW_MAG = YAW_MAG;
53−var JOYSTICK_PITCH_MAG = PITCH_MAG * 0.5;
54−
55−
56−var LEFT_PALM = 0;
57−var LEFT_BUTTON_4 = 4;
58−var LEFT_BUTTON_FWD = 5;
59−var RIGHT_PALM = 2;
60−var RIGHT_BUTTON_4 = 10;
61−var RIGHT_BUTTON_FWD = 11;
62−
63−
64−
65−function printVector(text, v, decimals) {
66− print(text + " " + v.x.toFixed(decimals) + ", " + v.y.toFixed(decimals) + ", " + v.z.toFixed(decimals));
67−}
68−
69−var debug = false;
70−var RED_COLOR = { red: 255, green: 0, blue: 0 };
71−var GRAY_COLOR = { red: 25, green: 25, blue: 25 };
72−var defaultPosition = { x: 0, y: 0, z: 0};
73−var RADIUS = 0.05;
74−var greenSphere = -1;
75−var redSphere = -1;
76−
77−function createDebugOverlay() {
78−
79− if (greenSphere == -1) {
80− greenSphere = Overlays.addOverlay("sphere", {
81− position: defaultPosition,
82− size: RADIUS,
83− color: GRAY_COLOR,
84− alpha: 0.75,
85− visible: true,
86− solid: true,
87− anchor: "MyAvatar"
88− });
89− redSphere = Overlays.addOverlay("sphere", {
90− position: defaultPosition,
91− size: RADIUS,
92− color: RED_COLOR,
93− alpha: 0.5,
94− visible: true,
95− solid: true,
96− anchor: "MyAvatar"
97− });
98− }
99−}
100−
101−function destroyDebugOverlay() {
102− if (greenSphere != -1) {
103− Overlays.deleteOverlay(greenSphere);
104− Overlays.deleteOverlay(redSphere);
105− greenSphere = -1;
106− redSphere = -1;
107− }
108−}
109−
110−function displayDebug() {
111− if (!(grabbingWithRightHand || grabbingWithLeftHand)) {
112− if (greenSphere != -1) {
113− destroyDebugOverlay();
114− }
115− } else {
116− // update debug indicator
117− if (greenSphere == -1) {
118− createDebugOverlay();
119− }
120−
121− var displayOffset = { x:0, y:0.5, z:-0.5 };
122−
123− Overlays.editOverlay(greenSphere, { position: Vec3.sum(grabStartPosition, displayOffset) } );
124− Overlays.editOverlay(redSphere, { position: Vec3.sum(Vec3.sum(grabStartPosition, grabDelta), displayOffset), size: RADIUS + (0.25 * Vec3.length(grabDelta)) } );
125− }
126−}
127−
128−function getJoystickPosition(palm) {
129− // returns CONTROLLER_ID position in avatar local frame
130− var invRotation = Quat.inverse(MyAvatar.orientation);
131− var palmWorld = Controller.getSpatialControlPosition(palm);
132− var palmRelative = Vec3.subtract(palmWorld, MyAvatar.position);
133− var palmLocal = Vec3.multiplyQbyV(invRotation, palmRelative);
134− return palmLocal;
135−}
136−
137−// Used by handleGrabBehavior() for managing the grab position changes
138−function getAndResetGrabDelta() {
139− var HAND_GRAB_SCALE_DISTANCE = 2.0;
140− var delta = Vec3.multiply(grabDelta, (MyAvatar.scale * HAND_GRAB_SCALE_DISTANCE));
141− grabDelta = { x: 0, y: 0, z: 0};
142− var avatarRotation = MyAvatar.orientation;
143− var result = Vec3.multiplyQbyV(avatarRotation, Vec3.multiply(delta, -1));
144− return result;
145−}
146−
147−function getGrabRotation() {
148− var quatDiff = Quat.multiply(grabCurrentRotation, Quat.inverse(grabStartRotation));
149− return quatDiff;
150−}
151−
152−// When move button is pressed, process results
153−function handleGrabBehavior(deltaTime) {
154− // check for and handle grab behaviors
155− grabbingWithRightHand = Controller.isButtonPressed(RIGHT_BUTTON_4);
156− grabbingWithLeftHand = Controller.isButtonPressed(LEFT_BUTTON_4);
157− stoppedGrabbingWithLeftHand = false;
158− stoppedGrabbingWithRightHand = false;
159−
160− if (grabbingWithRightHand && !wasGrabbingWithRightHand) {
161− // Just starting grab, capture starting rotation
162− grabStartRotation = Controller.getSpatialControlRawRotation(RIGHT_PALM);
163− grabStartPosition = getJoystickPosition(RIGHT_PALM);
164− if (debug) printVector("start position", grabStartPosition, 3);
165− }
166− if (grabbingWithRightHand) {
167− grabDelta = Vec3.subtract(getJoystickPosition(RIGHT_PALM), grabStartPosition);
168− grabCurrentRotation = Controller.getSpatialControlRawRotation(RIGHT_PALM);
169− }
170− if (!grabbingWithRightHand && wasGrabbingWithRightHand) {
171− // Just ending grab, capture velocity
172− grabDeltaVelocity = Controller.getSpatialControlVelocity(RIGHT_PALM);
173− stoppedGrabbingWithRightHand = true;
174− }
175−
176− if (grabbingWithLeftHand && !wasGrabbingWithLeftHand) {
177− // Just starting grab, capture starting rotation
178− grabStartRotation = Controller.getSpatialControlRawRotation(LEFT_PALM);
179− grabStartPosition = getJoystickPosition(LEFT_PALM);
180− if (debug) printVector("start position", grabStartPosition, 3);
181− }
182−
183− if (grabbingWithLeftHand) {
184− grabDelta = Vec3.subtract(getJoystickPosition(LEFT_PALM), grabStartPosition);
185− grabCurrentRotation = Controller.getSpatialControlRawRotation(LEFT_PALM);
186− }
187− if (!grabbingWithLeftHand && wasGrabbingWithLeftHand) {
188− // Just ending grab, capture velocity
189− grabDeltaVelocity = Controller.getSpatialControlVelocity(LEFT_PALM);
190− stoppedGrabbingWithLeftHand = true;
191− }
192−
193− grabbing = grabbingWithRightHand || grabbingWithLeftHand;
194− stoppedGrabbing = stoppedGrabbingWithRightHand || stoppedGrabbingWithLeftHand;
195−
196− if (grabbing) {
197−
198− var headOrientation = MyAvatar.headOrientation;
199− var front = Quat.getFront(headOrientation);
200− var right = Quat.getRight(headOrientation);
201− var up = Quat.getUp(headOrientation);
202−
203− if (debug) {
204− printVector("grabDelta: ", grabDelta, 3);
205− }
206−
207− var thrust = Vec3.multiply(grabDelta, Math.abs(Vec3.length(grabDelta)));
208−
209− var THRUST_GRAB_SCALING = 100000.0;
210−
211− var thrustFront = Vec3.multiply(front, MyAvatar.scale * -thrust.z * THRUST_GRAB_SCALING * deltaTime);
212− MyAvatar.addThrust(thrustFront);
213− var thrustRight = Vec3.multiply(right, MyAvatar.scale * thrust.x * THRUST_GRAB_SCALING * deltaTime);
214− MyAvatar.addThrust(thrustRight);
215− var thrustUp = Vec3.multiply(up, MyAvatar.scale * thrust.y * THRUST_GRAB_SCALING * deltaTime);
216− MyAvatar.addThrust(thrustUp);
217−
218− // add some rotation...
219− var deltaRotation = getGrabRotation();
220− var PITCH_SCALING = 2.5;
221− var PITCH_DEAD_ZONE = 2.0;
222− var YAW_SCALING = 2.5;
223− var ROLL_SCALING = 2.0;
224−
225− var euler = Quat.safeEulerAngles(deltaRotation);
226−
227− // Adjust body yaw by roll from controller
228− var orientation = Quat.multiply(Quat.angleAxis(((euler.y * YAW_SCALING) +
229− (euler.z * ROLL_SCALING)) * deltaTime, {x:0, y: 1, z:0}), MyAvatar.orientation);
230− MyAvatar.orientation = orientation;
231−
232− // Adjust head pitch from controller
233− var pitch = 0.0;
234− if (Math.abs(euler.x) > PITCH_DEAD_ZONE) {
235− pitch = (euler.x < 0.0) ? (euler.x + PITCH_DEAD_ZONE) : (euler.x - PITCH_DEAD_ZONE);
236− }
237− MyAvatar.headPitch = MyAvatar.headPitch + (pitch * PITCH_SCALING * deltaTime);
238−
239− // TODO: Add some camera roll proportional to the rate of turn (so it feels like an airplane or roller coaster)
240−
241− }
242−
243− wasGrabbingWithRightHand = grabbingWithRightHand;
244− wasGrabbingWithLeftHand = grabbingWithLeftHand;
245−}
246−
247−// Update for joysticks and move button
248−var THRUST_DEAD_ZONE = 0.1;
249−var ROTATE_DEAD_ZONE = 0.1;
250−function flyWithHydra(deltaTime) {
251− var thrustJoystickPosition = Controller.getJoystickPosition(THRUST_CONTROLLER);
252−
253− if (Math.abs(thrustJoystickPosition.x) > THRUST_DEAD_ZONE || Math.abs(thrustJoystickPosition.y) > THRUST_DEAD_ZONE) {
254− if (thrustMultiplier < MAX_THRUST_MULTIPLIER) {
255− thrustMultiplier *= 1 + (deltaTime * THRUST_INCREASE_RATE);
256− }
257− var headOrientation = MyAvatar.headOrientation;
258−
259− var front = Quat.getFront(headOrientation);
260− var right = Quat.getRight(headOrientation);
261− var up = Quat.getUp(headOrientation);
262−
263− var thrustFront = Vec3.multiply(front, MyAvatar.scale * THRUST_MAG_HAND_JETS *
264− thrustJoystickPosition.y * thrustMultiplier * deltaTime);
265− MyAvatar.addThrust(thrustFront);
266− var thrustRight = Vec3.multiply(right, MyAvatar.scale * THRUST_MAG_HAND_JETS *
267− thrustJoystickPosition.x * thrustMultiplier * deltaTime);
268− MyAvatar.addThrust(thrustRight);
269− } else {
270− thrustMultiplier = INITIAL_THRUST_MULTIPLIER;
271− }
272−
273− // View Controller
274− var viewJoystickPosition = Controller.getJoystickPosition(VIEW_CONTROLLER);
275− if (Math.abs(viewJoystickPosition.x) > ROTATE_DEAD_ZONE || Math.abs(viewJoystickPosition.y) > ROTATE_DEAD_ZONE) {
276−
277− // change the body yaw based on our x controller
278− var orientation = MyAvatar.orientation;
279− var deltaOrientation = Quat.fromPitchYawRollDegrees(0, (-1 * viewJoystickPosition.x * JOYSTICK_YAW_MAG * deltaTime), 0);
280− MyAvatar.orientation = Quat.multiply(orientation, deltaOrientation);
281−
282− // change the headPitch based on our x controller
283− var newPitch = MyAvatar.headPitch + (viewJoystickPosition.y * JOYSTICK_PITCH_MAG * deltaTime);
284− MyAvatar.headPitch = newPitch;
285− }
286− handleGrabBehavior(deltaTime);
287− displayDebug();
288−
289−}
290−
291−Script.update.connect(flyWithHydra);
292−Controller.captureJoystick(THRUST_CONTROLLER);
293−Controller.captureJoystick(VIEW_CONTROLLER);
294−
295−// Map keyPress and mouse move events to our callbacks
296−function scriptEnding() {
297− // re-enabled the standard application for touch events
298− Controller.releaseJoystick(THRUST_CONTROLLER);
299− Controller.releaseJoystick(VIEW_CONTROLLER);
300−}
301−Script.scriptEnding.connect(scriptEnding);
302−
303−
examples/controllers/hydra/paddleBall.js+14−25
@@ -20,10 +20,11 @@ var BALL_SIZE = 0.08; · 19 unmodified lines
2020 var PADDLE_SIZE = 0.20;
2121 var PADDLE_THICKNESS = 0.06;
2222 var PADDLE_COLOR = { red: 184, green: 134, blue: 11 };
23−var BALL_COLOR = { red: 255, green: 0, blue: 0 };
23+var BALL_COLOR = { red: 0, green: 255, blue: 0 };
2424 var LINE_COLOR = { red: 255, green: 255, blue: 0 };
2525 var PADDLE_BOX_OFFSET = { x: 0.05, y: 0.0, z: 0.0 };
2626
27+//probably we need to fix these initial values (offsets and orientation)
2728 var HOLD_POSITION_LEFT_OFFSET = { x: -0.15, y: 0.05, z: -0.05 };
2829 var HOLD_POSITION_RIGHT_OFFSET = { x: -0.15, y: 0.05, z: 0.05 };
2930 var PADDLE_ORIENTATION = Quat.fromPitchYawRollDegrees(0,0,0);
@@ -32,18 +33,7 @@ var SPRING_FORCE = 15.0; · 2 unmodified lines
3233 var lastSoundTime = 0;
3334 var gameOn = false;
3435 var leftHanded = true;
35−var controllerID;
3636
37−
38−function setControllerID() {
39− if (leftHanded) {
40− controllerID = 1;
41− } else {
42− controllerID = 3;
43− }
44−}
45−
46−setControllerID();
4737 Menu.addMenu("PaddleBall");
4838 Menu.addMenuItem({ menuName: "PaddleBall", menuItemName: "Left-Handed", isCheckable: true, isChecked: true });
4939
@@ -63,7 +53,7 @@ var ball, paddle, paddleModel, line; · 13 unmodified lines
6353 function createEntities() {
6454 ball = Entities.addEntity(
6555 { type: "Sphere",
66− position: Controller.getSpatialControlPosition(controllerID),
56+ position: leftHanded ? MyAvatar.leftHandPose.translation : MyAvatar.rightHandPose.translation,
6757 dimensions: { x: BALL_SIZE, y: BALL_SIZE, z: BALL_SIZE },
6858 color: BALL_COLOR,
6959 gravity: { x: 0, y: GRAVITY, z: 0 },
@@ -73,28 +63,28 @@ function createEntities() { · 3 unmodified lines
7363
7464 paddle = Entities.addEntity(
7565 { type: "Box",
76− position: Controller.getSpatialControlPosition(controllerID),
66+ position: leftHanded ? MyAvatar.leftHandPose.translation : MyAvatar.rightHandPose.translation,
7767 dimensions: { x: PADDLE_SIZE, y: PADDLE_THICKNESS, z: PADDLE_SIZE * 0.80 },
7868 color: PADDLE_COLOR,
7969 gravity: { x: 0, y: 0, z: 0 },
8070 ignoreCollisions: false,
8171 damping: 0.10,
8272 visible: false,
83− rotation: Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(controllerID)),
84− collisionsWillMove: false });
73+ rotation : leftHanded ? MyAvatar.leftHandPose.rotation : MyAvatar.rightHandPose.rotation,
74+ collisionsWillMove: false });
8575
8676 modelURL = "http://public.highfidelity.io/models/attachments/pong_paddle.fbx";
8777 paddleModel = Entities.addEntity(
8878 { 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),
9080 dimensions: { x: PADDLE_SIZE * 1.5, y: PADDLE_THICKNESS, z: PADDLE_SIZE * 1.25 },
9181 color: PADDLE_COLOR,
9282 gravity: { x: 0, y: 0, z: 0 },
9383 ignoreCollisions: true,
9484 modelURL: modelURL,
9585 damping: 0.10,
96− rotation: Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(controllerID)),
97− collisionsWillMove: false });
86+ rotation : leftHanded ? MyAvatar.leftHandPose.rotation : MyAvatar.rightHandPose.rotation,
87+ collisionsWillMove: false });
9888
9989 line = Overlays.addOverlay("line3d", {
10090 start: { x: 0, y: 0, z: 0 },
@@ -118,7 +108,7 @@ function deleteEntities() { · 17 unmodified lines
118108 }
119109
120110 function update(deltaTime) {
121− var palmPosition = Controller.getSpatialControlPosition(controllerID);
111+ var palmPosition = leftHanded ? MyAvatar.leftHandPose.translation : MyAvatar.rightHandPose.translation;
122112 var controllerActive = (Vec3.length(palmPosition) > 0);
123113
124114 if (!gameOn && controllerActive) {
@@ -133,8 +123,8 @@ function update(deltaTime) { · 8 unmodified lines
133123 }
134124
135125 var paddleOrientation = leftHanded ? PADDLE_ORIENTATION : Quat.multiply(PADDLE_ORIENTATION, Quat.fromPitchYawRollDegrees(0, 180, 0));
136− var paddleWorldOrientation = Quat.multiply(Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(controllerID)), paddleOrientation);
137− var holdPosition = Vec3.sum(leftHanded ? MyAvatar.getLeftPalmPosition() : MyAvatar.getRightPalmPosition(),
126+ var paddleWorldOrientation = Quat.multiply(leftHanded ? MyAvatar.leftHandPose.rotation : MyAvatar.rightHandPose.rotation, paddleOrientation);
127+ var holdPosition = Vec3.sum(leftHanded ? MyAvatar.leftHandPose.translation : MyAvatar.rightHandPose.translation,
138128 Vec3.multiplyQbyV(paddleWorldOrientation, leftHanded ? HOLD_POSITION_LEFT_OFFSET : HOLD_POSITION_RIGHT_OFFSET ));
139129
140130 var props = Entities.getEntityProperties(ball);
@@ -146,10 +136,10 @@ function update(deltaTime) { · 5 unmodified lines
146136 Entities.editEntity(ball, { velocity: ballVelocity });
147137 Overlays.editOverlay(line, { start: props.position, end: holdPosition });
148138 Entities.editEntity(paddle, { position: holdPosition,
149− velocity: Controller.getSpatialControlVelocity(controllerID),
139+ velocity: leftHanded ? MyAvatar.leftHandPose.velocity : MyAvatar.rightHandPose.velocity,
150140 rotation: paddleWorldOrientation });
151141 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,
153143 rotation: paddleWorldOrientation });
154144
155145 }
@@ -182,7 +172,6 @@ function menuItemEvent(menuItem) { · 26 unmodified lines
182172 leftHanded = Menu.isOptionChecked("Left-Handed");
183173 }
184174 if ((leftHanded != oldHanded) && gameOn) {
185− setControllerID();
186175 deleteEntities();
187176 createEntities();
188177 }
examples/controllers/hydra/toyball.js+19−43
@@ -19,14 +19,7 @@ HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; · 18 unmodified lines
1919
2020 // maybe we should make these constants...
2121 var LEFT_PALM = 0;
22−var LEFT_TIP = 1;
23−var LEFT_BUTTON_FWD = 5;
24−var LEFT_BUTTON_3 = 3;
25−
2622 var RIGHT_PALM = 2;
27−var RIGHT_TIP = 3;
28−var RIGHT_BUTTON_FWD = 11;
29−var RIGHT_BUTTON_3 = 9;
3023
3124 var BALL_RADIUS = 0.08;
3225 var GRAVITY_STRENGTH = 3.0;
@@ -69,9 +62,6 @@ function getBallHoldPosition(whichSide) { · 36 unmodified lines
6962 }
7063
7164 function checkControllerSide(whichSide) {
72− var BUTTON_FWD;
73− var BUTTON_3;
74− var TRIGGER;
7565 var palmPosition;
7666 var palmRotation;
7767 var ballAlreadyInHand;
@@ -79,35 +69,35 @@ function checkControllerSide(whichSide) { · 1 unmodified line
7969 var linearVelocity;
8070 var angularVelocity;
8171 var AVERAGE_FACTOR = 0.33;
82−
72+ var grabButtonPressed;
73+
8374 if (whichSide == LEFT_PALM) {
84− BUTTON_FWD = LEFT_BUTTON_FWD;
85− BUTTON_3 = LEFT_BUTTON_3;
86− TRIGGER = 0;
87− palmPosition = Controller.getSpatialControlPosition(LEFT_PALM);
88− palmRotation = Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(LEFT_PALM));
75+ palmPosition = MyAvatar.leftHandPose.translation;
76+ palmRotation = MyAvatar.leftHandPose.rotation;
8977 ballAlreadyInHand = leftBallAlreadyInHand;
9078 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),
9280 Vec3.multiply(1.0 - AVERAGE_FACTOR, averageLinearVelocity[0]));
81+
9382 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+
9586 } else {
96− BUTTON_FWD = RIGHT_BUTTON_FWD;
97− BUTTON_3 = RIGHT_BUTTON_3;
98− TRIGGER = 1;
99− palmPosition = Controller.getSpatialControlPosition(RIGHT_PALM);
100− palmRotation = Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(RIGHT_PALM));
87+ palmPosition = MyAvatar.rightHandPose.translation;
88+ palmRotation = MyAvatar.rightHandPose.rotation;
10189 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),
10391 Vec3.multiply(1.0 - AVERAGE_FACTOR, averageLinearVelocity[1]));
92+
10493 linearVelocity = averageLinearVelocity[1];
105− angularVelocity = Vec3.multiplyQbyV(MyAvatar.orientation, Controller.getSpatialControlRawAngularVelocity(RIGHT_TIP));
94+ angularVelocity = MyAvatar.rightHandTipPose.angularVelocity;
10695 handMessage = "RIGHT";
96+ grabButtonPressed = (Controller.getValue(Controller.Standard.RT) > 0.5);
97+
10798 }
10899
109− var grabButtonPressed = (Controller.isButtonPressed(BUTTON_FWD) || Controller.isButtonPressed(BUTTON_3) || (Controller.getTriggerValue(TRIGGER) > 0.5));
110−
100+
111101 // If I don't currently have a ball in my hand, then try to catch closest one
112102 if (!ballAlreadyInHand && grabButtonPressed) {
113103 var closestEntity = Entities.findClosestEntity(palmPosition, targetRadius);
@@ -187,10 +177,8 @@ function checkControllerSide(whichSide) { · 73 unmodified lines
187177 if (ballAlreadyInHand) {
188178 if (whichSide == LEFT_PALM) {
189179 handEntity = leftHandEntity;
190− whichTip = LEFT_TIP;
191180 } else {
192181 handEntity = rightHandEntity;
193− whichTip = RIGHT_TIP;
194182 }
195183
196184 // If holding the ball keep it in the palm
@@ -231,22 +219,10 @@ function checkControllerSide(whichSide) { · 34 unmodified lines
231219 }
232220 }
233221 }
234−
235−
236222 function checkController(deltaTime) {
237− var numberOfButtons = Controller.getNumberOfButtons();
238− var numberOfTriggers = Controller.getNumberOfTriggers();
239− var numberOfSpatialControls = Controller.getNumberOfSpatialControls();
240− var controllersPerTrigger = numberOfSpatialControls / numberOfTriggers;
241−
242− // this is expected for hydras
243− if (!(numberOfButtons==12 && numberOfTriggers == 2 && controllersPerTrigger == 2)) {
244− debugPrint("total buttons = " + numberOfButtons + ", Triggers = " + numberOfTriggers + ", controllers/trigger = " + controllersPerTrigger);
245− return; // bail if no hydra
246− }
247223
248− checkControllerSide(LEFT_PALM);
249− checkControllerSide(RIGHT_PALM);
224+ checkControllerSide(LEFT_PALM);
225+ checkControllerSide(RIGHT_PALM);
250226 }
251227
252228
examples/example/avatarcontrol/controllerExample.js+9−38
@@ -10,25 +10,19 @@ · 9 unmodified lines
1010 // Distributed under the Apache License, Version 2.0.
1111 // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
1212 //
13−
1413 // initialize our triggers
1514 var triggerPulled = new Array();
16−var numberOfTriggers = Controller.getNumberOfTriggers();
17−for (t = 0; t < numberOfTriggers; t++) {
15+var NUMBER_OF_TRIGGERS = 2;
16+for (t = 0; t < NUMBER_OF_TRIGGERS; t++) {
1817 triggerPulled[t] = false;
1918 }
20−
19+var triggers = new Array();
20+triggers[0] = Controller.Standard.LT;
21+triggers[1] = Controller.Standard.RT;
2122 function checkController(deltaTime) {
22− var numberOfTriggers = Controller.getNumberOfTriggers();
23− var numberOfSpatialControls = Controller.getNumberOfSpatialControls();
24− var controllersPerTrigger = numberOfSpatialControls / numberOfTriggers;
2523 var triggerToggled = false;
26−
27− // this is expected for hydras
28− if (numberOfTriggers == 2 && controllersPerTrigger == 2) {
29− for (var t = 0; t < numberOfTriggers; t++) {
30− var triggerValue = Controller.getTriggerValue(t);
31−
24+ for (var t = 0; t < NUMBER_OF_TRIGGERS; t++) {
25+ var triggerValue = Controller.getValue(triggers[t]);
3226 if (triggerPulled[t]) {
3327 // must release to at least 0.1
3428 if (triggerValue < 0.1) {
@@ -41,17 +35,14 @@ function checkController(deltaTime) { · 6 unmodified lines
4135 triggerToggled = true;
4236 }
4337 }
44−
4538 if (triggerToggled) {
4639 print("a trigger was toggled");
4740 }
4841 }
49− }
42+
5043 }
51−
5244 // register the call back so it fires before each data send
5345 Script.update.connect(checkController);
54−
5546 function printKeyEvent(eventName, event) {
5647 print(eventName);
5748 print(" event.key=" + event.key);
@@ -64,7 +55,6 @@ function printKeyEvent(eventName, event) { · 6 unmodified lines
6455 }
6556 function keyPressEvent(event) {
6657 printKeyEvent("keyPressEvent", event);
67−
6858 if (event.text == "A") {
6959 print("the A key was pressed");
7060 }
@@ -72,10 +62,8 @@ function keyPressEvent(event) { · 1 unmodified line
7262 print("the <space> key was pressed");
7363 }
7464 }
75−
7665 function keyReleaseEvent(event) {
7766 printKeyEvent("keyReleaseEvent", event);
78−
7967 if (event.text == "A") {
8068 print("the A key was released");
8169 }
@@ -83,11 +71,9 @@ function keyReleaseEvent(event) { · 1 unmodified line
8371 print("the <space> key was pressed");
8472 }
8573 }
86−
8774 // Map keyPress and mouse move events to our callbacks
8875 Controller.keyPressEvent.connect(keyPressEvent);
8976 Controller.keyReleaseEvent.connect(keyReleaseEvent);
90−
9177 // prevent the A key from going through to the application
9278 Controller.captureKeyEvents({ text: "A" });
9379 Controller.captureKeyEvents({ key: "A".charCodeAt(0) }); // same as above, just another example of how to capture the key
@@ -95,8 +81,6 @@ Controller.captureKeyEvents({ text: " " }); · 1 unmodified line
9581 Controller.captureKeyEvents({ text: "@", isMeta: true });
9682 Controller.captureKeyEvents({ text: "page up" });
9783 Controller.captureKeyEvents({ text: "page down" });
98−
99−
10084 function printMouseEvent(eventName, event) {
10185 print(eventName);
10286 print(" event.x,y=" + event.x + ", " + event.y);
@@ -109,22 +93,18 @@ function printMouseEvent(eventName, event) { · 6 unmodified lines
10993 print(" event.isMeta=" + event.isMeta);
11094 print(" event.isAlt=" + event.isAlt);
11195 }
112−
11396 function mouseMoveEvent(event) {
11497 printMouseEvent("mouseMoveEvent", event);
11598 }
11699 function mousePressEvent(event) {
117100 printMouseEvent("mousePressEvent", event);
118101 }
119−
120102 function mouseReleaseEvent(event) {
121103 printMouseEvent("mouseReleaseEvent", event);
122104 }
123−
124105 Controller.mouseMoveEvent.connect(mouseMoveEvent);
125106 Controller.mousePressEvent.connect(mousePressEvent);
126107 Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
127−
128108 function printTouchEvent(eventName, event) {
129109 print(eventName);
130110
@@ -143,7 +123,6 @@ function printTouchEvent(eventName, event) { · 12 unmodified lines
143123 print(" event.radius=" + event.radius);
144124 print(" event.isPinching=" + event.isPinching);
145125 print(" event.isPinchOpening=" + event.isPinchOpening);
146−
147126 print(" event.angle=" + event.angle);
148127 for (var i = 0; i < event.points.length; i++) {
149128 print(" event.angles[" + i + "]:" + event.angles[i]);
@@ -151,15 +130,12 @@ function printTouchEvent(eventName, event) { · 1 unmodified line
151130 print(" event.isRotating=" + event.isRotating);
152131 print(" event.rotating=" + event.rotating);
153132 }
154−
155133 function touchBeginEvent(event) {
156134 printTouchEvent("touchBeginEvent", event);
157135 }
158−
159136 function touchUpdateEvent(event) {
160137 printTouchEvent("touchUpdateEvent", event);
161138 }
162−
163139 function touchEndEvent(event) {
164140 printTouchEvent("touchEndEvent", event);
165141 }
@@ -167,8 +143,6 @@ function touchEndEvent(event) { · 1 unmodified line
167143 Controller.touchBeginEvent.connect(touchBeginEvent);
168144 Controller.touchUpdateEvent.connect(touchUpdateEvent);
169145 Controller.touchEndEvent.connect(touchEndEvent);
170−
171−
172146 function wheelEvent(event) {
173147 print("wheelEvent");
174148 print(" event.x,y=" + event.x + ", " + event.y);
@@ -182,9 +156,7 @@ function wheelEvent(event) { · 7 unmodified lines
182156 print(" event.isMeta=" + event.isMeta);
183157 print(" event.isAlt=" + event.isAlt);
184158 }
185−
186159 Controller.wheelEvent.connect(wheelEvent);
187−
188160 function scriptEnding() {
189161 // re-enabled the standard application for touch events
190162 Controller.releaseKeyEvents({ text: "A" });
@@ -194,5 +166,4 @@ function scriptEnding() { · 3 unmodified lines
194166 Controller.releaseKeyEvents({ text: "page up" });
195167 Controller.releaseKeyEvents({ text: "page down" });
196168 }
197−
198−Script.scriptEnding.connect(scriptEnding);
169+Script.scriptEnding.connect(scriptEnding);
examples/example/games/hydraGrabHockey.js+3−5
@@ -59,9 +59,7 @@ function controller(side) { · 58 unmodified lines
5959 this.triggerHeld = false;
6060 this.triggerThreshold = 0.9;
6161 this.side = side;
62− this.palm = 2 * side;
63− this.tip = 2 * side + 1;
64− this.trigger = side;
62+ this.trigger = side == LEFT ? Controller.Standard.LT : Controller.Standard.RT;
6563 this.originalGravity = {
6664 x: 0,
6765 y: 0,
@@ -150,8 +148,8 @@ function controller(side) { · 82 unmodified lines
150148
151149
152150 this.updateControllerState = function() {
153− this.palmPosition = Controller.getSpatialControlPosition(this.palm);
154− this.tipPosition = Controller.getSpatialControlPosition(this.tip);
151+ this.palmPosition = this.side == RIGHT ? MyAvatar.rightHandPose.translation : MyAvatar.leftHandPose.translation;
152+ this.tipPosition = this.side == RIGHT ? MyAvatar.rightHandTipPose.translation : MyAvatar.leftHandTipPose.translation;
155153 this.triggerValue = Controller.getTriggerValue(this.trigger);
156154 }
157155
examples/example/games/sword.js+3−3
@@ -238,7 +238,7 @@ var inHand = false; · 237 unmodified lines
238238
239239 function isControllerActive() {
240240 // 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;
242242 return controllerActive;
243243 }
244244
@@ -312,10 +312,10 @@ function grabSword(hand) { · 67 unmodified lines
312312 }
313313 var handRotation;
314314 if (hand === "right") {
315− handRotation = MyAvatar.getRightPalmRotation();
315+ handRotation = MyAvatar.rightHandPose.rotation;
316316
317317 } else if (hand === "left") {
318− handRotation = MyAvatar.getLeftPalmRotation();
318+ handRotation = MyAvatar.leftHandPose.rotation;
319319 }
320320 var swordRotation = Entities.getEntityProperties(swordID).rotation;
321321 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
7171 this.triggerHeld = false;
7272 this.triggerThreshold = 0.9;
7373 this.side = side;
74− this.palm = 2 * side;
75− this.tip = 2 * side + 1;
76− this.trigger = side;
77− this.cycleColorButton = cycleColorButton;
74+ this.trigger = side == LEFT ? Controller.Stantard.LT : Controller.Standard.RT;
75+ this.cycleColorButton = side == LEFT ? Controller.Stantard.LeftPrimaryThumb : Controller.Standard.RightPrimaryThumb;
7876
7977 this.points = [];
8078 this.normals = [];
@@ -173,11 +171,10 @@ function controller(side, cycleColorButton) { · 92 unmodified lines
173171
174172
175173 this.updateControllerState = function() {
176− this.cycleColorButtonPressed = Controller.isButtonPressed(this.cycleColorButton);
177− this.palmPosition = Controller.getSpatialControlPosition(this.palm);
178− this.tipPosition = Controller.getSpatialControlPosition(this.tip);
179− this.palmNormal = Controller.getSpatialControlNormal(this.palm);
180− this.triggerValue = Controller.getTriggerValue(this.trigger);
174+ this.cycleColorButtonPressed = Controller.getValue(this.cycleColorButton);
175+ this.palmPosition = this.side == RIGHT ? MyAvatar.rightHandPose.translation : MyAvatar.leftHandPose.translation;
176+ this.tipPosition = this.side == RIGHT ? MyAvatar.rightHandTipPose.translation : MyAvatar.leftHandTipPose.translation;
177+ this.triggerValue = Controller.getValue(this.trigger);
181178
182179
183180 if (this.prevCycleColorButtonPressed === true && this.cycleColorButtonPressed === false) {
@@ -215,8 +212,8 @@ function vectorIsZero(v) { · 31 unmodified lines
215212 }
216213
217214
218−var rightController = new controller(RIGHT, RIGHT_BUTTON_4);
219−var leftController = new controller(LEFT, LEFT_BUTTON_4);
215+var rightController = new controller(RIGHT);
216+var leftController = new controller(LEFT);
220217 Script.update.connect(update);
221218 Script.scriptEnding.connect(scriptEnding);
222219
examples/example/scripts/controllerScriptingExamples.js+47−12
@@ -11,6 +11,37 @@ · 10 unmodified lines
1111
1212 // Assumes you only have the default keyboard connected
1313
14+function findAction(name) {
15+ var actions = Controller.getAllActions();
16+ for (var i = 0; i < actions.length; i++) {
17+ if (actions[i].actionName == name) {
18+ return i;
19+ }
20+ }
21+ // If the action isn't found, it will default to the first available action
22+ return 0;
23+}
24+
25+
26+var hydra = Controller.Hardware.Hydra;
27+if (hydra !== undefined) {
28+ print("-----------------------------------");
29+ var mapping = Controller.newMapping("Test");
30+ var standard = Controller.Standard;
31+ print("standard:" + standard);
32+ mapping.from(function () { return Math.sin(Date.now() / 250); }).to(function (newValue, oldValue, source) {
33+ print("function source newValue:" + newValue + ", oldValue:" + oldValue + ", source:" + source);
34+ });
35+ mapping.from(hydra.L1).to(standard.A);
36+ mapping.from(hydra.L2).to(standard.B);
37+ mapping.from(hydra.L3).to(function (newValue, oldValue, source) {
38+ print("hydra.L3 newValue:" + newValue + ", oldValue:" + oldValue + ", source:" + source);
39+ });
40+ Controller.enableMapping("Test");
41+ print("-----------------------------------");
42+} else {
43+ print("couldn't find hydra");
44+}
1445
1546 Object.keys(Controller.Standard).forEach(function (input) {
1647 print("Controller.Standard." + input + ":" + Controller.Standard[input]);
@@ -32,44 +63,48 @@ Controller.resetAllDeviceBindings(); · 15 unmodified lines
3263 // Query all actions
3364 print("All Actions: \n" + Controller.getAllActions());
3465
66+var actionId = findAction("YAW_LEFT")
67+
68+print("Yaw Left action ID: " + actionId)
69+
3570 // Each action stores:
3671 // action: int representation of enum
37−print("Action 5 int: \n" + Controller.getAllActions()[5].action);
72+print("Action int: \n" + Controller.getAllActions()[actionId].action);
3873
3974 // actionName: string representation of enum
40−print("Action 5 name: \n" + Controller.getAllActions()[5].actionName);
75+print("Action name: \n" + Controller.getAllActions()[actionId].actionName);
4176
4277 // inputChannels: list of all inputchannels that control that action
43−print("Action 5 input channels: \n" + Controller.getAllActions()[5].inputChannels + "\n");
78+print("Action input channels: \n" + Controller.getAllActions()[actionId].inputChannels + "\n");
4479
4580
4681 // Each input channel stores:
4782 // action: Action that this InputChannel maps to
48−print("Input channel action: \n" + Controller.getAllActions()[5].inputChannels[0].action);
83+print("Input channel action: \n" + Controller.getAllActions()[actionId].inputChannels[0].action);
4984
5085 // scale: sensitivity of input
51−print("Input channel scale: \n" + Controller.getAllActions()[5].inputChannels[0].scale);
86+print("Input channel scale: \n" + Controller.getAllActions()[actionId].inputChannels[0].scale);
5287
5388 // input and modifier: Inputs
54−print("Input channel input and modifier: \n" + Controller.getAllActions()[5].inputChannels[0].input + "\n" + Controller.getAllActions()[5].inputChannels[0].modifier + "\n");
89+print("Input channel input and modifier: \n" + Controller.getAllActions()[actionId].inputChannels[0].input + "\n" + Controller.getAllActions()[actionId].inputChannels[0].modifier + "\n");
5590
5691
5792 // Each Input stores:
5893 // device: device of input
59−print("Input device: \n" + Controller.getAllActions()[5].inputChannels[0].input.device);
94+print("Input device: \n" + Controller.getAllActions()[actionId].inputChannels[0].input.device);
6095
6196 // channel: channel of input
62−print("Input channel: \n" + Controller.getAllActions()[5].inputChannels[0].input.channel);
97+print("Input channel: \n" + Controller.getAllActions()[actionId].inputChannels[0].input.channel);
6398
6499 // type: type of input (Unknown, Button, Axis, Joint)
65−print("Input type: \n" + Controller.getAllActions()[5].inputChannels[0].input.type);
100+print("Input type: \n" + Controller.getAllActions()[actionId].inputChannels[0].input.type);
66101
67102 // id: id of input
68−print("Input id: \n" + Controller.getAllActions()[5].inputChannels[0].input.id + "\n");
103+print("Input id: \n" + Controller.getAllActions()[actionId].inputChannels[0].input.id + "\n");
69104
70105
71106 // You can get the name of a device from its id
72−print("Device 1 name: \n" + Controller.getDeviceName(Controller.getAllActions()[5].inputChannels[0].input.id));
107+print("Device 1 name: \n" + Controller.getDeviceName(Controller.getAllActions()[actionId].inputChannels[0].input.id));
73108
74109 // You can also get all of a devices input channels
75110 print("Device 1's input channels: \n" + Controller.getAllInputsForDevice(1) + "\n");
@@ -105,7 +140,7 @@ for (i = 0; i < availableInputs.length; i++) { · 29 unmodified lines
105140
106141 // You can modify key bindings by using these avaiable inputs
107142 // This will replace e (up) with 6
108−var e = Controller.getAllActions()[5].inputChannels[0];
143+var e = Controller.getAllActions()[actionId].inputChannels[0];
109144 Controller.removeInputChannel(e);
110145 e.input = availableInputs[6].input;
111146 Controller.addInputChannel(e);
examples/libraries/omniTool.js+33−34
@@ -15,16 +15,18 @@ Script.include("omniTool/models/invisibleWand.js"); · 14 unmodified lines
1515
1616 OmniToolModules = {};
1717 OmniToolModuleType = null;
18+LOG_DEBUG = 1;
1819
19−OmniTool = function(side) {
20+OmniTool = function(left) {
2021 this.OMNI_KEY = "OmniTool";
2122 this.MAX_FRAMERATE = 60;
2223 this.UPDATE_INTERVAL = 1.0 / this.MAX_FRAMERATE
23− this.SIDE = side;
24− this.PALM = 2 * side;
25− this.ACTION = findAction(side ? "ACTION2" : "ACTION1");
26− this.ALT_ACTION = findAction(side ? "ACTION1" : "ACTION2");
27−
24+ this.left = left;
25+ this.triggered = false;
26+ var actions = Controller.Actions;
27+ var standard = Controller.Standard;
28+ this.palmControl = left ? actions.LeftHand : actions.RightHand;
29+ logDebug("Init OmniTool " + (left ? "left" : "right"));
2830 this.highlighter = new Highlighter();
2931 this.ignoreEntities = {};
3032 this.nearestOmniEntity = {
@@ -47,22 +49,25 @@ OmniTool = function(side) { · 16 unmodified lines
4749 this.showWand(false);
4850
4951 // Connect to desired events
50− var _this = this;
51− Controller.actionEvent.connect(function(action, state) {
52− _this.onActionEvent(action, state);
53− });
52+ var that = this;
5453
5554 Script.update.connect(function(deltaTime) {
56− _this.lastUpdateInterval += deltaTime;
57− if (_this.lastUpdateInterval >= _this.UPDATE_INTERVAL) {
58− _this.onUpdate(_this.lastUpdateInterval);
59− _this.lastUpdateInterval = 0;
55+ that.lastUpdateInterval += deltaTime;
56+ if (that.lastUpdateInterval >= that.UPDATE_INTERVAL) {
57+ that.onUpdate(that.lastUpdateInterval);
58+ that.lastUpdateInterval = 0;
6059 }
6160 });
6261
6362 Script.scriptEnding.connect(function() {
64− _this.onCleanup();
63+ that.onCleanup();
6564 });
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();
6671 }
6772
6873 OmniTool.prototype.showWand = function(show) {
@@ -81,30 +86,23 @@ OmniTool.prototype.showWand = function(show) { · 12 unmodified lines
8186 }
8287 }
8388
84−
8589 OmniTool.prototype.onCleanup = function(action) {
90+ this.mapping.disable();
8691 this.unloadModule();
8792 }
8893
89−OmniTool.prototype.onActionEvent = function(action, state) {
90− // FIXME figure out the issues when only one spatial controller is active
91− // logDebug("Action: " + action + " " + state);
9294
93− if (this.module && this.module.onActionEvent) {
94− this.module.onActionEvent(action, state);
95− }
96−
97− if (action == this.ACTION) {
98− if (state) {
95+OmniTool.prototype.onUpdateTrigger = function (value) {
96+ //logDebug("Trigger update value " + value);
97+ var triggered = value != 0;
98+ if (triggered != this.triggered) {
99+ this.triggered = triggered;
100+ if (this.triggered) {
99101 this.onClick();
100102 } else {
101103 this.onRelease();
102104 }
103105 }
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
108106 }
109107
110108 OmniTool.prototype.getOmniToolData = function(entityId) {
@@ -127,7 +125,7 @@ OmniTool.prototype.setActive = function(active) { · 16 unmodified lines
127125 if (active === this.active) {
128126 return;
129127 }
130− logDebug("OmniTool changing active state: " + active);
128+ logDebug("OmniTool " + this.left + " changing active state: " + active);
131129 this.active = active;
132130 this.model.setVisible(this.active);
133131 if (this.module && this.module.onActiveChanged) {
@@ -138,17 +136,17 @@ OmniTool.prototype.setActive = function(active) { · 4 unmodified lines
138136
139137 OmniTool.prototype.onUpdate = function(deltaTime) {
140138 // 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;
142141 // When on the base, hydras report a position of 0
143142 this.setActive(Vec3.length(this.position) > 0.001);
144143 if (!this.active) {
145144 return;
146145 }
147146
148−
149147 if (this.model) {
150148 // Update the wand
151− var rawRotation = Controller.getSpatialControlRawRotation(this.PALM);
149+ var rawRotation = this.pose.rotation;
152150 this.rotation = Quat.multiply(MyAvatar.orientation, rawRotation);
153151 this.model.setTransform({
154152 rotation: this.rotation,
@@ -306,6 +304,7 @@ OmniTool.prototype.scan = function() { · 151 unmodified lines
306304 }
307305
308306 OmniTool.prototype.unloadModule = function() {
307+ logDebug("Unloading omniTool module")
309308 if (this.module && this.module.onUnload) {
310309 this.module.onUnload();
311310 }
@@ -348,4 +347,4 @@ OmniTool.prototype.activateNewOmniModule = function() { · 36 unmodified lines
348347 }
349348
350349 // FIXME find a good way to sync the two omni tools
351−OMNI_TOOLS = [ new OmniTool(0), new OmniTool(1) ];
350+OMNI_TOOLS = [ new OmniTool(true), new OmniTool(false) ];
examples/libraries/utils.js+1−7
@@ -31,13 +31,7 @@ scaleLine = function (start, end, scale) { · 30 unmodified lines
3131 }
3232
3333 findAction = function(name) {
34− var actions = Controller.getAllActions();
35− for (var i = 0; i < actions.length; i++) {
36− if (actions[i].actionName == name) {
37− return i;
38− }
39− }
40− return 0;
34+ return Controller.findAction(name);
4135 }
4236
4337 addLine = function(origin, vector, color) {
examples/libraries/walkApi.js+2−18
@@ -18,24 +18,8 @@ Script.include("./libraries/walkConstants.js"); · 17 unmodified lines
1818
1919 Avatar = function() {
2020 // if Hydras are connected, the only way to enable use is to never set any arm joint rotation
21− this.hydraCheck = function() {
22− // function courtesy of Thijs Wenker (frisbee.js)
23− var numberOfButtons = Controller.getNumberOfButtons();
24− var numberOfTriggers = Controller.getNumberOfTriggers();
25− var numberOfSpatialControls = Controller.getNumberOfSpatialControls();
26− const HYDRA_BUTTONS = 12;
27− const HYDRA_TRIGGERS = 2;
28− const HYDRA_CONTROLLERS_PER_TRIGGER = 2;
29− var controllersPerTrigger = numberOfSpatialControls / numberOfTriggers;
30− if (numberOfButtons == HYDRA_BUTTONS &&
31− numberOfTriggers == HYDRA_TRIGGERS &&
32− controllersPerTrigger == HYDRA_CONTROLLERS_PER_TRIGGER) {
33− print('walk.js info: Razer Hydra detected. Setting arms free (not controlled by script)');
34− return true;
35− } else {
36− print('walk.js info: Razer Hydra not detected. Arms will be controlled by script.');
37− return false;
38− }
21+ this.hydraCheck = function () {
22+ return Controller.Hardware.Hydra !== undefined;
3923 }
4024 // settings
4125 this.headFree = true;
examples/mouseLook.js+1−1
@@ -38,7 +38,7 @@ var mouseLook = (function () { · 37 unmodified lines
3838 keyboardID = 0;
3939
4040 function onKeyPressEvent(event) {
41− if (event.text == 'M') {
41+ if (event.text == 'm') {
4242 active = !active;
4343 updateMapping();
4444 }
examples/stick.js+5−13
@@ -10,7 +10,7 @@ · 9 unmodified lines
1010 // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
1111 //
1212
13−var hand = "left";
13+var hand = "right";
1414 var nullActionID = "00000000-0000-0000-0000-000000000000";
1515 var controllerID;
1616 var controllerActive;
@@ -32,7 +32,7 @@ function makeNewStick() { · 15 unmodified lines
3232 modelURL: "https://hifi-public.s3.amazonaws.com/eric/models/stick.fbx",
3333 compoundShapeURL: "https://hifi-public.s3.amazonaws.com/eric/models/stick.obj",
3434 dimensions: {x: .11, y: .11, z: 1.0},
35− position: MyAvatar.getRightPalmPosition(), // initial position doesn't matter, as long as it's close
35+ position: MyAvatar.rightHandPosition, // initial position doesn't matter, as long as it's close
3636 rotation: MyAvatar.orientation,
3737 damping: .1,
3838 collisionSoundURL: "http://public.highfidelity.io/sounds/Collisions-hitsandslaps/67LCollision07.wav",
@@ -84,23 +84,15 @@ function mouseMoveEvent(event) { · 45 unmodified lines
8484 }
8585
8686
87−function initControls(){
88− if (hand == "right") {
89− controllerID = 3; // right handed
90− } else {
91− controllerID = 4; // left handed
92− }
93−}
94−
95−
9687 function update(deltaTime){
97− var palmPosition = Controller.getSpatialControlPosition(controllerID);
88+ var handPose = (hand == "right") ? MyAvatar.rightHandPose : MyAvatar.leftHandPose;
89+ var palmPosition = handPose.translation;
9890 controllerActive = (Vec3.length(palmPosition) > 0);
9991 if(!controllerActive){
10092 return;
10193 }
10294
103− stickOrientation = Controller.getSpatialControlRawRotation(controllerID);
95+ stickOrientation = handPose.rotation;
10496 var adjustment = Quat.fromPitchYawRollDegrees(180, 0, 0);
10597 stickOrientation = Quat.multiply(stickOrientation, adjustment);
10698
examples/tests/controllerInterfaceTest.js+77−0
@@ -0,0 +1,77 @@
1+ControllerTest = function() {
2+ var standard = Controller.Standard;
3+ var actions = Controller.Actions;
4+ var xbox = Controller.Hardware.GamePad;
5+ this.mappingEnabled = false;
6+ this.mapping = Controller.newMapping();
7+ this.mapping.from(standard.LX).when([standard.LB, standard.RB]).to(actions.Yaw);
8+ this.mapping.from(standard.RX).to(actions.StepYaw);
9+ this.mapping.from(standard.RY).invert().to(actions.Pitch);
10+ this.mapping.from(standard.RY).invert().to(actions.Pitch);
11+
12+
13+ var testMakeAxis = false;
14+ if (testMakeAxis) {
15+ this.mapping.makeAxis(standard.LB, standard.RB).pulse(0.25).scale(40.0).to(actions.StepYaw);
16+ }
17+
18+ var testStepYaw = false;
19+ if (!testMakeAxis && testStepYaw){
20+ this.mapping.from(standard.LB).pulse(0.10).invert().scale(40.0).to(actions.StepYaw);
21+ this.mapping.from(standard.RB).pulse(0.10).scale(15.0).to(actions.StepYaw);
22+ }
23+
24+ var testFunctionSource = false;
25+ if (testFunctionSource) {
26+ this.mapping.from(function(){
27+ return Math.sin(Date.now() / 250);
28+ }).to(actions.Yaw);
29+ }
30+
31+ var testFunctionDest = true;
32+ if (testFunctionDest) {
33+ this.mapping.from(standard.DU).pulse(1.0).to(function(value){
34+ if (value != 0.0) {
35+ print(value);
36+ }
37+ });
38+
39+ }
40+
41+ this.mapping.enable();
42+ this.mappingEnabled = true;
43+
44+ var dumpInputs = false;
45+ if (dumpInputs) {
46+ print("Actions");
47+ for (var prop in Controller.Actions) {
48+ print("\t" + prop);
49+ }
50+ print("Standard");
51+ for (var prop in Controller.Standard) {
52+ print("\t" + prop);
53+ }
54+ print("Hardware");
55+ for (var prop in Controller.Hardware) {
56+ print("\t" + prop);
57+ for (var prop2 in Controller.Hardware[prop]) {
58+ print("\t\t" + prop2);
59+ }
60+ }
61+ print("Done");
62+ }
63+
64+ var that = this;
65+ Script.scriptEnding.connect(function() {
66+ that.onCleanup();
67+ });
68+}
69+
70+ControllerTest.prototype.onCleanup = function() {
71+ if (this.mappingEnabled) {
72+ this.mapping.disable();
73+ }
74+}
75+
76+
77+new ControllerTest();
examples/toybox/ping_pong_gun/pingPongGun.js+9−8
@@ -45,6 +45,12 @@ · 44 unmodified lines
4545 green: 255,
4646 blue: 255
4747 };
48+
49+ var TRIGGER_CONTROLS = [
50+ Controller.Standard.LT,
51+ Controller.Standard.RT,
52+ ];
53+
4854
4955 PingPongGun.prototype = {
5056 hand: null,
@@ -53,11 +59,11 @@ · 2 unmodified lines
5359 canShoot: false,
5460 canShootTimeout: null,
5561 setRightHand: function() {
56− this.hand = 'RIGHT';
62+ this.hand = 1;
5763 },
5864
5965 setLeftHand: function() {
60− this.hand = 'LEFT';
66+ this.hand = 0;
6167 },
6268
6369 startNearGrab: function() {
@@ -92,12 +98,7 @@ · 28 unmodified lines
9298 },
9399
94100 checkTriggerPressure: function(gunHand) {
95− var handClickString = gunHand + "_HAND_CLICK";
96−
97− var handClick = Controller.findAction(handClickString);
98−
99− this.triggerValue = Controller.getActionValue(handClick);
100−
101+ this.triggerValue = Controller.getValue(TRIGGER_CONTROLS[gunHand]);
101102 if (this.triggerValue < RELOAD_THRESHOLD) {
102103 // print('RELOAD');
103104 this.canShoot = true;
examples/toybox/spray_paint/sprayPaintCan.js+8−7
@@ -33,12 +33,17 @@ · 32 unmodified lines
3333 var MIN_POINT_DISTANCE = 0.01;
3434 var STROKE_WIDTH = 0.02;
3535
36+ var TRIGGER_CONTROLS = [
37+ Controller.Standard.LT,
38+ Controller.Standard.RT,
39+ ];
40+
3641 this.setRightHand = function () {
37− this.hand = 'RIGHT';
42+ this.hand = 1;
3843 }
3944
4045 this.setLeftHand = function () {
41− this.hand = 'LEFT';
46+ this.hand = 0;
4247 }
4348
4449 this.startNearGrab = function () {
@@ -46,11 +51,7 @@ · 1 unmodified line
4651 }
4752
4853 this.toggleWithTriggerPressure = function () {
49− var handClickString = this.whichHand + "_HAND_CLICK";
50−
51− var handClick = Controller.findAction(handClickString);
52−
53− this.triggerValue = Controller.getActionValue(handClick);
54+ this.triggerValue = Controller.getValue(TRIGGER_CONTROLS[this.whichHand]);
5455 if (this.triggerValue < DISABLE_SPRAY_THRESHOLD && this.spraying === true) {
5556 this.spraying = false;
5657 this.disableStream();
interface/CMakeLists.txt+12−5
@@ -2,7 +2,12 @@ set(TARGET_NAME interface) · 1 unmodified line
22 project(${TARGET_NAME})
33
44 # set a default root dir for each of our optional externals if it was not passed
5−set(OPTIONAL_EXTERNALS "Faceshift" "LeapMotion" "RtMidi" "RSSDK" "3DConnexionClient" "iViewHMD")
5+set(OPTIONAL_EXTERNALS "LeapMotion" "RtMidi" "RSSDK" "iViewHMD")
6+
7+if(WIN32)
8+ list(APPEND OPTIONAL_EXTERNALS "3DConnexionClient")
9+endif()
10+
611 foreach(EXTERNAL ${OPTIONAL_EXTERNALS})
712 string(TOUPPER ${EXTERNAL} ${EXTERNAL}_UPPERCASE)
813 if (NOT ${${EXTERNAL}_UPPERCASE}_ROOT_DIR)
@@ -99,15 +104,17 @@ endif() · 90 unmodified lines
99104 link_hifi_libraries(shared octree environment gpu gl procedural model render
100105 fbx networking model-networking entities avatars
101106 audio audio-client animation script-engine physics
102− render-utils entities-renderer ui auto-updater
103− plugins display-plugins input-plugins)
107+ render-utils entities-renderer ui auto-updater
108+ controllers plugins display-plugins input-plugins )
104109
110+#fixme find a way to express faceshift as a plugin
105111 target_bullet()
106112 target_glew()
107113 target_opengl()
108114
109−
110−add_dependency_external_projects(sdl2)
115+if (WIN32 OR APPLE)
116+ target_faceshift()
117+endif()
111118
112119 # perform standard include and linking for found externals
113120 foreach(EXTERNAL ${OPTIONAL_EXTERNALS})
interface/external/faceshift/readme.txt+0−26
@@ -1,26 +0,0 @@
1−
2−Instructions for adding the Faceshift library to Interface
3−Stephen Birarda, July 18th, 2014
4−
5−OS X users: You can also use homebrew to get the Faceshift library by tapping our repo - highfidelity/homebrew-formulas
6−and then calling 'brew install highfidelity/formulas/faceshift'.
7−
8−You can download the Faceshift SDK from http://download.faceshift.com/faceshift-network.zip.
9−
10−Create a ‘faceshift’ folder under interface/externals.
11−
12−You may optionally choose to place this folder in a location outside the repository (so you can re-use with different checkouts and different projects).
13−
14−If so our CMake find module expects you to set the ENV variable 'HIFI_LIB_DIR' to a directory containing a subfolder ‘faceshift’ that contains the lib and include folders.
15−
16−1. Build a Faceshift static library from the fsbinarystream.cpp file.
17− Windows: Win32 console application; no precompiled header or SDL checks; no ATL or MFC headers; Project Properties, Configuration Type = Static Library (.lib).
18−
19−2. Copy the library files to the ‘lib’ folder in your Faceshift folder.
20− OSX: If you build a release version call it libfaceshift.a. The debug version should be called libfaceshiftd.a.
21− Windows: The release and debug versions should be called faceshift.lib and faceshiftd.lib, respectively. Copy them into a ‘Win32’ folder in your ‘lib’ folder.
22−
23−3. Copy the fsbinarystream.h header file from the Faceshift SDK into the ‘include’ folder in your Faceshift folder.
24−
25−4. Clear your build directory, run cmake and build, and you should be all set.
26−
interface/external/sdl2/readme.txt+0−13
@@ -1,13 +0,0 @@
1−
2−Instructions for adding the SDL library (SDL2) to Interface
3−David Rowe, 11 Jan 2015
4−
5−You can download the SDL development library from https://www.libsdl.org/. Interface has been tested with version 2.0.3.
6−
7−1. Copy the include and lib folders into the interface/externals/sdl2 folder.
8− This readme.txt should be there as well.
9−
10− You may optionally choose to copy the SDK folders to a location outside the repository (so you can re-use with different checkouts and different projects).
11− If so our CMake find module expects you to set the ENV variable 'HIFI_LIB_DIR' to a directory containing a subfolder 'sdl2' that contains the two folders mentioned above.
12−
13−2. Clear your build directory, run cmake and build, and you should be all set.
interface/external/sixense/readme.txt+0−10
@@ -1,10 +0,0 @@
1−
2−Instructions for adding the Sixense driver to Interface
3−Andrzej Kapolka, November 18, 2013
4−
5−1. Copy the Sixense sdk folders (bin, include, lib, and samples) into the interface/external/Sixense folder. This readme.txt should be there as well.
6−
7− You may optionally choose to copy the SDK folders to a location outside the repository (so you can re-use with different checkouts and different projects).
8− If so our CMake find module expects you to set the ENV variable 'HIFI_LIB_DIR' to a directory containing a subfolder 'sixense' that contains the folders mentioned above.
9−
10−3. Delete your build directory, run cmake and build, and you should be all set.
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 @@
1+import QtQuick 2.1
2+import QtQuick.Controls 1.0
3+import QtQuick.Layouts 1.0
4+import QtQuick.Dialogs 1.0
5+
6+Rectangle {
7+ id: root
8+ property int size: 64
9+ width: size
10+ height: size
11+ color: 'black'
12+ property int controlId: 0
13+ property real value: 0.5
14+ property int scrollWidth: 1
15+ property real min: 0.0
16+ property real max: 1.0
17+ property bool log: false
18+ property real range: max - min
19+ property color lineColor: 'yellow'
20+ property bool bar: false
21+ property real lastHeight: -1
22+ property string label: ""
23+
24+ function update() {
25+ value = Controller.getValue(controlId);
26+ if (log) {
27+ var log = Math.log(10) / Math.log(Math.abs(value));
28+ var sign = Math.sign(value);
29+ value = log * sign;
30+ }
31+ canvas.requestPaint();
32+ }
33+
34+ function drawHeight() {
35+ if (value < min) {
36+ return 0;
37+ }
38+ if (value > max) {
39+ return height;
40+ }
41+ return ((value - min) / range) * height;
42+ }
43+
44+ Timer {
45+ interval: 50; running: true; repeat: true
46+ onTriggered: root.update()
47+ }
48+
49+ Canvas {
50+ id: canvas
51+ anchors.fill: parent
52+ antialiasing: false
53+
54+ Text {
55+ anchors.top: parent.top
56+ text: root.label
57+ color: 'white'
58+ }
59+
60+ Text {
61+ anchors.right: parent.right
62+ anchors.top: parent.top
63+ text: root.max
64+ color: 'white'
65+ }
66+
67+ Text {
68+ anchors.right: parent.right
69+ anchors.bottom: parent.bottom
70+ text: root.min
71+ color: 'white'
72+ }
73+
74+ function scroll() {
75+ var ctx = canvas.getContext('2d');
76+ var image = ctx.getImageData(0, 0, canvas.width, canvas.height);
77+ ctx.beginPath();
78+ ctx.clearRect(0, 0, canvas.width, canvas.height);
79+ ctx.drawImage(image, -root.scrollWidth, 0, canvas.width, canvas.height)
80+ ctx.restore()
81+ }
82+
83+ onPaint: {
84+ scroll();
85+ var ctx = canvas.getContext('2d');
86+ ctx.save();
87+ var currentHeight = root.drawHeight();
88+ if (root.lastHeight == -1) {
89+ root.lastHeight = currentHeight
90+ }
91+
92+// var x = canvas.width - root.drawWidth;
93+// var y = canvas.height - drawHeight;
94+// ctx.fillStyle = root.color
95+// ctx.fillRect(x, y, root.drawWidth, root.bar ? drawHeight : 1)
96+// ctx.fill();
97+// ctx.restore()
98+
99+
100+ ctx.beginPath();
101+ ctx.lineWidth = 1
102+ ctx.strokeStyle = root.lineColor
103+ ctx.moveTo(canvas.width - root.scrollWidth, root.lastHeight).lineTo(canvas.width, currentHeight)
104+ ctx.stroke()
105+ ctx.restore()
106+ root.lastHeight = currentHeight
107+ }
108+ }
109+}
110+
111+
interface/resources/qml/TestControllers.qml+161−0
@@ -0,0 +1,161 @@
1+import QtQuick 2.3
2+import QtQuick.Controls 1.2
3+import QtQuick.Layouts 1.0
4+import QtQuick.Dialogs 1.0
5+
6+import "controller"
7+import "controls" as HifiControls
8+import "styles"
9+
10+HifiControls.VrDialog {
11+ id: root
12+ HifiConstants { id: hifi }
13+ title: "Controller Test"
14+ resizable: true
15+ contentImplicitWidth: clientArea.implicitWidth
16+ contentImplicitHeight: clientArea.implicitHeight
17+ backgroundColor: "beige"
18+
19+ property var actions: Controller.Actions
20+ property var standard: Controller.Standard
21+ property var hydra: null
22+ property var testMapping: null
23+ property bool testMappingEnabled: false
24+ property var xbox: null
25+
26+ function buildMapping() {
27+ testMapping = Controller.newMapping();
28+ testMapping.fromQml(standard.RY).invert().toQml(actions.Pitch);
29+ testMapping.fromQml(function(){
30+ return Math.sin(Date.now() / 250);
31+ }).toQml(actions.Yaw);
32+ //testMapping.makeAxis(standard.LB, standard.RB).to(actions.Yaw);
33+ // Step yaw takes a number of degrees
34+ testMapping.fromQml(standard.LB).pulse(0.10).invert().scale(40.0).toQml(actions.StepYaw);
35+ testMapping.fromQml(standard.RB).pulse(0.10).scale(15.0).toQml(actions.StepYaw);
36+ testMapping.fromQml(standard.RX).scale(15.0).toQml(actions.StepYaw);
37+ }
38+
39+ function toggleMapping() {
40+ testMapping.enable(!testMappingEnabled);
41+ testMappingEnabled = !testMappingEnabled;
42+ }
43+
44+ Component.onCompleted: {
45+ enabled = true
46+ var xboxRegex = /^GamePad/;
47+ var hydraRegex = /^Hydra/;
48+ for (var prop in Controller.Hardware) {
49+ if(xboxRegex.test(prop)) {
50+ root.xbox = Controller.Hardware[prop]
51+ print("found xbox")
52+ continue
53+ }
54+ if (hydraRegex.test(prop)) {
55+ root.hydra = Controller.Hardware[prop]
56+ print("found hydra")
57+ continue
58+ }
59+ }
60+ }
61+
62+ Column {
63+ id: clientArea
64+ spacing: 12
65+ x: root.clientX
66+ y: root.clientY
67+
68+ Row {
69+ spacing: 8
70+
71+ Button {
72+ text: !root.testMapping ? "Build Mapping" : (root.testMappingEnabled ? "Disable Mapping" : "Enable Mapping")
73+ onClicked: {
74+
75+ if (!root.testMapping) {
76+ root.buildMapping()
77+ } else {
78+ root.toggleMapping();
79+ }
80+ }
81+ }
82+ }
83+
84+ Row {
85+ Standard { device: root.standard; label: "Standard"; width: 180 }
86+ }
87+
88+ Row {
89+ spacing: 8
90+ Xbox { device: root.xbox; label: "XBox"; width: 180 }
91+ Hydra { device: root.hydra; width: 180 }
92+ }
93+
94+ Row {
95+ spacing: 4
96+ ScrollingGraph {
97+ controlId: Controller.Actions.Yaw
98+ label: "Yaw"
99+ min: -2.0
100+ max: 2.0
101+ size: 64
102+ }
103+
104+ ScrollingGraph {
105+ controlId: Controller.Actions.YawLeft
106+ label: "Yaw Left"
107+ min: -2.0
108+ max: 2.0
109+ size: 64
110+ }
111+
112+ ScrollingGraph {
113+ controlId: Controller.Actions.YawRight
114+ label: "Yaw Right"
115+ min: -2.0
116+ max: 2.0
117+ size: 64
118+ }
119+
120+ ScrollingGraph {
121+ controlId: Controller.Actions.StepYaw
122+ label: "StepYaw"
123+ min: -20.0
124+ max: 20.0
125+ size: 64
126+ }
127+ }
128+
129+ Row {
130+ ScrollingGraph {
131+ controlId: Controller.Actions.TranslateZ
132+ label: "TranslateZ"
133+ min: -2.0
134+ max: 2.0
135+ size: 64
136+ }
137+
138+ ScrollingGraph {
139+ controlId: Controller.Actions.Forward
140+ label: "Forward"
141+ min: -2.0
142+ max: 2.0
143+ size: 64
144+ }
145+
146+ ScrollingGraph {
147+ controlId: Controller.Actions.Backward
148+ label: "Backward"
149+ min: -2.0
150+ max: 2.0
151+ size: 64
152+ }
153+
154+ }
155+ }
156+} // dialog
157+
158+
159+
160+
161+
interface/resources/qml/controller/AnalogButton.qml+45−0
@@ -0,0 +1,45 @@
1+import QtQuick 2.1
2+import QtQuick.Controls 1.0
3+import QtQuick.Layouts 1.0
4+import QtQuick.Dialogs 1.0
5+
6+Item {
7+ id: root
8+ property int size: 64
9+ width: size
10+ height: size
11+ property int controlId: 0
12+ property real value: 0
13+ property color color: 'black'
14+
15+ function update() {
16+ value = controlId ? Controller.getValue(controlId) : 0;
17+ canvas.requestPaint();
18+ }
19+
20+ Timer {
21+ interval: 50; running: true; repeat: true
22+ onTriggered: root.update();
23+ }
24+
25+ Canvas {
26+ id: canvas
27+ anchors.fill: parent
28+ antialiasing: false
29+
30+ onPaint: {
31+ var ctx = canvas.getContext('2d');
32+ ctx.save();
33+ ctx.beginPath();
34+ ctx.clearRect(0, 0, canvas.width, canvas.height);
35+ var fillHeight = root.value * canvas.height;
36+
37+ ctx.fillStyle = 'red'
38+ ctx.fillRect(0, canvas.height - fillHeight, canvas.width, fillHeight);
39+ ctx.fill();
40+ ctx.restore()
41+ }
42+ }
43+}
44+
45+
interface/resources/qml/controller/AnalogStick.qml+55−0
@@ -0,0 +1,55 @@
1+import QtQuick 2.1
2+import QtQuick.Controls 1.0
3+import QtQuick.Layouts 1.0
4+import QtQuick.Dialogs 1.0
5+
6+Item {
7+ id: root
8+ property int size: 64
9+ width: size
10+ height: size
11+ property bool invertY: false
12+
13+
14+ property int halfSize: size / 2
15+ property var controlIds: [ 0, 0 ]
16+ property vector2d value: Qt.vector2d(0, 0)
17+
18+ function update() {
19+ value = Qt.vector2d(
20+ controlIds[0] ? Controller.getValue(controlIds[0]) : 0,
21+ controlIds[1] ? Controller.getValue(controlIds[1]) : 0
22+ );
23+ if (root.invertY) {
24+ value.y = value.y * -1.0
25+ }
26+ canvas.requestPaint();
27+ }
28+
29+ Timer {
30+ interval: 50; running: controlIds; repeat: true
31+ onTriggered: root.update()
32+ }
33+
34+ Canvas {
35+ id: canvas
36+ anchors.fill: parent
37+ antialiasing: false
38+
39+ onPaint: {
40+ var ctx = canvas.getContext('2d');
41+ ctx.save();
42+ ctx.beginPath();
43+ ctx.clearRect(0, 0, width, height);
44+ ctx.fill();
45+ ctx.translate(root.halfSize, root.halfSize)
46+ ctx.lineWidth = 4
47+ ctx.strokeStyle = Qt.rgba(Math.max(Math.abs(value.x), Math.abs(value.y)), 0, 0, 1)
48+ ctx.moveTo(0, 0).lineTo(root.value.x * root.halfSize, root.value.y * root.halfSize)
49+ ctx.stroke()
50+ ctx.restore()
51+ }
52+ }
53+}
54+
55+
interface/resources/qml/controller/Hydra.qml+34−0
@@ -0,0 +1,34 @@
1+import QtQuick 2.1
2+import QtQuick.Controls 1.0
3+import QtQuick.Layouts 1.0
4+import QtQuick.Dialogs 1.0
5+
6+import "hydra"
7+
8+Item {
9+ id: root
10+ width: 480
11+ height: width * 3.0 / 4.0
12+ property var device
13+ property real scale: width / 480
14+ property real rightOffset: (width / 2) * scale
15+
16+ Image {
17+ anchors.fill: parent
18+ source: "hydra/hydra.png"
19+
20+ HydraStick {
21+ leftStick: true
22+ scale: root.scale
23+ device: root.device
24+ }
25+
26+
27+ HydraStick {
28+ leftStick: false
29+ scale: root.scale
30+ device: root.device
31+ }
32+
33+ }
34+}
interface/resources/qml/controller/Standard.qml+35−0
@@ -0,0 +1,35 @@
1+import QtQuick 2.1
2+import QtQuick.Controls 1.0
3+import QtQuick.Layouts 1.0
4+import QtQuick.Dialogs 1.0
5+
6+import "xbox"
7+
8+Item {
9+ id: root
10+ property real aspect: 300.0 / 215.0
11+ width: 300
12+ height: width / aspect
13+ property var device
14+ property string label: ""
15+ property real scale: width / 300.0
16+
17+ Xbox {
18+ width: root.width; height: root.height
19+ device: root.device
20+ }
21+
22+ // Left primary
23+ ToggleButton {
24+ x: 0; y: parent.height - height;
25+ controlId: root.device.LeftPrimaryThumb
26+ width: 16 * root.scale; height: 16 * root.scale
27+ }
28+
29+ // Left primary
30+ ToggleButton {
31+ x: parent.width - width; y: parent.height - height;
32+ controlId: root.device.RightPrimaryThumb
33+ width: 16 * root.scale; height: 16 * root.scale
34+ }
35+}
interface/resources/qml/controller/ToggleButton.qml+45−0
@@ -0,0 +1,45 @@
1+import QtQuick 2.1
2+import QtQuick.Controls 1.0
3+import QtQuick.Layouts 1.0
4+import QtQuick.Dialogs 1.0
5+
6+Item {
7+ id: root
8+ width: size
9+ height: size
10+ property int size: 64
11+ property int controlId: 0
12+ property real value: 0
13+ property color color: 'black'
14+
15+ function update() {
16+ value = controlId ? Controller.getValue(controlId) : 0;
17+ canvas.requestPaint();
18+ }
19+
20+ Timer {
21+ interval: 50; running: root.controlId; repeat: true
22+ onTriggered: root.update()
23+ }
24+
25+ Canvas {
26+ id: canvas
27+ anchors.fill: parent
28+ antialiasing: false
29+
30+ onPaint: {
31+ var ctx = canvas.getContext('2d');
32+ ctx.save();
33+ ctx.beginPath();
34+ ctx.clearRect(0, 0, width, height);
35+ if (root.value > 0.0) {
36+ ctx.fillStyle = root.color
37+ ctx.fillRect(0, 0, canvas.width, canvas.height);
38+ }
39+ ctx.fill();
40+ ctx.restore()
41+ }
42+ }
43+}
44+
45+
interface/resources/qml/controller/Xbox.qml+104−0
@@ -0,0 +1,104 @@
1+import QtQuick 2.1
2+import QtQuick.Controls 1.0
3+import QtQuick.Layouts 1.0
4+import QtQuick.Dialogs 1.0
5+
6+import "xbox"
7+
8+Item {
9+ id: root
10+ property real aspect: 300.0 / 215.0
11+ width: 300
12+ height: width / aspect
13+ property var device
14+ property string label: ""
15+ property real scale: width / 300.0
16+
17+ Image {
18+ Text {
19+ anchors.left: parent.left
20+ anchors.top: parent.top
21+ text: root.label
22+ visible: root.label != ""
23+ }
24+ anchors.fill: parent
25+ source: "xbox/xbox360-controller-md.png"
26+
27+ LeftAnalogStick {
28+ device: root.device
29+ x: (65 * root.scale) - width / 2; y: (42 * root.scale) - height / 2
30+ }
31+
32+ // Left stick press
33+ ToggleButton {
34+ controlId: root.device.LS
35+ width: 16 * root.scale; height: 16 * root.scale
36+ x: (65 * root.scale) - width / 2; y: (42 * root.scale) - height / 2
37+ }
38+
39+
40+ RightAnalogStick {
41+ device: root.device
42+ x: (193 * root.scale) - width / 2; y: (96 * root.scale) - height / 2
43+ }
44+
45+ // Right stick press
46+ ToggleButton {
47+ controlId: root.device.RS
48+ width: 16 * root.scale; height: 16 * root.scale
49+ x: (193 * root.scale) - width / 2; y: (96 * root.scale) - height / 2
50+ }
51+
52+ // Left trigger
53+ AnalogButton {
54+ controlId: root.device.LT
55+ width: 8; height: 64
56+ x: (20 * root.scale); y: (7 * root.scale)
57+ }
58+
59+ // Right trigger
60+ AnalogButton {
61+ controlId: root.device.RT
62+ width: 8; height: 64
63+ x: (272 * root.scale); y: (7 * root.scale)
64+ }
65+
66+ // Left bumper
67+ ToggleButton {
68+ controlId: root.device.LB
69+ width: 32 * root.scale; height: 16 * root.scale
70+ x: (40 * root.scale); y: (7 * root.scale)
71+ }
72+
73+ // Right bumper
74+ ToggleButton {
75+ controlId: root.device.RB
76+ width: 32 * root.scale; height: 16 * root.scale
77+ x: (root.width - width) - (40 * root.scale); y: (7 * root.scale)
78+ }
79+
80+ DPad {
81+ device: root.device
82+ size: 48 * root.scale
83+ x: (80 * root.scale); y: (71 * root.scale)
84+ }
85+
86+ XboxButtons {
87+ device: root.device
88+ size: 65 * root.scale
89+ x: (206 * root.scale); y: (19 * root.scale)
90+ }
91+
92+ ToggleButton {
93+ controlId: root.device.Back
94+ width: 16 * root.scale; height: 12 * root.scale
95+ x: (112 * root.scale); y: (45 * root.scale)
96+ }
97+
98+ ToggleButton {
99+ controlId: root.device.Start
100+ width: 16 * root.scale; height: 12 * root.scale
101+ x: (177 * root.scale); y: (45 * root.scale)
102+ }
103+ }
104+}
interface/resources/qml/controller/hydra/HydraButtons.qml+18−0
@@ -0,0 +1,18 @@
1+import QtQuick 2.1
2+import QtQuick.Controls 1.0
3+import QtQuick.Layouts 1.0
4+import QtQuick.Dialogs 1.0
5+
6+import ".."
7+
8+Item {
9+ id: root
10+ width: 72 * scale
11+ height: 48 * scale
12+ property var device
13+ property real scale: 1.0
14+ property bool leftStick: true
15+
16+}
17+
18+
interface/resources/qml/controller/hydra/HydraStick.qml+91−0
@@ -0,0 +1,91 @@
1+import QtQuick 2.1
2+import QtQuick.Controls 1.0
3+import QtQuick.Layouts 1.0
4+import QtQuick.Dialogs 1.0
5+
6+import ".."
7+
8+Item {
9+ id: root
10+ property var device
11+ property real scale: 1.0
12+ property bool leftStick: true
13+ width: parent.width / 2; height: parent.height
14+ x: leftStick ? 0 : parent.width / 2
15+
16+ Text {
17+ x: parent.width / 2 - width / 2; y: parent.height / 2 - height / 2
18+ text: root.leftStick ? "L" : "R"
19+ color: 'green'
20+ }
21+
22+ // Analog Stick
23+ AnalogStick {
24+ size: 64 * root.scale
25+ x: 127 * root.scale - width / 2; y: 45 * root.scale - width / 2; z: 100
26+ invertY: true
27+ controlIds: [
28+ root.leftStick ? root.device.LX : root.device.RX,
29+ root.leftStick ? root.device.LY : root.device.RY
30+ ]
31+ }
32+
33+ // Stick press
34+ ToggleButton {
35+ controlId: root.leftStick ? root.device.LS : root.device.RS
36+ width: 16 * root.scale; height: 16 * root.scale
37+ x: 127 * root.scale - width / 2; y: 45 * root.scale - width / 2;
38+ color: 'yellow'
39+ }
40+
41+ // Trigger
42+ AnalogButton {
43+ controlId: root.leftStick ? root.device.LT : root.device.RT
44+ width: 8 * root.scale ; height: 64 * root.scale
45+ y: 24 * root.scale
46+ x: root.leftStick ? (48 * root.scale) : root.width - (48 * root.scale) - width / 2
47+ }
48+
49+ // Bumper
50+ ToggleButton {
51+ controlId: root.leftStick ? root.device.LB : root.device.RB
52+ height: 16 * root.scale; width: 32 * root.scale
53+ x: 128 * root.scale - width / 2; y: 24 * root.scale
54+ color: 'red'
55+ }
56+
57+ ToggleButton {
58+ controlId: root.leftStick ? root.device.L0 : root.device.R0
59+ height: 16 * root.scale; width: 4 * root.scale
60+ x: 128 * root.scale - width / 2; y: 109 * root.scale
61+ color: 'yellow'
62+ }
63+
64+ ToggleButton {
65+ controlId: root.leftStick ? root.device.L1 : root.device.R1
66+ width: 16 * root.scale; height: 16 * root.scale
67+ x: 103 * root.scale - width / 2; y: 100 * root.scale - height / 2
68+ color: 'yellow'
69+ }
70+
71+ ToggleButton {
72+ controlId: root.leftStick ? root.device.L2 : root.device.R2
73+ width: 16 * root.scale; height: 16 * root.scale
74+ x: 148 * root.scale - width / 2; y: 100 * root.scale - height / 2
75+ color: 'yellow'
76+ }
77+
78+ ToggleButton {
79+ controlId: root.leftStick ? root.device.L3 : root.device.R3
80+ width: 16 * root.scale; height: 16 * root.scale
81+ x: 97 * root.scale - width / 2; y: 76 * root.scale - height / 2
82+ color: 'yellow'
83+ }
84+
85+ ToggleButton {
86+ controlId: root.leftStick ? root.device.L4 : root.device.R4
87+ width: 16 * root.scale; height: 16 * root.scale
88+ x: 155 * root.scale - width / 2; y: 76 * root.scale - height / 2
89+ color: 'yellow'
90+ }
91+}
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 @@
1+import QtQuick 2.1
2+import QtQuick.Controls 1.0
3+import QtQuick.Layouts 1.0
4+import QtQuick.Dialogs 1.0
5+
6+import ".."
7+
8+Item {
9+ id: root
10+ property int size: 64
11+ width: size
12+ height: size
13+ property int spacer: size / 3
14+ property var device
15+ property color color: 'black'
16+
17+ ToggleButton {
18+ controlId: device.Up
19+ x: spacer
20+ width: spacer; height: spacer
21+ }
22+
23+ ToggleButton {
24+ controlId: device.Left
25+ y: spacer
26+ width: spacer; height: spacer
27+ }
28+
29+ ToggleButton {
30+ controlId: device.Right
31+ x: spacer * 2; y: spacer
32+ width: spacer; height: spacer
33+ }
34+
35+ ToggleButton {
36+ controlId: device.Down
37+ x: spacer; y: spacer * 2
38+ width: spacer; height: spacer
39+ }
40+}
41+
42+
interface/resources/qml/controller/xbox/LeftAnalogStick.qml+21−0
@@ -0,0 +1,21 @@
1+import QtQuick 2.1
2+import QtQuick.Controls 1.0
3+import QtQuick.Layouts 1.0
4+import QtQuick.Dialogs 1.0
5+
6+import ".."
7+
8+Item {
9+ id: root
10+ property int size: 64
11+ width: size
12+ height: size
13+ property var device
14+
15+ AnalogStick {
16+ size: size
17+ controlIds: [ device.LX, device.LY ]
18+ }
19+}
20+
21+
interface/resources/qml/controller/xbox/RightAnalogStick.qml+21−0
@@ -0,0 +1,21 @@
1+import QtQuick 2.1
2+import QtQuick.Controls 1.0
3+import QtQuick.Layouts 1.0
4+import QtQuick.Dialogs 1.0
5+
6+import ".."
7+
8+Item {
9+ id: root
10+ property int size: 64
11+ width: size
12+ height: size
13+ property var device
14+
15+ AnalogStick {
16+ size: size
17+ controlIds: [ device.RX, device.RY ]
18+ }
19+}
20+
21+
interface/resources/qml/controller/xbox/XboxButtons.qml+46−0
@@ -0,0 +1,46 @@
1+import QtQuick 2.1
2+import QtQuick.Controls 1.0
3+import QtQuick.Layouts 1.0
4+import QtQuick.Dialogs 1.0
5+
6+import ".."
7+
8+Item {
9+ id: root
10+ property int size: 64
11+ width: size
12+ height: size
13+ property int spacer: size / 3
14+ property var device
15+ property color color: 'black'
16+
17+ ToggleButton {
18+ controlId: device.Y
19+ x: spacer
20+ width: spacer; height: spacer
21+ color: 'yellow'
22+ }
23+
24+ ToggleButton {
25+ controlId: device.X
26+ y: spacer
27+ width: spacer; height: spacer
28+ color: 'blue'
29+ }
30+
31+ ToggleButton {
32+ controlId: device.B
33+ x: spacer * 2; y: spacer
34+ width: spacer; height: spacer
35+ color: 'red'
36+ }
37+
38+ ToggleButton {
39+ controlId: device.A
40+ x: spacer; y: spacer * 2
41+ width: spacer; height: spacer
42+ color: 'green'
43+ }
44+}
45+
46+
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
1717 #include <glm/gtx/vector_angle.hpp>
1818 #include <glm/gtc/type_ptr.hpp>
1919
20−#include <QAbstractNativeEventFilter>
21−#include <QActionGroup>
22−#include <QDebug>
23−#include <QDesktopServices>
24−#include <QDesktopWidget>
25−#include <QImage>
26−#include <QFileDialog>
27−#include <QInputDialog>
28−#include <QKeyEvent>
29−#include <QMediaPlayer>
30−#include <QMenuBar>
31−#include <QMessageBox>
32−#include <QMimeData>
33−#include <QMouseEvent>
34−#include <QNetworkDiskCache>
35−#include <QObject>
36−#include <QScreen>
37−#include <QTimer>
38−#include <QUrl>
39−#include <QWheelEvent>
40−#include <QWindow>
20+#include <QtCore/QDebug>
21+#include <QtCore/QObject>
22+#include <QtCore/QUrl>
23+#include <QtCore/QTimer>
24+#include <QtCore/QAbstractNativeEventFilter>
25+#include <QtCore/QMimeData>
26+
27+#include <QtGui/QScreen>
28+#include <QtGui/QImage>
29+#include <QtGui/QWheelEvent>
30+#include <QtGui/QWindow>
31+#include <QtQml/QQmlContext>
32+#include <QtGui/QKeyEvent>
33+#include <QtGui/QMouseEvent>
34+#include <QtGui/QDesktopServices>
35+
36+#include <QtWidgets/QActionGroup>
37+#include <QtWidgets/QDesktopWidget>
38+#include <QtWidgets/QFileDialog>
39+#include <QtWidgets/QInputDialog>
40+#include <QtWidgets/QMenuBar>
41+#include <QtWidgets/QMessageBox>
42+
43+#include <QtMultimedia/QMediaPlayer>
44+
45+#include <QtNetwork/QNetworkDiskCache>
4146
4247 #include <AccountManager.h>
4348 #include <AddressManager.h>
@@ -60,7 +65,8 @@ · 16 unmodified lines
6065 #include <InfoView.h>
6166 #include <input-plugins/InputPlugin.h>
6267 #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>
6470 #include <LogHandler.h>
6571 #include <MainWindow.h>
6672 #include <MessageDialog.h>
@@ -120,6 +126,7 @@ · 53 unmodified lines
120126 #include "scripting/SettingsScriptingInterface.h"
121127 #include "scripting/WebWindowClass.h"
122128 #include "scripting/WindowScriptingInterface.h"
129+#include "scripting/ControllerScriptingInterface.h"
123130 #if defined(Q_OS_MAC) || defined(Q_OS_WIN)
124131 #include "SpeechRecognizer.h"
125132 #endif
@@ -137,6 +144,7 @@ · 11 unmodified lines
137144 #include "ui/UpdateDialog.h"
138145 #include "Util.h"
139146
147+
140148 // ON WIndows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU
141149 // FIXME seems to be broken.
142150 #if defined(Q_OS_WIN)
@@ -316,6 +324,7 @@ bool setupEssentials(int& argc, char** argv) { · 173 unmodified lines
316324 DependencyManager::set<EntityScriptingInterface>();
317325 DependencyManager::set<WindowScriptingInterface>();
318326 DependencyManager::set<HMDScriptingInterface>();
327+
319328 #if defined(Q_OS_MAC) || defined(Q_OS_WIN)
320329 DependencyManager::set<SpeechRecognizer>();
321330 #endif
@@ -327,7 +336,7 @@ bool setupEssentials(int& argc, char** argv) { · 5 unmodified lines
327336 DependencyManager::set<InterfaceActionFactory>();
328337 DependencyManager::set<AssetClient>();
329338 DependencyManager::set<UserInputMapper>();
330−
339+ DependencyManager::set<controller::ScriptingInterface, ControllerScriptingInterface>();
331340 return true;
332341 }
333342
@@ -339,45 +348,47 @@ int _keyboardFocusHighlightID{ -1 }; · 5 unmodified lines
339348 PluginContainer* _pluginContainer;
340349
341350 Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
342− QApplication(argc, argv),
343− _dependencyManagerIsSetup(setupEssentials(argc, argv)),
344− _window(new MainWindow(desktop())),
345− _toolWindow(NULL),
346− _undoStackScriptingInterface(&_undoStack),
347− _frameCount(0),
348− _fps(60.0f),
349− _physicsEngine(new PhysicsEngine(Vectors::ZERO)),
350− _entities(true, this, this),
351− _entityClipboardRenderer(false, this, this),
352− _entityClipboard(new EntityTree()),
353− _lastQueriedTime(usecTimestampNow()),
354− _mirrorViewRect(QRect(MIRROR_VIEW_LEFT_PADDING, MIRROR_VIEW_TOP_PADDING, MIRROR_VIEW_WIDTH, MIRROR_VIEW_HEIGHT)),
355− _firstRun("firstRun", true),
356− _previousScriptLocation("LastScriptLocation", DESKTOP_LOCATION),
357− _scriptsLocationHandle("scriptsLocation", DESKTOP_LOCATION),
358− _fieldOfView("fieldOfView", DEFAULT_FIELD_OF_VIEW_DEGREES),
359− _scaleMirror(1.0f),
360− _rotateMirror(0.0f),
361− _raiseMirror(0.0f),
362− _lastMouseMoveWasSimulated(false),
363− _enableProcessOctreeThread(true),
364− _runningScriptsWidget(NULL),
365− _runningScriptsWidgetWasVisible(false),
366− _lastNackTime(usecTimestampNow()),
367− _lastSendDownstreamAudioStats(usecTimestampNow()),
368− _aboutToQuit(false),
369− _notifiedPacketVersionMismatchThisDomain(false),
370− _maxOctreePPS(maxOctreePacketsPerSecond.get()),
371− _lastFaceTrackerUpdate(0)
351+ QApplication(argc, argv),
352+ _dependencyManagerIsSetup(setupEssentials(argc, argv)),
353+ _window(new MainWindow(desktop())),
354+ _toolWindow(NULL),
355+ _undoStackScriptingInterface(&_undoStack),
356+ _frameCount(0),
357+ _fps(60.0f),
358+ _physicsEngine(new PhysicsEngine(Vectors::ZERO)),
359+ _entities(true, this, this),
360+ _entityClipboardRenderer(false, this, this),
361+ _entityClipboard(new EntityTree()),
362+ _lastQueriedTime(usecTimestampNow()),
363+ _mirrorViewRect(QRect(MIRROR_VIEW_LEFT_PADDING, MIRROR_VIEW_TOP_PADDING, MIRROR_VIEW_WIDTH, MIRROR_VIEW_HEIGHT)),
364+ _firstRun("firstRun", true),
365+ _previousScriptLocation("LastScriptLocation", DESKTOP_LOCATION),
366+ _scriptsLocationHandle("scriptsLocation", DESKTOP_LOCATION),
367+ _fieldOfView("fieldOfView", DEFAULT_FIELD_OF_VIEW_DEGREES),
368+ _scaleMirror(1.0f),
369+ _rotateMirror(0.0f),
370+ _raiseMirror(0.0f),
371+ _lastMouseMoveWasSimulated(false),
372+ _enableProcessOctreeThread(true),
373+ _runningScriptsWidget(NULL),
374+ _runningScriptsWidgetWasVisible(false),
375+ _lastNackTime(usecTimestampNow()),
376+ _lastSendDownstreamAudioStats(usecTimestampNow()),
377+ _aboutToQuit(false),
378+ _notifiedPacketVersionMismatchThisDomain(false),
379+ _maxOctreePPS(maxOctreePacketsPerSecond.get()),
380+ _lastFaceTrackerUpdate(0)
372381 {
373382 thread()->setObjectName("Main Thread");
374−
383+
375384 setInstance(this);
376−
385+
386+ auto controllerScriptingInterface = DependencyManager::get<controller::ScriptingInterface>().data();
387+ _controllerScriptingInterface = dynamic_cast<ControllerScriptingInterface*>(controllerScriptingInterface);
377388 // to work around the Qt constant wireless scanning, set the env for polling interval very high
378389 const QByteArray EXTREME_BEARER_POLL_TIMEOUT = QString::number(INT_MAX).toLocal8Bit();
379390 qputenv("QT_BEARER_POLL_TIMEOUT", EXTREME_BEARER_POLL_TIMEOUT);
380−
391+
381392 _entityClipboard->createRootElement();
382393
383394 _pluginContainer = new PluginContainerProxy();
@@ -440,7 +451,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 56 unmodified lines
440451 audioIO->moveToThread(audioThread);
441452
442453 auto& audioScriptingInterface = AudioScriptingInterface::getInstance();
443−
454+
444455 connect(audioThread, &QThread::started, audioIO.data(), &AudioClient::start);
445456 connect(audioIO.data(), &AudioClient::destroyed, audioThread, &QThread::quit);
446457 connect(audioThread, &QThread::finished, audioThread, &QThread::deleteLater);
@@ -479,7 +490,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 32 unmodified lines
479490 connect(&domainHandler, SIGNAL(disconnectedFromDomain()), SLOT(clearDomainOctreeDetails()));
480491 connect(&domainHandler, &DomainHandler::settingsReceived, this, &Application::domainSettingsReceived);
481492 connect(&domainHandler, &DomainHandler::hostnameChanged,
482− DependencyManager::get<AddressManager>().data(), &AddressManager::storeCurrentAddress);
493+ DependencyManager::get<AddressManager>().data(), &AddressManager::storeCurrentAddress);
483494
484495 // update our location every 5 seconds in the metaverse server, assuming that we are authenticated with one
485496 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
490501
491502 // if we get a domain change, immediately attempt update location in metaverse server
492503 connect(&nodeList->getDomainHandler(), &DomainHandler::connectedToDomain,
493− discoverabilityManager.data(), &DiscoverabilityManager::updateLocation);
504+ discoverabilityManager.data(), &DiscoverabilityManager::updateLocation);
494505
495506 connect(nodeList.data(), &NodeList::nodeAdded, this, &Application::nodeAdded);
496507 connect(nodeList.data(), &NodeList::nodeKilled, this, &Application::nodeKilled);
@@ -529,14 +540,14 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 32 unmodified lines
529540 connect(addressManager.data(), &AddressManager::hostChanged, this, &Application::updateWindowTitle);
530541 connect(this, &QCoreApplication::aboutToQuit, addressManager.data(), &AddressManager::storeCurrentAddress);
531542
532− #ifdef _WIN32
543+#ifdef _WIN32
533544 WSADATA WsaData;
534− int wsaresult = WSAStartup(MAKEWORD(2,2), &WsaData);
535− #endif
545+ int wsaresult = WSAStartup(MAKEWORD(2, 2), &WsaData);
546+#endif
536547
537548 // tell the NodeList instance who to tell the domain server we care about
538549 nodeList->addSetOfNodeTypesToNodeInterestSet(NodeSet() << NodeType::AudioMixer << NodeType::AvatarMixer
539− << NodeType::EntityServer << NodeType::AssetServer);
550+ << NodeType::EntityServer << NodeType::AssetServer);
540551
541552 // connect to the packet sent signal of the _entityEditSender
542553 connect(&_entityEditSender, &EntityEditPacketSender::packetSent, this, &Application::packetSent);
@@ -609,29 +620,38 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 66 unmodified lines
609620 // hook up bandwidth estimator
610621 QSharedPointer<BandwidthRecorder> bandwidthRecorder = DependencyManager::get<BandwidthRecorder>();
611622 connect(nodeList.data(), &LimitedNodeList::dataSent,
612− bandwidthRecorder.data(), &BandwidthRecorder::updateOutboundData);
623+ bandwidthRecorder.data(), &BandwidthRecorder::updateOutboundData);
613624 connect(&nodeList->getPacketReceiver(), &PacketReceiver::dataReceived,
614− bandwidthRecorder.data(), &BandwidthRecorder::updateInboundData);
625+ bandwidthRecorder.data(), &BandwidthRecorder::updateInboundData);
615626
616627 connect(&getMyAvatar()->getSkeletonModel(), &SkeletonModel::skeletonLoaded,
617− this, &Application::checkSkeleton, Qt::QueuedConnection);
628+ this, &Application::checkSkeleton, Qt::QueuedConnection);
618629
619630 // Setup the userInputMapper with the actions
620631 auto userInputMapper = DependencyManager::get<UserInputMapper>();
621− connect(userInputMapper.data(), &UserInputMapper::actionEvent, &_controllerScriptingInterface, &AbstractControllerScriptingInterface::actionEvent);
622632 connect(userInputMapper.data(), &UserInputMapper::actionEvent, [this](int action, float state) {
623− if (state) {
624− switch (action) {
625− case UserInputMapper::Action::TOGGLE_MUTE:
626− DependencyManager::get<AudioClient>()->toggleMute();
627− break;
628− }
633+ if (state && action == toInt(controller::Action::TOGGLE_MUTE)) {
634+ DependencyManager::get<AudioClient>()->toggleMute();
629635 }
630636 });
631637
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+
632650 // Setup the keyboardMouseDevice and the user input mapper with the default bindings
633− _keyboardMouseDevice->registerToUserInputMapper(*userInputMapper);
634− _keyboardMouseDevice->assignDefaultInputMapping(*userInputMapper);
651+ userInputMapper->registerDevice(_keyboardMouseDevice);
652+
653+
654+ userInputMapper->loadDefaultMapping(userInputMapper->getStandardDeviceID());
635655
636656 // check first run...
637657 if (_firstRun.get()) {
@@ -704,8 +724,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 66 unmodified lines
704724 // Now that menu is initalized we can sync myAvatar with it's state.
705725 getMyAvatar()->updateMotionBehaviorFromMenu();
706726
727+#if 0
707728 // the 3Dconnexion device wants to be initiliazed after a window is displayed.
708729 ConnexionClient::getInstance().init();
730+#endif
709731
710732 auto& packetReceiver = nodeList->getPacketReceiver();
711733 packetReceiver.registerListener(PacketType::DomainConnectionDenied, this, "handleDomainConnectionDeniedPacket");
@@ -792,6 +814,9 @@ void Application::cleanupBeforeQuit() { · 80 unmodified lines
792814
793815 AnimDebugDraw::getInstance().shutdown();
794816
817+ // FIXME: once we move to shared pointer for the INputDevice we shoud remove this naked delete:
818+ _applicationStateDevice.reset();
819+
795820 if (_keyboardFocusHighlightID > 0) {
796821 getOverlays().deleteOverlay(_keyboardFocusHighlightID);
797822 _keyboardFocusHighlightID = -1;
@@ -902,7 +927,10 @@ Application::~Application() { · 104 unmodified lines
902927
903928 Leapmotion::destroy();
904929 RealSense::destroy();
930+
931+#if 0
905932 ConnexionClient::getInstance().destroy();
933+#endif
906934
907935 qInstallMessageHandler(NULL); // NOTE: Do this as late as possible so we continue to get our log messages
908936 }
@@ -975,6 +1003,8 @@ void Application::initializeUi() { · 66 unmodified lines
9751003 offscreenUi->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/"));
9761004 offscreenUi->load("Root.qml");
9771005 offscreenUi->load("RootMenu.qml");
1006+ auto scriptingInterface = DependencyManager::get<controller::ScriptingInterface>();
1007+ offscreenUi->getRootContext()->setContextProperty("Controller", scriptingInterface.data());
9781008 _glWidget->installEventFilter(offscreenUi.data());
9791009 VrMenu::load();
9801010 VrMenu::executeQueuedLambdas();
@@ -1003,7 +1033,10 @@ void Application::initializeUi() { · 22 unmodified lines
10031033 foreach(auto inputPlugin, PluginManager::getInstance()->getInputPlugins()) {
10041034 QString name = inputPlugin->getName();
10051035 if (name == KeyboardMouseDevice::NAME) {
1006− _keyboardMouseDevice = static_cast<KeyboardMouseDevice*>(inputPlugin.data()); // TODO: this seems super hacky
1036+ auto kbm = static_cast<KeyboardMouseDevice*>(inputPlugin.data());
1037+ // FIXME incredibly evil.... _keyboardMouseDevice is now owned by
1038+ // both a QSharedPointer and a std::shared_ptr
1039+ _keyboardMouseDevice = std::shared_ptr<KeyboardMouseDevice>(kbm);
10071040 }
10081041 }
10091042 updateInputModes();
@@ -1456,7 +1489,7 @@ bool Application::event(QEvent* event) { · 446 unmodified lines
14561489 }
14571490
14581491 if (HFActionEvent::types().contains(event->type())) {
1459− _controllerScriptingInterface.handleMetaEvent(static_cast<HFMetaEvent*>(event));
1492+ _controllerScriptingInterface->handleMetaEvent(static_cast<HFMetaEvent*>(event));
14601493 }
14611494
14621495 return QApplication::event(event);
@@ -1470,7 +1503,7 @@ bool Application::eventFilter(QObject* object, QEvent* event) { · 7 unmodified lines
14701503 }
14711504
14721505 // 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))) {
14741507 event->accept();
14751508 return true;
14761509 }
@@ -1485,10 +1518,10 @@ void Application::keyPressEvent(QKeyEvent* event) { · 8 unmodified lines
14851518 _altPressed = event->key() == Qt::Key_Alt;
14861519 _keysPressed.insert(event->key());
14871520
1488− _controllerScriptingInterface.emitKeyPressEvent(event); // send events to any registered scripts
1521+ _controllerScriptingInterface->emitKeyPressEvent(event); // send events to any registered scripts
14891522
14901523 // 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)) {
14921525 return;
14931526 }
14941527
@@ -1518,6 +1551,13 @@ void Application::keyPressEvent(QKeyEvent* event) { · 23 unmodified lines
15181551 if (isMeta) {
15191552 auto offscreenUi = DependencyManager::get<OffscreenUi>();
15201553 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");
15211561 }
15221562 break;
15231563
@@ -1752,10 +1792,10 @@ void Application::keyReleaseEvent(QKeyEvent* event) { · 228 unmodified lines
17521792
17531793 _keysPressed.remove(event->key());
17541794
1755− _controllerScriptingInterface.emitKeyReleaseEvent(event); // send events to any registered scripts
1795+ _controllerScriptingInterface->emitKeyReleaseEvent(event); // send events to any registered scripts
17561796
17571797 // 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)) {
17591799 return;
17601800 }
17611801
@@ -1796,7 +1836,9 @@ void Application::focusOutEvent(QFocusEvent* event) { · 34 unmodified lines
17961836 inputPlugin->pluginFocusOutEvent();
17971837 }
17981838 }
1839+#if 0
17991840 ConnexionData::getInstance().focusOutEvent();
1841+#endif
18001842
18011843 // synthesize events for keys currently pressed, since we may not get their release events
18021844 foreach (int key, _keysPressed) {
@@ -1844,10 +1886,10 @@ void Application::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) { · 41 unmodified lines
18441886
18451887
18461888 _entities.mouseMoveEvent(&mappedEvent, deviceID);
1847− _controllerScriptingInterface.emitMouseMoveEvent(&mappedEvent, deviceID); // send events to any registered scripts
1889+ _controllerScriptingInterface->emitMouseMoveEvent(&mappedEvent, deviceID); // send events to any registered scripts
18481890
18491891 // if one of our scripts have asked to capture this event, then stop processing it
1850− if (_controllerScriptingInterface.isMouseCaptured()) {
1892+ if (_controllerScriptingInterface->isMouseCaptured()) {
18511893 return;
18521894 }
18531895
@@ -1872,10 +1914,10 @@ void Application::mousePressEvent(QMouseEvent* event, unsigned int deviceID) { · 18 unmodified lines
18721914 _entities.mousePressEvent(&mappedEvent, deviceID);
18731915 }
18741916
1875− _controllerScriptingInterface.emitMousePressEvent(&mappedEvent); // send events to any registered scripts
1917+ _controllerScriptingInterface->emitMousePressEvent(&mappedEvent); // send events to any registered scripts
18761918
18771919 // if one of our scripts have asked to capture this event, then stop processing it
1878− if (_controllerScriptingInterface.isMouseCaptured()) {
1920+ if (_controllerScriptingInterface->isMouseCaptured()) {
18791921 return;
18801922 }
18811923
@@ -1897,11 +1939,11 @@ void Application::mousePressEvent(QMouseEvent* event, unsigned int deviceID) { · 15 unmodified lines
18971939
18981940 void Application::mouseDoublePressEvent(QMouseEvent* event, unsigned int deviceID) {
18991941 // if one of our scripts have asked to capture this event, then stop processing it
1900− if (_controllerScriptingInterface.isMouseCaptured()) {
1942+ if (_controllerScriptingInterface->isMouseCaptured()) {
19011943 return;
19021944 }
19031945
1904− _controllerScriptingInterface.emitMouseDoublePressEvent(event);
1946+ _controllerScriptingInterface->emitMouseDoublePressEvent(event);
19051947 }
19061948
19071949 void Application::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) {
@@ -1917,10 +1959,10 @@ void Application::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) { · 9 unmodified lines
19171959 _entities.mouseReleaseEvent(&mappedEvent, deviceID);
19181960 }
19191961
1920− _controllerScriptingInterface.emitMouseReleaseEvent(&mappedEvent); // send events to any registered scripts
1962+ _controllerScriptingInterface->emitMouseReleaseEvent(&mappedEvent); // send events to any registered scripts
19211963
19221964 // if one of our scripts have asked to capture this event, then stop processing it
1923− if (_controllerScriptingInterface.isMouseCaptured()) {
1965+ if (_controllerScriptingInterface->isMouseCaptured()) {
19241966 return;
19251967 }
19261968
@@ -1943,12 +1985,12 @@ void Application::touchUpdateEvent(QTouchEvent* event) { · 16 unmodified lines
19431985
19441986 if (event->type() == QEvent::TouchUpdate) {
19451987 TouchEvent thisEvent(*event, _lastTouchEvent);
1946− _controllerScriptingInterface.emitTouchUpdateEvent(thisEvent); // send events to any registered scripts
1988+ _controllerScriptingInterface->emitTouchUpdateEvent(thisEvent); // send events to any registered scripts
19471989 _lastTouchEvent = thisEvent;
19481990 }
19491991
19501992 // if one of our scripts have asked to capture this event, then stop processing it
1951− if (_controllerScriptingInterface.isTouchCaptured()) {
1993+ if (_controllerScriptingInterface->isTouchCaptured()) {
19521994 return;
19531995 }
19541996
@@ -1960,13 +2002,13 @@ void Application::touchUpdateEvent(QTouchEvent* event) { · 5 unmodified lines
19602002 void Application::touchBeginEvent(QTouchEvent* event) {
19612003 _altPressed = false;
19622004 TouchEvent thisEvent(*event); // on touch begin, we don't compare to last event
1963− _controllerScriptingInterface.emitTouchBeginEvent(thisEvent); // send events to any registered scripts
2005+ _controllerScriptingInterface->emitTouchBeginEvent(thisEvent); // send events to any registered scripts
19642006
19652007 _lastTouchEvent = thisEvent; // and we reset our last event to this event before we call our update
19662008 touchUpdateEvent(event);
19672009
19682010 // if one of our scripts have asked to capture this event, then stop processing it
1969− if (_controllerScriptingInterface.isTouchCaptured()) {
2011+ if (_controllerScriptingInterface->isTouchCaptured()) {
19702012 return;
19712013 }
19722014
@@ -1979,11 +2021,11 @@ void Application::touchBeginEvent(QTouchEvent* event) { · 6 unmodified lines
19792021 void Application::touchEndEvent(QTouchEvent* event) {
19802022 _altPressed = false;
19812023 TouchEvent thisEvent(*event, _lastTouchEvent);
1982− _controllerScriptingInterface.emitTouchEndEvent(thisEvent); // send events to any registered scripts
2024+ _controllerScriptingInterface->emitTouchEndEvent(thisEvent); // send events to any registered scripts
19832025 _lastTouchEvent = thisEvent;
19842026
19852027 // if one of our scripts have asked to capture this event, then stop processing it
1986− if (_controllerScriptingInterface.isTouchCaptured()) {
2028+ if (_controllerScriptingInterface->isTouchCaptured()) {
19872029 return;
19882030 }
19892031
@@ -1996,10 +2038,10 @@ void Application::touchEndEvent(QTouchEvent* event) { · 6 unmodified lines
19962038
19972039 void Application::wheelEvent(QWheelEvent* event) {
19982040 _altPressed = false;
1999− _controllerScriptingInterface.emitWheelEvent(event); // send events to any registered scripts
2041+ _controllerScriptingInterface->emitWheelEvent(event); // send events to any registered scripts
20002042
20012043 // if one of our scripts have asked to capture this event, then stop processing it
2002− if (_controllerScriptingInterface.isWheelCaptured()) {
2044+ if (_controllerScriptingInterface->isWheelCaptured()) {
20032045 return;
20042046 }
20052047
@@ -2164,7 +2206,7 @@ float Application::getAvatarSimrate() { · 158 unmodified lines
21642206 }
21652207
21662208 void Application::setLowVelocityFilter(bool lowVelocityFilter) {
2167− InputDevice::setLowVelocityFilter(lowVelocityFilter);
2209+ controller::InputDevice::setLowVelocityFilter(lowVelocityFilter);
21682210 }
21692211
21702212 ivec2 Application::getMouse() const {
@@ -2694,13 +2736,9 @@ void Application::update(float deltaTime) { · 523 unmodified lines
26942736 userInputMapper->setSensorToWorldMat(myAvatar->getSensorToWorldMatrix());
26952737 userInputMapper->update(deltaTime);
26962738
2697− // This needs to go after userInputMapper->update() because of the keyboard
26982739 bool jointsCaptured = false;
2699− auto inputPlugins = PluginManager::getInstance()->getInputPlugins();
2700− foreach(auto inputPlugin, inputPlugins) {
2701− QString name = inputPlugin->getName();
2702− QAction* action = Menu::getInstance()->getActionForOption(name);
2703− if (action && action->isChecked()) {
2740+ for (auto inputPlugin : PluginManager::getInstance()->getInputPlugins()) {
2741+ if (inputPlugin->isActive()) {
27042742 inputPlugin->pluginUpdate(deltaTime, jointsCaptured);
27052743 if (inputPlugin->isJointController()) {
27062744 jointsCaptured = true;
@@ -2708,19 +2746,14 @@ void Application::update(float deltaTime) { · 1 unmodified line
27082746 }
27092747 }
27102748
2711− // Dispatch input events
2712− _controllerScriptingInterface.updateInputControllers();
2713−
27142749 // Transfer the user inputs to the driveKeys
2750+ // FIXME can we drop drive keys and just have the avatar read the action states directly?
27152751 myAvatar->clearDriveKeys();
27162752 if (_myCamera.getMode() != CAMERA_MODE_INDEPENDENT) {
2717− if (!_controllerScriptingInterface.areActionsCaptured()) {
2718− myAvatar->setDriveKeys(FWD, userInputMapper->getActionState(UserInputMapper::LONGITUDINAL_FORWARD));
2719− myAvatar->setDriveKeys(BACK, userInputMapper->getActionState(UserInputMapper::LONGITUDINAL_BACKWARD));
2720− myAvatar->setDriveKeys(UP, userInputMapper->getActionState(UserInputMapper::VERTICAL_UP));
2721− myAvatar->setDriveKeys(DOWN, userInputMapper->getActionState(UserInputMapper::VERTICAL_DOWN));
2722− myAvatar->setDriveKeys(LEFT, userInputMapper->getActionState(UserInputMapper::LATERAL_LEFT));
2723− myAvatar->setDriveKeys(RIGHT, userInputMapper->getActionState(UserInputMapper::LATERAL_RIGHT));
2753+ if (!_controllerScriptingInterface->areActionsCaptured()) {
2754+ myAvatar->setDriveKeys(TRANSLATE_Z, -1.0f * userInputMapper->getActionState(controller::Action::TRANSLATE_Z));
2755+ myAvatar->setDriveKeys(TRANSLATE_Y, userInputMapper->getActionState(controller::Action::TRANSLATE_Y));
2756+ myAvatar->setDriveKeys(TRANSLATE_X, userInputMapper->getActionState(controller::Action::TRANSLATE_X));
27242757 if (deltaTime > FLT_EPSILON) {
27252758 // For rotations what we really want are meausures of "angles per second" (in order to prevent
27262759 // 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
27282761 // controllers to provide a delta_per_second value rather than a raw delta.)
27292762 const float EXPECTED_FRAME_RATE = 60.0f;
27302763 float timeFactor = EXPECTED_FRAME_RATE * deltaTime;
2731− myAvatar->setDriveKeys(ROT_UP, userInputMapper->getActionState(UserInputMapper::PITCH_UP) / timeFactor);
2732− myAvatar->setDriveKeys(ROT_DOWN, userInputMapper->getActionState(UserInputMapper::PITCH_DOWN) / timeFactor);
2733− myAvatar->setDriveKeys(ROT_LEFT, userInputMapper->getActionState(UserInputMapper::YAW_LEFT) / timeFactor);
2734− myAvatar->setDriveKeys(ROT_RIGHT, userInputMapper->getActionState(UserInputMapper::YAW_RIGHT) / timeFactor);
2764+ myAvatar->setDriveKeys(PITCH, -1.0f * userInputMapper->getActionState(controller::Action::PITCH) / timeFactor);
2765+ myAvatar->setDriveKeys(YAW, -1.0f * userInputMapper->getActionState(controller::Action::YAW) / timeFactor);
2766+ myAvatar->setDriveKeys(STEP_YAW, -1.0f * userInputMapper->getActionState(controller::Action::STEP_YAW) / timeFactor);
27352767 }
27362768 }
2737− myAvatar->setDriveKeys(BOOM_IN, userInputMapper->getActionState(UserInputMapper::BOOM_IN));
2738− myAvatar->setDriveKeys(BOOM_OUT, userInputMapper->getActionState(UserInputMapper::BOOM_OUT));
2769+ myAvatar->setDriveKeys(ZOOM, userInputMapper->getActionState(controller::Action::TRANSLATE_CAMERA_Z));
27392770 }
2740− UserInputMapper::PoseValue leftHand = userInputMapper->getPoseState(UserInputMapper::LEFT_HAND);
2741− UserInputMapper::PoseValue rightHand = userInputMapper->getPoseState(UserInputMapper::RIGHT_HAND);
2771+
2772+ controller::Pose leftHand = userInputMapper->getPoseState(controller::Action::LEFT_HAND);
2773+ controller::Pose rightHand = userInputMapper->getPoseState(controller::Action::RIGHT_HAND);
27422774 Hand* hand = DependencyManager::get<AvatarManager>()->getMyAvatar()->getHand();
2743− setPalmData(hand, leftHand, deltaTime, LEFT_HAND_INDEX, userInputMapper->getActionState(UserInputMapper::LEFT_HAND_CLICK));
2744− setPalmData(hand, rightHand, deltaTime, RIGHT_HAND_INDEX, userInputMapper->getActionState(UserInputMapper::RIGHT_HAND_CLICK));
2775+ setPalmData(hand, leftHand, deltaTime, HandData::LeftHand, userInputMapper->getActionState(controller::Action::LEFT_HAND_CLICK));
2776+ setPalmData(hand, rightHand, deltaTime, HandData::RightHand, userInputMapper->getActionState(controller::Action::RIGHT_HAND_CLICK));
27452777 if (Menu::getInstance()->isOptionChecked(MenuOption::EnableHandMouseInput)) {
2746− emulateMouse(hand, userInputMapper->getActionState(UserInputMapper::LEFT_HAND_CLICK),
2747− userInputMapper->getActionState(UserInputMapper::SHIFT), LEFT_HAND_INDEX);
2748− emulateMouse(hand, userInputMapper->getActionState(UserInputMapper::RIGHT_HAND_CLICK),
2749− userInputMapper->getActionState(UserInputMapper::SHIFT), RIGHT_HAND_INDEX);
2778+ emulateMouse(hand, userInputMapper->getActionState(controller::Action::LEFT_HAND_CLICK),
2779+ userInputMapper->getActionState(controller::Action::SHIFT), HandData::LeftHand);
2780+ emulateMouse(hand, userInputMapper->getActionState(controller::Action::RIGHT_HAND_CLICK),
2781+ userInputMapper->getActionState(controller::Action::SHIFT), HandData::RightHand);
27502782 }
27512783
27522784 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
48074839 return mat4();
48084840 }
48094841
4810−void Application::setPalmData(Hand* hand, UserInputMapper::PoseValue pose, float deltaTime, int index, float triggerValue) {
4811− PalmData* palm;
4812− bool foundHand = false;
4813− for (size_t j = 0; j < hand->getNumPalms(); j++) {
4814− if (hand->getPalms()[j].getSixenseID() == index) {
4815− palm = &(hand->getPalms()[j]);
4816− foundHand = true;
4817− break;
4818− }
4819− }
4820− if (!foundHand) {
4821− PalmData newPalm(hand);
4822− hand->getPalms().push_back(newPalm);
4823− palm = &(hand->getPalms()[hand->getNumPalms() - 1]);
4824− palm->setSixenseID(index);
4825− }
4826−
4827− palm->setActive(pose.isValid());
4842+void Application::setPalmData(Hand* hand, const controller::Pose& pose, float deltaTime, HandData::Hand whichHand, float triggerValue) {
48284843
4829− // transform from sensor space, to world space, to avatar model space.
4830− glm::mat4 poseMat = createMatFromQuatAndPos(pose.getRotation(), pose.getTranslation());
4831− glm::mat4 sensorToWorldMat = getMyAvatar()->getSensorToWorldMatrix();
4832− glm::mat4 modelMat = createMatFromQuatAndPos(getMyAvatar()->getOrientation(), getMyAvatar()->getPosition());
4833− glm::mat4 objectPose = glm::inverse(modelMat) * sensorToWorldMat * poseMat;
4844+ // NOTE: the Hand::modifyPalm() will allow the lambda to modify the palm data while ensuring some other user isn't
4845+ // reading or writing to the Palms. This is definitely not the best way of handling this, and I'd like to see more
4846+ // of this palm manipulation in the Hand class itself. But unfortunately the Hand and Palm don't knbow about
4847+ // controller::Pose. More work is needed to clean this up.
4848+ hand->modifyPalm(whichHand, [&](PalmData& palm) {
4849+ auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
4850+ palm.setActive(pose.isValid());
48344851
4835− glm::vec3 position = extractTranslation(objectPose);
4836− glm::quat rotation = glm::quat_cast(objectPose);
4852+ // transform from sensor space, to world space, to avatar model space.
4853+ glm::mat4 poseMat = createMatFromQuatAndPos(pose.getRotation(), pose.getTranslation());
4854+ glm::mat4 sensorToWorldMat = myAvatar->getSensorToWorldMatrix();
4855+ glm::mat4 modelMat = createMatFromQuatAndPos(myAvatar->getOrientation(), myAvatar->getPosition());
4856+ glm::mat4 objectPose = glm::inverse(modelMat) * sensorToWorldMat * poseMat;
48374857
4838− // Compute current velocity from position change
4839− glm::vec3 rawVelocity;
4840− if (deltaTime > 0.0f) {
4841− rawVelocity = (position - palm->getRawPosition()) / deltaTime;
4842− } else {
4843− rawVelocity = glm::vec3(0.0f);
4844− }
4845− palm->setRawVelocity(rawVelocity); // meters/sec
4858+ glm::vec3 position = extractTranslation(objectPose);
4859+ glm::quat rotation = glm::quat_cast(objectPose);
4860+
4861+ // Compute current velocity from position change
4862+ glm::vec3 rawVelocity;
4863+ if (deltaTime > 0.0f) {
4864+ rawVelocity = (position - palm.getRawPosition()) / deltaTime;
4865+ } else {
4866+ rawVelocity = glm::vec3(0.0f);
4867+ }
4868+ palm.setRawVelocity(rawVelocity); // meters/sec
48464869
4847− // Angular Velocity of Palm
4848− glm::quat deltaRotation = rotation * glm::inverse(palm->getRawRotation());
4849− glm::vec3 angularVelocity(0.0f);
4850− float rotationAngle = glm::angle(deltaRotation);
4851− if ((rotationAngle > EPSILON) && (deltaTime > 0.0f)) {
4852− angularVelocity = glm::normalize(glm::axis(deltaRotation));
4853− angularVelocity *= (rotationAngle / deltaTime);
4854− palm->setRawAngularVelocity(angularVelocity);
4855− } else {
4856− palm->setRawAngularVelocity(glm::vec3(0.0f));
4857− }
4858−
4859− if (InputDevice::getLowVelocityFilter()) {
4860− // Use a velocity sensitive filter to damp small motions and preserve large ones with
4861− // no latency.
4862− float velocityFilter = glm::clamp(1.0f - glm::length(rawVelocity), 0.0f, 1.0f);
4863− position = palm->getRawPosition() * velocityFilter + position * (1.0f - velocityFilter);
4864− rotation = safeMix(palm->getRawRotation(), rotation, 1.0f - velocityFilter);
4865− }
4866− palm->setRawPosition(position);
4867− palm->setRawRotation(rotation);
4868−
4869− // Store the one fingertip in the palm structure so we can track velocity
4870− const float FINGER_LENGTH = 0.3f; // meters
4871− const glm::vec3 FINGER_VECTOR(0.0f, FINGER_LENGTH, 0.0f);
4872− const glm::vec3 newTipPosition = position + rotation * FINGER_VECTOR;
4873− glm::vec3 oldTipPosition = palm->getTipRawPosition();
4874− if (deltaTime > 0.0f) {
4875− palm->setTipVelocity((newTipPosition - oldTipPosition) / deltaTime);
4876− } else {
4877− palm->setTipVelocity(glm::vec3(0.0f));
4878− }
4879− palm->setTipPosition(newTipPosition);
4880− palm->setTrigger(triggerValue);
4870+ // Angular Velocity of Palm
4871+ glm::quat deltaRotation = rotation * glm::inverse(palm.getRawRotation());
4872+ glm::vec3 angularVelocity(0.0f);
4873+ float rotationAngle = glm::angle(deltaRotation);
4874+ if ((rotationAngle > EPSILON) && (deltaTime > 0.0f)) {
4875+ angularVelocity = glm::normalize(glm::axis(deltaRotation));
4876+ angularVelocity *= (rotationAngle / deltaTime);
4877+ palm.setRawAngularVelocity(angularVelocity);
4878+ } else {
4879+ palm.setRawAngularVelocity(glm::vec3(0.0f));
4880+ }
4881+
4882+ if (controller::InputDevice::getLowVelocityFilter()) {
4883+ // Use a velocity sensitive filter to damp small motions and preserve large ones with
4884+ // no latency.
4885+ float velocityFilter = glm::clamp(1.0f - glm::length(rawVelocity), 0.0f, 1.0f);
4886+ position = palm.getRawPosition() * velocityFilter + position * (1.0f - velocityFilter);
4887+ rotation = safeMix(palm.getRawRotation(), rotation, 1.0f - velocityFilter);
4888+ }
4889+ palm.setRawPosition(position);
4890+ palm.setRawRotation(rotation);
4891+
4892+ // Store the one fingertip in the palm structure so we can track velocity
4893+ const float FINGER_LENGTH = 0.3f; // meters
4894+ const glm::vec3 FINGER_VECTOR(0.0f, FINGER_LENGTH, 0.0f);
4895+ const glm::vec3 newTipPosition = position + rotation * FINGER_VECTOR;
4896+ glm::vec3 oldTipPosition = palm.getTipRawPosition();
4897+ if (deltaTime > 0.0f) {
4898+ palm.setTipVelocity((newTipPosition - oldTipPosition) / deltaTime);
4899+ } else {
4900+ palm.setTipVelocity(glm::vec3(0.0f));
4901+ }
4902+ palm.setTipPosition(newTipPosition);
4903+ palm.setTrigger(triggerValue); // FIXME - we want to get rid of this idea of PalmData having a trigger
4904+ });
48814905 }
48824906
4883−void Application::emulateMouse(Hand* hand, float click, float shift, int index) {
4907+void Application::emulateMouse(Hand* hand, float click, float shift, HandData::Hand whichHand) {
4908+ auto palms = hand->getCopyOfPalms();
4909+
48844910 // Locate the palm, if it exists and is active
48854911 PalmData* palm;
48864912 bool foundHand = false;
4887− for (size_t j = 0; j < hand->getNumPalms(); j++) {
4888− if (hand->getPalms()[j].getSixenseID() == index) {
4889− palm = &(hand->getPalms()[j]);
4913+ for (size_t j = 0; j < palms.size(); j++) {
4914+ if (palms[j].whichHand() == whichHand) {
4915+ palm = &(palms[j]);
48904916 foundHand = true;
48914917 break;
48924918 }
@@ -4898,12 +4924,14 @@ void Application::emulateMouse(Hand* hand, float click, float shift, int index) · 5 unmodified lines
48984924 // Process the mouse events
48994925 QPoint pos;
49004926
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
49024931
49034932 if (isHMDMode()) {
49044933 pos = getApplicationCompositor().getPalmClickLocation(palm);
4905− }
4906− else {
4934+ } else {
49074935 // Get directon relative to avatar orientation
49084936 glm::vec3 direction = glm::inverse(getMyAvatar()->getOrientation()) * palm->getFingerDirection();
49094937
@@ -4912,7 +4940,7 @@ void Application::emulateMouse(Hand* hand, float click, float shift, int index) · 2 unmodified lines
49124940 float yAngle = 0.5f - ((atan2f(direction.z, direction.y) + (float)M_PI_2));
49134941 auto canvasSize = getCanvasSize();
49144942 // 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();
49164944
49174945 pos.setX(canvasSize.x / 2.0f + cursorRange * xAngle);
49184946 pos.setY(canvasSize.y / 2.0f + cursorRange * yAngle);
interface/src/Application.h+26−17
@@ -14,13 +14,15 @@ · 13 unmodified lines
1414
1515 #include <functional>
1616
17−#include <QApplication>
18−#include <QHash>
19−#include <QImage>
20−#include <QPointer>
21−#include <QSet>
22−#include <QStringList>
23−#include <QUndoStack>
17+#include <QtCore/QHash>
18+#include <QtCore/QPointer>
19+#include <QtCore/QSet>
20+#include <QtCore/QStringList>
21+
22+#include <QtGui/QImage>
23+
24+#include <QtWidgets/QApplication>
25+#include <QtWidgets/QUndoStack>
2426
2527 #include <AbstractScriptingServicesInterface.h>
2628 #include <AbstractViewStateInterface.h>
@@ -69,6 +71,10 @@ class FaceTracker; · 42 unmodified lines
6971 class MainWindow;
7072 class AssetUpload;
7173
74+namespace controller {
75+ class StateController;
76+}
77+
7278 #ifdef Q_OS_WIN
7379 static const UINT UWM_IDENTIFY_INSTANCES =
7480 RegisterWindowMessage("UWM_IDENTIFY_INSTANCES_{8AB82783-B74A-4258-955B-8188C22AA0D6}_" + qgetenv("USERNAME"));
@@ -161,7 +167,7 @@ public: · 86 unmodified lines
161167
162168 ToolWindow* getToolWindow() { return _toolWindow ; }
163169
164− virtual AbstractControllerScriptingInterface* getControllerScriptingInterface() { return &_controllerScriptingInterface; }
170+ virtual controller::ScriptingInterface* getControllerScriptingInterface() { return _controllerScriptingInterface; }
165171 virtual void registerScriptEngineWithApplicationServices(ScriptEngine* scriptEngine);
166172
167173 QImage renderAvatarBillboard(RenderArgs* renderArgs);
@@ -350,8 +356,8 @@ private: · 182 unmodified lines
350356
351357 void update(float deltaTime);
352358
353− void setPalmData(Hand* hand, UserInputMapper::PoseValue pose, float deltaTime, int index, float triggerValue);
354− void emulateMouse(Hand* hand, float click, float shift, int index);
359+ void setPalmData(Hand* hand, const controller::Pose& pose, float deltaTime, HandData::Hand whichHand, float triggerValue);
360+ void emulateMouse(Hand* hand, float click, float shift, HandData::Hand whichHand);
355361
356362 // Various helper functions called during update()
357363 void updateLOD();
@@ -440,7 +446,8 @@ private: · 82 unmodified lines
440446
441447 OctreeQuery _octreeQuery; // NodeData derived class for querying octee cells from octree servers
442448
443− KeyboardMouseDevice* _keyboardMouseDevice{ nullptr }; // Default input device, the good old keyboard mouse and maybe touchpad
449+ std::shared_ptr<controller::StateController> _applicationStateDevice; // Default ApplicationDevice reflecting the state of different properties of the session
450+ std::shared_ptr<KeyboardMouseDevice> _keyboardMouseDevice; // Default input device, the good old keyboard mouse and maybe touchpad
444451 AvatarUpdate* _avatarUpdate {nullptr};
445452 SimpleMovingAverage _avatarSimsPerSecond {10};
446453 int _avatarSimsPerSecondReport {0};
@@ -474,8 +481,7 @@ private: · 27 unmodified lines
474481
475482 NodeToJurisdictionMap _entityServerJurisdictions;
476483 NodeToOctreeSceneStats _octreeServerSceneStats;
477−
478− ControllerScriptingInterface _controllerScriptingInterface;
484+ ControllerScriptingInterface* _controllerScriptingInterface{ nullptr };
479485 QPointer<LogDialog> _logDialog;
480486 QPointer<SnapshotShareDialog> _snapshotShareDialog;
481487
@@ -521,10 +527,13 @@ private: · 39 unmodified lines
521527 ApplicationCompositor _compositor;
522528 OverlayConductor _overlayConductor;
523529
524− int _oldHandMouseX[2];
525− int _oldHandMouseY[2];
526− bool _oldHandLeftClick[2];
527− bool _oldHandRightClick[2];
530+
531+ // FIXME - Hand Controller to mouse emulation helpers. This is crufty and should be moved
532+ // into the input plugins or something.
533+ int _oldHandMouseX[(int)HandData::NUMBER_OF_HANDS];
534+ int _oldHandMouseY[(int)HandData::NUMBER_OF_HANDS];
535+ bool _oldHandLeftClick[(int)HandData::NUMBER_OF_HANDS];
536+ bool _oldHandRightClick[(int)HandData::NUMBER_OF_HANDS];
528537
529538 DialogsManagerScriptingInterface* _dialogsManagerScriptingInterface = new DialogsManagerScriptingInterface();
530539
interface/src/Menu.cpp+2−0
@@ -464,11 +464,13 @@ Menu::Menu() { · 463 unmodified lines
464464 addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::MeshVisible, 0, true,
465465 avatar, SLOT(setEnableMeshVisible(bool)));
466466 addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::DisableEyelidAdjustment, 0, false);
467+#if 0
467468 addCheckableActionToQMenuAndActionHash(avatarDebugMenu,
468469 MenuOption::Connexion,
469470 0, false,
470471 &ConnexionClient::getInstance(),
471472 SLOT(toggleConnexion(bool)));
473+#endif
472474 addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ComfortMode, 0, true);
473475
474476 MenuWrapper* handOptionsMenu = developerMenu->addMenu("Hands");
interface/src/avatar/Avatar.cpp+0−32
@@ -1177,22 +1177,6 @@ glm::vec3 Avatar::getLeftPalmPosition() { · 1176 unmodified lines
11771177 return leftHandPosition;
11781178 }
11791179
1180−glm::vec3 Avatar::getLeftPalmVelocity() {
1181− const PalmData* palm = getHand()->getPalm(LEFT_HAND_INDEX);
1182− if (palm != NULL) {
1183− return palm->getVelocity();
1184− }
1185− return glm::vec3(0.0f);
1186−}
1187−
1188−glm::vec3 Avatar::getLeftPalmAngularVelocity() {
1189− const PalmData* palm = getHand()->getPalm(LEFT_HAND_INDEX);
1190− if (palm != NULL) {
1191− return palm->getRawAngularVelocity();
1192− }
1193− return glm::vec3(0.0f);
1194−}
1195−
11961180 glm::quat Avatar::getLeftPalmRotation() {
11971181 glm::quat leftRotation;
11981182 getSkeletonModel().getJointRotationInWorldFrame(getSkeletonModel().getLeftHandJointIndex(), leftRotation);
@@ -1208,22 +1192,6 @@ glm::vec3 Avatar::getRightPalmPosition() { · 9 unmodified lines
12081192 return rightHandPosition;
12091193 }
12101194
1211−glm::vec3 Avatar::getRightPalmVelocity() {
1212− const PalmData* palm = getHand()->getPalm(RIGHT_HAND_INDEX);
1213− if (palm != NULL) {
1214− return palm->getVelocity();
1215− }
1216− return glm::vec3(0.0f);
1217−}
1218−
1219−glm::vec3 Avatar::getRightPalmAngularVelocity() {
1220− const PalmData* palm = getHand()->getPalm(RIGHT_HAND_INDEX);
1221− if (palm != NULL) {
1222− return palm->getRawAngularVelocity();
1223− }
1224− return glm::vec3(0.0f);
1225−}
1226−
12271195 glm::quat Avatar::getRightPalmRotation() {
12281196 glm::quat rightRotation;
12291197 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
4343 extern const float CHAT_MESSAGE_SCALE;
4444 extern const float CHAT_MESSAGE_HEIGHT;
4545
46−enum DriveKeys {
47− FWD = 0,
48− BACK,
49− LEFT,
50− RIGHT,
51− UP,
52− DOWN,
53− ROT_LEFT,
54− ROT_RIGHT,
55− ROT_UP,
56− ROT_DOWN,
57− BOOM_IN,
58− BOOM_OUT,
59− MAX_DRIVE_KEYS
60−};
6146
6247 enum ScreenTintLayer {
6348 SCREEN_TINT_BEFORE_LANDSCAPE = 0,
@@ -175,13 +160,11 @@ public: · 111 unmodified lines
175160 AvatarMotionState* getMotionState() { return _motionState; }
176161
177162 public slots:
163+
164+ // FIXME - these should be migrated to use Pose data instead
178165 glm::vec3 getLeftPalmPosition();
179− glm::vec3 getLeftPalmVelocity();
180− glm::vec3 getLeftPalmAngularVelocity();
181166 glm::quat getLeftPalmRotation();
182167 glm::vec3 getRightPalmPosition();
183− glm::vec3 getRightPalmVelocity();
184− glm::vec3 getRightPalmAngularVelocity();
185168 glm::quat getRightPalmRotation();
186169
187170 protected:
interface/src/avatar/AvatarManager.cpp+10−0
@@ -248,6 +248,16 @@ QVector<AvatarManager::LocalLight> AvatarManager::getLocalLights() const { · 247 unmodified lines
248248 return _localLights;
249249 }
250250
251+QVector<QUuid> AvatarManager::getAvatarIdentifiers() {
252+ QReadLocker locker(&_hashLock);
253+ return _avatarHash.keys().toVector();
254+}
255+AvatarData* AvatarManager::getAvatar(QUuid avatarID) {
256+ QReadLocker locker(&_hashLock);
257+ return _avatarHash[avatarID].get(); // Non-obvious: A bogus avatarID answers your own avatar.
258+}
259+
260+
251261 void AvatarManager::getObjectsToDelete(VectorOfMotionStates& result) {
252262 result.clear();
253263 result.swap(_motionStatesToDelete);
interface/src/avatar/AvatarManager.h+4−0
@@ -52,6 +52,10 @@ public: · 51 unmodified lines
5252
5353 Q_INVOKABLE void setLocalLights(const QVector<AvatarManager::LocalLight>& localLights);
5454 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+
5559
5660 void getObjectsToDelete(VectorOfMotionStates& motionStates);
5761 void getObjectsToAdd(VectorOfMotionStates& motionStates);
interface/src/avatar/Hand.cpp+5−11
@@ -31,13 +31,7 @@ Hand::Hand(Avatar* owningAvatar) : · 30 unmodified lines
3131 }
3232
3333 void Hand::simulate(float deltaTime, bool isMine) {
34− if (isMine) {
35− // Iterate hand controllers, take actions as needed
36− for (size_t i = 0; i < getNumPalms(); ++i) {
37− PalmData& palm = getPalms()[i];
38− palm.setLastControllerButtons(palm.getControllerButtons());
39− }
40− }
34+ // nothing to do here
4135 }
4236
4337 void Hand::renderHandTargets(RenderArgs* renderArgs, bool isMine) {
@@ -53,10 +47,11 @@ void Hand::renderHandTargets(RenderArgs* renderArgs, bool isMine) { · 9 unmodified lines
5347 const glm::vec3 grayColor(0.5f);
5448 const float SPHERE_RADIUS = 0.03f * avatarScale;
5549
50+ auto palms = getCopyOfPalms();
51+
5652 gpu::Batch& batch = *renderArgs->_batch;
5753 if (isMine) {
58− for (size_t i = 0; i < getNumPalms(); i++) {
59− PalmData& palm = getPalms()[i];
54+ for (const auto& palm : palms) {
6055 if (!palm.isActive()) {
6156 continue;
6257 }
@@ -82,8 +77,7 @@ void Hand::renderHandTargets(RenderArgs* renderArgs, bool isMine) { · 19 unmodified lines
8277 const float AXIS_LENGTH = 10.0f * SPHERE_RADIUS;
8378
8479 // Draw the coordinate frames of the hand targets
85− for (size_t i = 0; i < getNumPalms(); ++i) {
86− PalmData& palm = getPalms()[i];
80+ for (const auto& palm : palms) {
8781 if (palm.isActive()) {
8882 glm::vec3 root = palm.getPosition();
8983
interface/src/avatar/MyAvatar.cpp+111−86
@@ -53,6 +53,7 @@ · 52 unmodified lines
5353
5454 using namespace std;
5555
56+static quint64 COMFORT_MODE_PULSE_TIMING = USECS_PER_SECOND / 2; // turn once per half second
5657 const glm::vec3 DEFAULT_UP_DIRECTION(0.0f, 1.0f, 0.0f);
5758 const float YAW_SPEED = 150.0f; // degrees/sec
5859 const float PITCH_SPEED = 100.0f; // degrees/sec
@@ -246,8 +247,31 @@ void MyAvatar::simulate(float deltaTime) { · 187 unmodified lines
246247
247248 {
248249 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+
249268 updateOrientation(deltaTime);
250269 updatePosition(deltaTime);
270+
271+ if (_pulseUpdate) {
272+ _lastStepPulse = now;
273+ _pulseUpdate = false;
274+ }
251275 }
252276
253277 {
@@ -531,6 +555,50 @@ void MyAvatar::updateFromTrackers(float deltaTime) { · 277 unmodified lines
531555 }
532556
533557
558+glm::vec3 MyAvatar::getLeftHandPosition() const {
559+ auto palmData = getHandData()->getCopyOfPalmData(HandData::LeftHand);
560+ return palmData.isValid() ? palmData.getPosition() : glm::vec3(0.0f);
561+}
562+
563+glm::vec3 MyAvatar::getRightHandPosition() const {
564+ auto palmData = getHandData()->getCopyOfPalmData(HandData::RightHand);
565+ return palmData.isValid() ? palmData.getPosition() : glm::vec3(0.0f);
566+}
567+
568+glm::vec3 MyAvatar::getLeftHandTipPosition() const {
569+ auto palmData = getHandData()->getCopyOfPalmData(HandData::LeftHand);
570+ return palmData.isValid() ? palmData.getTipPosition() : glm::vec3(0.0f);
571+}
572+
573+glm::vec3 MyAvatar::getRightHandTipPosition() const {
574+ auto palmData = getHandData()->getCopyOfPalmData(HandData::RightHand);
575+ return palmData.isValid() ? palmData.getTipPosition() : glm::vec3(0.0f);
576+}
577+
578+controller::Pose MyAvatar::getLeftHandPose() const {
579+ auto palmData = getHandData()->getCopyOfPalmData(HandData::LeftHand);
580+ return palmData.isValid() ? controller::Pose(palmData.getPosition(), palmData.getRotation(),
581+ palmData.getVelocity(), palmData.getRawAngularVelocityAsQuat()) : controller::Pose();
582+}
583+
584+controller::Pose MyAvatar::getRightHandPose() const {
585+ auto palmData = getHandData()->getCopyOfPalmData(HandData::RightHand);
586+ return palmData.isValid() ? controller::Pose(palmData.getPosition(), palmData.getRotation(),
587+ palmData.getVelocity(), palmData.getRawAngularVelocityAsQuat()) : controller::Pose();
588+}
589+
590+controller::Pose MyAvatar::getLeftHandTipPose() const {
591+ auto palmData = getHandData()->getCopyOfPalmData(HandData::LeftHand);
592+ return palmData.isValid() ? controller::Pose(palmData.getTipPosition(), palmData.getRotation(),
593+ palmData.getTipVelocity(), palmData.getRawAngularVelocityAsQuat()) : controller::Pose();
594+}
595+
596+controller::Pose MyAvatar::getRightHandTipPose() const {
597+ auto palmData = getHandData()->getCopyOfPalmData(HandData::RightHand);
598+ return palmData.isValid() ? controller::Pose(palmData.getTipPosition(), palmData.getRotation(),
599+ palmData.getTipVelocity(), palmData.getRawAngularVelocityAsQuat()) : controller::Pose();
600+}
601+
534602 // virtual
535603 void MyAvatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) {
536604 // 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
15331601
15341602 void MyAvatar::updateOrientation(float deltaTime) {
15351603 // Smoothly rotate body with arrow keys
1536− float targetSpeed = 0.0f;
1537−
1538− // FIXME - this comfort mode code is a total hack, remove it when we have new input mapping
1539− bool isComfortMode = Menu::getInstance()->isOptionChecked(MenuOption::ComfortMode);
1540− bool isHMDMode = qApp->getAvatarUpdater()->isHMDMode();
1541−
1542− if (!isHMDMode || !isComfortMode) {
1543− targetSpeed = (_driveKeys[ROT_LEFT] - _driveKeys[ROT_RIGHT]) * YAW_SPEED;
1544−
1545− if (targetSpeed != 0.0f) {
1546− const float ROTATION_RAMP_TIMESCALE = 0.1f;
1547− float blend = deltaTime / ROTATION_RAMP_TIMESCALE;
1548− if (blend > 1.0f) {
1549− blend = 1.0f;
1550− }
1551− _bodyYawDelta = (1.0f - blend) * _bodyYawDelta + blend * targetSpeed;
1552− } else if (_bodyYawDelta != 0.0f) {
1553− // attenuate body rotation speed
1554− const float ROTATION_DECAY_TIMESCALE = 0.05f;
1555− float attenuation = 1.0f - deltaTime / ROTATION_DECAY_TIMESCALE;
1556− if (attenuation < 0.0f) {
1557− attenuation = 0.0f;
1558− }
1559− _bodyYawDelta *= attenuation;
1560−
1561− float MINIMUM_ROTATION_RATE = 2.0f;
1562− if (fabsf(_bodyYawDelta) < MINIMUM_ROTATION_RATE) {
1563− _bodyYawDelta = 0.0f;
1564− }
1604+ float targetSpeed = _driveKeys[YAW] * YAW_SPEED;
1605+ if (targetSpeed != 0.0f) {
1606+ const float ROTATION_RAMP_TIMESCALE = 0.1f;
1607+ float blend = deltaTime / ROTATION_RAMP_TIMESCALE;
1608+ if (blend > 1.0f) {
1609+ blend = 1.0f;
15651610 }
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;
15661620
1567− // update body orientation by movement inputs
1568− setOrientation(getOrientation() *
1569− glm::quat(glm::radians(glm::vec3(0.0f, _bodyYawDelta * deltaTime, 0.0f))));
1570−
1571− } else {
1572− // Comfort Mode: If you press any of the left/right rotation drive keys or input, you'll
1573− // get an instantaneous 15 degree turn. If you keep holding the key down you'll get another
1574− // snap turn every half second.
1575− _bodyYawDelta = 0.0f;
1576−
1577− static quint64 lastPulse = 0;
1578− quint64 now = usecTimestampNow();
1579− quint64 COMFORT_MODE_PULSE_TIMING = USECS_PER_SECOND / 2; // turn once per half second
1580−
1581− float driveLeft = _driveKeys[ROT_LEFT];
1582− float driveRight= _driveKeys[ROT_RIGHT];
1583−
1584− if ((driveLeft != 0.0f || driveRight != 0.0f) && (now - lastPulse > COMFORT_MODE_PULSE_TIMING)) {
1585− lastPulse = now;
1621+ float MINIMUM_ROTATION_RATE = 2.0f;
1622+ if (fabsf(_bodyYawDelta) < MINIMUM_ROTATION_RATE) {
1623+ _bodyYawDelta = 0.0f;
1624+ }
1625+ }
15861626
1587− const float SNAP_TURN_DELTA = 15.0f; // degrees
1588− float direction = (driveLeft - driveRight) < 0.0f ? -1.0f : 1.0f;
1589− float turnAmount = direction * SNAP_TURN_DELTA;
1627+ float totalBodyYaw = _bodyYawDelta * deltaTime;
15901628
1591− // update body orientation by movement inputs
1592− setOrientation(getOrientation() *
1593− glm::quat(glm::radians(glm::vec3(0.0f, turnAmount, 0.0f))));
15941629
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];
15961636 }
15971637
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);
15991642
16001643 if (qApp->getAvatarUpdater()->isHMDMode()) {
16011644 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
16551698 float motorEfficiency = glm::clamp(deltaTime / timescale, 0.0f, 1.0f);
16561699
16571700 glm::vec3 newLocalVelocity = localVelocity;
1658− float keyboardInput = fabsf(_driveKeys[FWD] - _driveKeys[BACK]) +
1659− (fabsf(_driveKeys[RIGHT] - _driveKeys[LEFT])) +
1660− fabsf(_driveKeys[UP] - _driveKeys[DOWN]);
1701+ float stepControllerInput = fabsf(_driveKeys[STEP_TRANSLATE_Z]) + fabsf(_driveKeys[STEP_TRANSLATE_Z]) + fabsf(_driveKeys[STEP_TRANSLATE_Z]);
1702+ quint64 now = usecTimestampNow();
1703+ // FIXME how do I implement step translation as well?
1704+ if (stepControllerInput && now - _lastStepPulse > COMFORT_MODE_PULSE_TIMING) {
1705+ }
1706+
1707+ float keyboardInput = fabsf(_driveKeys[TRANSLATE_Z]) + fabsf(_driveKeys[TRANSLATE_X]) + fabsf(_driveKeys[TRANSLATE_Y]);
16611708 if (keyboardInput) {
16621709 // Compute keyboard input
1663− glm::vec3 front = (_driveKeys[FWD] - _driveKeys[BACK]) * IDENTITY_FRONT;
1664− glm::vec3 right = (_driveKeys[RIGHT] - _driveKeys[LEFT]) * IDENTITY_RIGHT;
1665− glm::vec3 up = (_driveKeys[UP] - _driveKeys[DOWN]) * IDENTITY_UP;
1710+ glm::vec3 front = (_driveKeys[TRANSLATE_Z]) * IDENTITY_FRONT;
1711+ glm::vec3 right = (_driveKeys[TRANSLATE_X]) * IDENTITY_RIGHT;
1712+ glm::vec3 up = (_driveKeys[TRANSLATE_Y]) * IDENTITY_UP;
16661713
16671714 glm::vec3 direction = front + right + up;
16681715 float directionLength = glm::length(direction);
@@ -1713,7 +1760,7 @@ glm::vec3 MyAvatar::applyKeyboardMotor(float deltaTime, const glm::vec3& localVe · 44 unmodified lines
17131760 }
17141761 }
17151762
1716− float boomChange = _driveKeys[BOOM_OUT] - _driveKeys[BOOM_IN];
1763+ float boomChange = _driveKeys[ZOOM];
17171764 _boomLength += 2.0f * _boomLength * boomChange + boomChange * boomChange;
17181765 _boomLength = glm::clamp<float>(_boomLength, ZOOM_MIN, ZOOM_MAX);
17191766
@@ -1915,28 +1962,6 @@ void MyAvatar::updateMotionBehaviorFromMenu() { · 195 unmodified lines
19151962 _characterController.setEnabled(menu->isOptionChecked(MenuOption::EnableCharacterController));
19161963 }
19171964
1918−//Renders sixense laser pointers for UI selection with controllers
1919−void MyAvatar::renderLaserPointers(gpu::Batch& batch) {
1920− const float PALM_TIP_ROD_RADIUS = 0.002f;
1921−
1922− //If the Oculus is enabled, we will draw a blue cursor ray
1923−
1924− for (size_t i = 0; i < getHand()->getNumPalms(); ++i) {
1925− PalmData& palm = getHand()->getPalms()[i];
1926− if (palm.isActive()) {
1927− glm::vec3 tip = getLaserPointerTipPosition(&palm);
1928− glm::vec3 root = palm.getPosition();
1929−
1930− //Scale the root vector with the avatar scale
1931− scaleVectorRelativeToPosition(root);
1932− Transform transform = Transform();
1933− transform.setTranslation(glm::vec3());
1934− batch.setModelTransform(transform);
1935− Avatar::renderJointConnectingCone(batch, root, tip, PALM_TIP_ROD_RADIUS, PALM_TIP_ROD_RADIUS, glm::vec4(0, 1, 1, 1));
1936− }
1937− }
1938−}
1939−
19401965 //Gets the tip position for the laser pointer
19411966 glm::vec3 MyAvatar::getLaserPointerTipPosition(const PalmData* palm) {
19421967 glm::vec3 direction = glm::normalize(palm->getTipPosition() - palm->getPosition());
@@ -1962,7 +1987,7 @@ void MyAvatar::clearDriveKeys() { · 19 unmodified lines
19621987 }
19631988
19641989 void MyAvatar::relayDriveKeysToCharacterController() {
1965− if (_driveKeys[UP] > 0.0f) {
1990+ if (_driveKeys[TRANSLATE_Y] > 0.0f) {
19661991 _characterController.jump();
19671992 }
19681993 }
interface/src/avatar/MyAvatar.h+53−2
@@ -17,6 +17,8 @@ · 16 unmodified lines
1717 #include <SettingHandle.h>
1818 #include <Rig.h>
1919
20+#include <controllers/Pose.h>
21+
2022 #include "Avatar.h"
2123 #include "AtRestDetector.h"
2224 #include "MyCharacterController.h"
@@ -24,6 +26,20 @@ · 1 unmodified line
2426
2527 class ModelItemID;
2628
29+enum DriveKeys {
30+ TRANSLATE_X = 0,
31+ TRANSLATE_Y,
32+ TRANSLATE_Z,
33+ YAW,
34+ STEP_TRANSLATE_X,
35+ STEP_TRANSLATE_Y,
36+ STEP_TRANSLATE_Z,
37+ STEP_YAW,
38+ PITCH,
39+ ZOOM,
40+ MAX_DRIVE_KEYS
41+};
42+
2743 enum eyeContactTarget {
2844 LEFT_EYE,
2945 RIGHT_EYE,
@@ -37,7 +53,6 @@ enum AudioListenerMode { · 7 unmodified lines
3753 };
3854 Q_DECLARE_METATYPE(AudioListenerMode);
3955
40−
4156 class MyAvatar : public Avatar {
4257 Q_OBJECT
4358 Q_PROPERTY(bool shouldRenderLocally READ getShouldRenderLocally WRITE setShouldRenderLocally)
@@ -53,6 +68,17 @@ class MyAvatar : public Avatar { · 9 unmodified lines
5368 Q_PROPERTY(AudioListenerMode CUSTOM READ getAudioListenerModeCustom)
5469 //TODO: make gravity feature work Q_PROPERTY(glm::vec3 gravity READ getGravity WRITE setGravity)
5570
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+
5682 public:
5783 MyAvatar(RigPointer rig);
5884 ~MyAvatar();
@@ -111,6 +137,18 @@ public: · 52 unmodified lines
111137 Q_INVOKABLE AnimationDetails getAnimationDetailsByRole(const QString& role);
112138 Q_INVOKABLE AnimationDetails getAnimationDetails(const QString& url);
113139 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); }
114152
115153 // get/set avatar data
116154 void saveData();
@@ -141,6 +179,16 @@ public: · 24 unmodified lines
141179
142180 Q_INVOKABLE glm::vec3 getTargetAvatarPosition() const { return _targetAvatarPosition; }
143181
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+
144192 AvatarWeakPointer getLookAtTargetAvatar() const { return _lookAtTargetAvatar; }
145193 void updateLookAtTargetAvatar();
146194 void clearLookAtTargetAvatar();
@@ -265,7 +313,6 @@ private: · 118 unmodified lines
265313 const glm::vec3& translation = glm::vec3(), const glm::quat& rotation = glm::quat(), float scale = 1.0f,
266314 bool allowDuplicates = false, bool useSaved = true) override;
267315
268− void renderLaserPointers(gpu::Batch& batch);
269316 const RecorderPointer getRecorder() const { return _recorder; }
270317 const PlayerPointer getPlayer() const { return _player; }
271318
@@ -281,6 +328,8 @@ private: · 9 unmodified lines
281328
282329 void setVisibleInSceneIfReady(Model* model, render::ScenePointer scene, bool visiblity);
283330
331+ PalmData getActivePalmData(int palmIndex) const;
332+
284333 // derive avatar body position and orientation from the current HMD Sensor location.
285334 // results are in sensor space
286335 glm::mat4 deriveBodyFromHMDSensor() const;
@@ -370,6 +419,8 @@ private: · 83 unmodified lines
370419 AtRestDetector _hmdAtRestDetector;
371420 glm::vec3 _lastPosition;
372421 bool _lastIsMoving { false };
422+ quint64 _lastStepPulse { 0 };
423+ bool _pulseUpdate { false };
373424 };
374425
375426 QScriptValue 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
9797 emit skeletonLoaded();
9898 }
9999
100−static const PalmData* getPalmWithIndex(Hand* hand, int index) {
101− const PalmData* palm = nullptr;
102− for (size_t j = 0; j < hand->getNumPalms(); j++) {
103− if (hand->getPalms()[j].getSixenseID() == index) {
104− palm = &(hand->getPalms()[j]);
105− break;
106− }
107− }
108− return palm;
109−}
110−
111100 const float PALM_PRIORITY = DEFAULT_PRIORITY;
112101 // Called within Model::simulate call, below.
113102 void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
114− if (_owningAvatar->isMyAvatar()) {
115− _rig->computeMotionAnimationState(deltaTime, _owningAvatar->getPosition(), _owningAvatar->getVelocity(), _owningAvatar->getOrientation());
116− }
117103 Head* head = _owningAvatar->getHead();
118104 if (_owningAvatar->isMyAvatar()) {
119105 MyAvatar* myAvatar = static_cast<MyAvatar*>(_owningAvatar);
@@ -160,28 +146,30 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { · 40 unmodified lines
160146 _rig->updateFromHeadParameters(headParams, deltaTime);
161147
162148 Rig::HandParameters handParams;
163− const PalmData* leftPalm = getPalmWithIndex(myAvatar->getHand(), LEFT_HAND_INDEX);
164− if (leftPalm && leftPalm->isActive()) {
149+
150+ auto leftPalm = myAvatar->getHand()->getCopyOfPalmData(HandData::LeftHand);
151+ if (leftPalm.isValid() && leftPalm.isActive()) {
165152 handParams.isLeftEnabled = true;
166− handParams.leftPosition = leftPalm->getRawPosition();
167− handParams.leftOrientation = leftPalm->getRawRotation();
168− handParams.leftTrigger = leftPalm->getTrigger();
153+ handParams.leftPosition = leftPalm.getRawPosition();
154+ handParams.leftOrientation = leftPalm.getRawRotation();
155+ handParams.leftTrigger = leftPalm.getTrigger();
169156 } else {
170157 handParams.isLeftEnabled = false;
171158 }
172159
173− const PalmData* rightPalm = getPalmWithIndex(myAvatar->getHand(), RIGHT_HAND_INDEX);
174− if (rightPalm && rightPalm->isActive()) {
160+ auto rightPalm = myAvatar->getHand()->getCopyOfPalmData(HandData::RightHand);
161+ if (rightPalm.isValid() && rightPalm.isActive()) {
175162 handParams.isRightEnabled = true;
176− handParams.rightPosition = rightPalm->getRawPosition();
177− handParams.rightOrientation = rightPalm->getRawRotation();
178− handParams.rightTrigger = rightPalm->getTrigger();
163+ handParams.rightPosition = rightPalm.getRawPosition();
164+ handParams.rightOrientation = rightPalm.getRawRotation();
165+ handParams.rightTrigger = rightPalm.getTrigger();
179166 } else {
180167 handParams.isRightEnabled = false;
181168 }
182169
183170 _rig->updateFromHandParameters(handParams, deltaTime);
184171
172+ _rig->computeMotionAnimationState(deltaTime, _owningAvatar->getPosition(), _owningAvatar->getVelocity(), _owningAvatar->getOrientation());
185173 // evaluate AnimGraph animation and update jointStates.
186174 Model::updateRig(deltaTime, parentTransform);
187175
@@ -196,7 +184,7 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { · 8 unmodified lines
196184
197185 _rig->updateFromEyeParameters(eyeParams);
198186
199− // rebuild the jointState transform for the eyes only
187+ // rebuild the jointState transform for the eyes only. Must be after updateRig.
200188 _rig->updateJointState(eyeParams.leftEyeJointIndex, parentTransform);
201189 _rig->updateJointState(eyeParams.rightEyeJointIndex, parentTransform);
202190
@@ -266,15 +254,15 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { · 63 unmodified lines
266254
267255 const FBXGeometry& geometry = _geometry->getFBXGeometry();
268256
269− // find the left and rightmost active palms
270− int leftPalmIndex, rightPalmIndex;
271− Hand* hand = _owningAvatar->getHand();
272− hand->getLeftRightPalmIndices(leftPalmIndex, rightPalmIndex);
273−
274257 // Don't Relax toward hand positions when in animGraph mode.
275258 if (!_rig->getEnableAnimGraph()) {
259+
260+ Hand* hand = _owningAvatar->getHand();
261+ auto leftPalm = hand->getCopyOfPalmData(HandData::LeftHand);
262+ auto rightPalm = hand->getCopyOfPalmData(HandData::RightHand);
263+
276264 const float HAND_RESTORATION_RATE = 0.25f;
277− if (leftPalmIndex == -1 && rightPalmIndex == -1) {
265+ if (!leftPalm.isActive() && !rightPalm.isActive()) {
278266 // palms are not yet set, use mouse
279267 if (_owningAvatar->getHandState() == HAND_STATE_NULL) {
280268 restoreRightHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY);
@@ -284,20 +272,14 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { · 3 unmodified lines
284272 applyHandPosition(geometry.rightHandJointIndex, handPosition);
285273 }
286274 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−
293275 } else {
294− if (leftPalmIndex != -1) {
295− applyPalmData(geometry.leftHandJointIndex, hand->getPalms()[leftPalmIndex]);
276+ if (leftPalm.isActive()) {
277+ applyPalmData(geometry.leftHandJointIndex, leftPalm);
296278 } else {
297279 restoreLeftHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY);
298280 }
299− if (rightPalmIndex != -1) {
300− applyPalmData(geometry.rightHandJointIndex, hand->getPalms()[rightPalmIndex]);
281+ if (rightPalm.isActive()) {
282+ applyPalmData(geometry.rightHandJointIndex, rightPalm);
301283 } else {
302284 restoreRightHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY);
303285 }
@@ -348,7 +330,7 @@ void SkeletonModel::applyHandPosition(int jointIndex, const glm::vec3& position) · 44 unmodified lines
348330 PALM_PRIORITY);
349331 }
350332
351−void SkeletonModel::applyPalmData(int jointIndex, PalmData& palm) {
333+void SkeletonModel::applyPalmData(int jointIndex, const PalmData& palm) {
352334 if (jointIndex == -1 || jointIndex >= _rig->getJointStateCount()) {
353335 return;
354336 }
interface/src/avatar/SkeletonModel.h+1−1
@@ -118,7 +118,7 @@ protected: · 117 unmodified lines
118118 /// \param position position of joint in model-frame
119119 void applyHandPosition(int jointIndex, const glm::vec3& position);
120120
121− void applyPalmData(int jointIndex, PalmData& palm);
121+ void applyPalmData(int jointIndex, const PalmData& palm);
122122 private:
123123
124124 void renderJointConstraints(gpu::Batch& batch, int jointIndex);
interface/src/devices/3DConnexionClient.cpp+80−94
@@ -9,9 +9,14 @@ · 8 unmodified lines
99 // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
1010 //
1111
12+
1213 #include "3DConnexionClient.h"
14+
15+#if 0
16+#include <UserActivityLogger.h>
17+#include <PathUtils.h>
18+
1319 #include "Menu.h"
14−#include "UserActivityLogger.h"
1520
1621 const float MAX_AXIS = 75.0f; // max forward = 2x speed
1722
@@ -25,22 +30,19 @@ ConnexionData& ConnexionData::getInstance() { · 7 unmodified lines
2530 return sharedInstance;
2631 }
2732
28−ConnexionData::ConnexionData() {
29−}
33+
34+ConnexionData::ConnexionData() : InputDevice("ConnexionClient") {}
35+
3036
3137 void ConnexionData::handleAxisEvent() {
32− _axisStateMap[makeInput(ROTATION_AXIS_Y_POS).getChannel()] = (cc_rotation.y > 0.0f) ? cc_rotation.y / MAX_AXIS : 0.0f;
33− _axisStateMap[makeInput(ROTATION_AXIS_Y_NEG).getChannel()] = (cc_rotation.y < 0.0f) ? -cc_rotation.y / MAX_AXIS : 0.0f;
34− _axisStateMap[makeInput(POSITION_AXIS_X_POS).getChannel()] = (cc_position.x > 0.0f) ? cc_position.x / MAX_AXIS : 0.0f;
35− _axisStateMap[makeInput(POSITION_AXIS_X_NEG).getChannel()] = (cc_position.x < 0.0f) ? -cc_position.x / MAX_AXIS : 0.0f;
36− _axisStateMap[makeInput(POSITION_AXIS_Y_POS).getChannel()] = (cc_position.y > 0.0f) ? cc_position.y / MAX_AXIS : 0.0f;
37− _axisStateMap[makeInput(POSITION_AXIS_Y_NEG).getChannel()] = (cc_position.y < 0.0f) ? -cc_position.y / MAX_AXIS : 0.0f;
38− _axisStateMap[makeInput(POSITION_AXIS_Z_POS).getChannel()] = (cc_position.z > 0.0f) ? cc_position.z / MAX_AXIS : 0.0f;
39− _axisStateMap[makeInput(POSITION_AXIS_Z_NEG).getChannel()] = (cc_position.z < 0.0f) ? -cc_position.z / MAX_AXIS : 0.0f;
40− _axisStateMap[makeInput(ROTATION_AXIS_X_POS).getChannel()] = (cc_rotation.x > 0.0f) ? cc_rotation.x / MAX_AXIS : 0.0f;
41− _axisStateMap[makeInput(ROTATION_AXIS_X_NEG).getChannel()] = (cc_rotation.x < 0.0f) ? -cc_rotation.x / MAX_AXIS : 0.0f;
42− _axisStateMap[makeInput(ROTATION_AXIS_Z_POS).getChannel()] = (cc_rotation.z > 0.0f) ? cc_rotation.z / MAX_AXIS : 0.0f;
43− _axisStateMap[makeInput(ROTATION_AXIS_Z_NEG).getChannel()] = (cc_rotation.z < 0.0f) ? -cc_rotation.z / MAX_AXIS : 0.0f;
38+ auto rotation = cc_rotation / MAX_AXIS;
39+ _axisStateMap[ROTATE_X] = rotation.x;
40+ _axisStateMap[ROTATE_Y] = rotation.y;
41+ _axisStateMap[ROTATE_Z] = rotation.z;
42+ auto position = cc_rotation / MAX_AXIS;
43+ _axisStateMap[TRANSLATE_X] = position.x;
44+ _axisStateMap[TRANSLATE_Y] = position.y;
45+ _axisStateMap[TRANSLATE_Z] = position.z;
4446 }
4547
4648 void ConnexionData::setButton(int lastButtonState) {
@@ -48,76 +50,65 @@ void ConnexionData::setButton(int lastButtonState) { · 1 unmodified line
4850 _buttonPressedMap.insert(lastButtonState);
4951 }
5052
51−void ConnexionData::registerToUserInputMapper(UserInputMapper& mapper) {
52− // Grab the current free device ID
53− _deviceID = mapper.getFreeDeviceID();
54−
55− auto proxy = UserInputMapper::DeviceProxy::Pointer(new UserInputMapper::DeviceProxy("ConnexionClient"));
56− proxy->getButton = [this](const UserInputMapper::Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); };
57− proxy->getAxis = [this](const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); };
58− proxy->getAvailabeInputs = [this]() -> QVector<UserInputMapper::InputPair> {
59− QVector<UserInputMapper::InputPair> availableInputs;
60−
61− availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_1), "Left button"));
62− availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_2), "Right button"));
63− availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_3), "Both buttons"));
64−
65− availableInputs.append(UserInputMapper::InputPair(makeInput(POSITION_AXIS_Y_NEG), "Move backward"));
66− availableInputs.append(UserInputMapper::InputPair(makeInput(POSITION_AXIS_Y_POS), "Move forward"));
67− availableInputs.append(UserInputMapper::InputPair(makeInput(POSITION_AXIS_X_POS), "Move right"));
68− availableInputs.append(UserInputMapper::InputPair(makeInput(POSITION_AXIS_X_NEG), "Move Left"));
69− availableInputs.append(UserInputMapper::InputPair(makeInput(POSITION_AXIS_Z_POS), "Move up"));
70− availableInputs.append(UserInputMapper::InputPair(makeInput(POSITION_AXIS_Z_NEG), "Move down"));
71− availableInputs.append(UserInputMapper::InputPair(makeInput(ROTATION_AXIS_Y_NEG), "Rotate backward"));
72− availableInputs.append(UserInputMapper::InputPair(makeInput(ROTATION_AXIS_Y_POS), "Rotate forward"));
73− availableInputs.append(UserInputMapper::InputPair(makeInput(ROTATION_AXIS_X_POS), "Rotate right"));
74− availableInputs.append(UserInputMapper::InputPair(makeInput(ROTATION_AXIS_X_NEG), "Rotate left"));
75− availableInputs.append(UserInputMapper::InputPair(makeInput(ROTATION_AXIS_Z_POS), "Rotate up"));
76− availableInputs.append(UserInputMapper::InputPair(makeInput(ROTATION_AXIS_Z_NEG), "Rotate down"));
77−
53+void ConnexionData::buildDeviceProxy(controller::DeviceProxy::Pointer proxy) {
54+ proxy->_name = _name = "ConnexionClient";
55+ proxy->getButton = [this](const controller::Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); };
56+ proxy->getAxis = [this](const controller::Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); };
57+ proxy->getAvailabeInputs = [this]() -> QVector<controller::Input::NamedPair> {
58+ using namespace controller;
59+ static QVector<controller::Input::NamedPair> availableInputs {
60+ Input::NamedPair(makeInput(BUTTON_1), "LeftButton"),
61+ Input::NamedPair(makeInput(BUTTON_2), "RightButton"),
62+ Input::NamedPair(makeInput(BUTTON_3), "BothButtons"),
63+ Input::NamedPair(makeInput(TRANSLATE_X), "TranslateX"),
64+ Input::NamedPair(makeInput(TRANSLATE_Y), "TranslateY"),
65+ Input::NamedPair(makeInput(TRANSLATE_Z), "TranslateZ"),
66+ Input::NamedPair(makeInput(ROTATE_X), "RotateX"),
67+ Input::NamedPair(makeInput(ROTATE_Y), "RotateY"),
68+ Input::NamedPair(makeInput(ROTATE_Z), "RotateZ"),
69+ };
7870 return availableInputs;
7971 };
80− proxy->resetDeviceBindings = [this, &mapper]() -> bool {
81− mapper.removeAllInputChannelsForDevice(_deviceID);
82− this->assignDefaultInputMapping(mapper);
83− return true;
84− };
85− mapper.registerDevice(_deviceID, proxy);
8672 }
8773
88−void ConnexionData::assignDefaultInputMapping(UserInputMapper& mapper) {
89− const float JOYSTICK_MOVE_SPEED = 1.0f;
90− //const float DPAD_MOVE_SPEED = 0.5f;
91− const float JOYSTICK_YAW_SPEED = 0.5f;
92− const float JOYSTICK_PITCH_SPEED = 0.25f;
93− const float BOOM_SPEED = 0.1f;
94−
95− // Y axes are flipped (up is negative)
96− // postion: Movement, strafing
97− mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(POSITION_AXIS_Y_NEG), JOYSTICK_MOVE_SPEED);
98− mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(POSITION_AXIS_Y_POS), JOYSTICK_MOVE_SPEED);
99− mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(POSITION_AXIS_X_POS), JOYSTICK_MOVE_SPEED);
100− mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(POSITION_AXIS_X_NEG), JOYSTICK_MOVE_SPEED);
101− mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(POSITION_AXIS_Z_NEG), JOYSTICK_MOVE_SPEED);
102− mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(POSITION_AXIS_Z_POS), JOYSTICK_MOVE_SPEED);
103−
104− // Rotation: Camera orientation with button 1
105− mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(ROTATION_AXIS_Z_POS), JOYSTICK_YAW_SPEED);
106− mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(ROTATION_AXIS_Z_NEG), JOYSTICK_YAW_SPEED);
107− mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(ROTATION_AXIS_Y_NEG), JOYSTICK_PITCH_SPEED);
108− mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(ROTATION_AXIS_Y_POS), JOYSTICK_PITCH_SPEED);
109−
110− // Button controls
111− // Zoom
112− mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(BUTTON_1), BOOM_SPEED);
113− mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(BUTTON_2), BOOM_SPEED);
114−
115− // Zoom
116− // mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(ROTATION_AXIS_Z_NEG), BOOM_SPEED);
117− // mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(ROTATION_AXIS_Z_POS), BOOM_SPEED);
118−
74+QString ConnexionData::getDefaultMappingConfig() {
75+ static const QString MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/vive.json";
76+ return MAPPING_JSON;
11977 }
12078
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+
121112 float ConnexionData::getButton(int channel) const {
122113 if (!_buttonPressedMap.empty()) {
123114 if (_buttonPressedMap.find(channel) != _buttonPressedMap.end()) {
@@ -138,15 +129,15 @@ float ConnexionData::getAxis(int channel) const { · 14 unmodified lines
138129 }
139130 }
140131
141−UserInputMapper::Input ConnexionData::makeInput(ConnexionData::ButtonChannel button) {
142− return UserInputMapper::Input(_deviceID, button, UserInputMapper::ChannelType::BUTTON);
132+controller::Input ConnexionData::makeInput(ConnexionData::ButtonChannel button) {
133+ return controller::Input(_deviceID, button, controller::ChannelType::BUTTON);
143134 }
144135
145−UserInputMapper::Input ConnexionData::makeInput(ConnexionData::PositionChannel axis) {
146− return UserInputMapper::Input(_deviceID, axis, UserInputMapper::ChannelType::AXIS);
136+controller::Input ConnexionData::makeInput(ConnexionData::PositionChannel axis) {
137+ return controller::Input(_deviceID, axis, controller::ChannelType::AXIS);
147138 }
148139
149−void ConnexionData::update() {
140+void ConnexionData::update(float deltaTime, bool jointsCaptured) {
150141 // the update is done in the ConnexionClient class.
151142 // for windows in the nativeEventFilter the inputmapper is connected or registed or removed when an 3Dconnnexion device is attached or detached
152143 // 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
187178 QAbstractEventDispatcher::instance()->removeNativeEventFilter(this);
188179 ConnexionData& connexiondata = ConnexionData::getInstance();
189180 int deviceid = connexiondata.getDeviceID();
190− connexiondata.setDeviceID(0);
191181 auto userInputMapper = DependencyManager::get<UserInputMapper>();
192182 userInputMapper->removeDevice(deviceid);
193183 }
@@ -295,13 +285,10 @@ bool ConnexionClient::RawInputEventFilter(void* msg, long* result) { · 101 unmodified lines
295285 ConnexionData& connexiondata = ConnexionData::getInstance();
296286 auto userInputMapper = DependencyManager::get<UserInputMapper>();
297287 if (Is3dmouseAttached() && connexiondata.getDeviceID() == 0) {
298− connexiondata.registerToUserInputMapper(*userInputMapper);
299− connexiondata.assignDefaultInputMapping(*userInputMapper);
288+ userInputMapper->registerDevice(&connexiondata);
300289 UserActivityLogger::getInstance().connectedDevice("controller", "3Dconnexion");
301290 } else if (!Is3dmouseAttached() && connexiondata.getDeviceID() != 0) {
302− int deviceid = connexiondata.getDeviceID();
303− connexiondata.setDeviceID(0);
304− userInputMapper->removeDevice(deviceid);
291+ userInputMapper->removeDevice(connexiondata.getDeviceID());
305292 }
306293
307294 if (!Is3dmouseAttached()) {
@@ -894,8 +881,7 @@ void ConnexionClient::init() { · 586 unmodified lines
894881
895882 if (Is3dmouseAttached() && connexiondata.getDeviceID() == 0) {
896883 auto userInputMapper = DependencyManager::get<UserInputMapper>();
897− connexiondata.registerToUserInputMapper(*userInputMapper);
898− connexiondata.assignDefaultInputMapping(*userInputMapper);
884+ userInputMapper->registerDevice(&connexiondata);
899885 UserActivityLogger::getInstance().connectedDevice("controller", "3Dconnexion");
900886 }
901887 //let one axis be dominant
@@ -926,8 +912,7 @@ void DeviceAddedHandler(unsigned int connection) { · 24 unmodified lines
926912 if (connexiondata.getDeviceID() == 0) {
927913 qCWarning(interfaceapp) << "3Dconnexion device added ";
928914 auto userInputMapper = DependencyManager::get<UserInputMapper>();
929− connexiondata.registerToUserInputMapper(*userInputMapper);
930− connexiondata.assignDefaultInputMapping(*userInputMapper);
915+ userInputMapper->registerDevice(&connexiondata);
931916 UserActivityLogger::getInstance().connectedDevice("controller", "3Dconnexion");
932917 }
933918 }
@@ -984,3 +969,4 @@ void MessageHandler(unsigned int connection, unsigned int messageType, void *mes · 50 unmodified lines
984969 #endif // __APPLE__
985970
986971 #endif // HAVE_3DCONNEXIONCLIENT
972+#endif
interface/src/devices/3DConnexionClient.h+17−34
@@ -11,9 +11,10 @@ · 10 unmodified lines
1111 #ifndef hifi_3DConnexionClient_h
1212 #define hifi_3DConnexionClient_h
1313
14+#if 0
1415 #include <QObject>
1516 #include <QLibrary>
16−#include <input-plugins/UserInputMapper.h>
17+#include <controllers/UserInputMapper.h>
1718
1819 #include "InterfaceLogging.h"
1920
@@ -175,26 +176,19 @@ public slots: · 155 unmodified lines
175176
176177
177178 // connnects to the userinputmapper
178−class ConnexionData : public QObject {
179+class ConnexionData : public QObject, public controller::InputDevice {
179180 Q_OBJECT
180181
181182 public:
182183 static ConnexionData& getInstance();
183184 ConnexionData();
184−
185185 enum PositionChannel {
186− POSITION_AXIS_X_POS = 1,
187− POSITION_AXIS_X_NEG = 2,
188− POSITION_AXIS_Y_POS = 3,
189− POSITION_AXIS_Y_NEG = 4,
190− POSITION_AXIS_Z_POS = 5,
191− POSITION_AXIS_Z_NEG = 6,
192− ROTATION_AXIS_X_POS = 7,
193− ROTATION_AXIS_X_NEG = 8,
194− ROTATION_AXIS_Y_POS = 9,
195− ROTATION_AXIS_Y_NEG = 10,
196− ROTATION_AXIS_Z_POS = 11,
197− ROTATION_AXIS_Z_NEG = 12
186+ TRANSLATE_X,
187+ TRANSLATE_Y,
188+ TRANSLATE_Z,
189+ ROTATE_X,
190+ ROTATE_Y,
191+ ROTATE_Z,
198192 };
199193
200194 enum ButtonChannel {
@@ -209,19 +203,12 @@ public: · 8 unmodified lines
209203 float getButton(int channel) const;
210204 float getAxis(int channel) const;
211205
212− UserInputMapper::Input makeInput(ConnexionData::PositionChannel axis);
213− UserInputMapper::Input makeInput(ConnexionData::ButtonChannel button);
214−
215− void registerToUserInputMapper(UserInputMapper& mapper);
216− void assignDefaultInputMapping(UserInputMapper& mapper);
217−
218− void update();
219− void focusOutEvent();
220−
221− int getDeviceID() { return _deviceID; }
222− void setDeviceID(int deviceID) { _deviceID = deviceID; }
223−
224− QString _name;
206+ controller::Input makeInput(ConnexionData::PositionChannel axis);
207+ controller::Input makeInput(ConnexionData::ButtonChannel button);
208+ virtual void buildDeviceProxy(controller::DeviceProxy::Pointer proxy) override;
209+ virtual QString getDefaultMappingConfig() override;
210+ virtual void update(float deltaTime, bool jointsCaptured) override;
211+ virtual void focusOutEvent() override;
225212
226213 glm::vec3 cc_position;
227214 glm::vec3 cc_rotation;
@@ -229,12 +216,8 @@ public: · 1 unmodified line
229216
230217 void setButton(int lastButtonState);
231218 void handleAxisEvent();
232−
233−protected:
234− int _deviceID = 0;
235−
236− ButtonPressedMap _buttonPressedMap;
237− AxisStateMap _axisStateMap;
238219 };
239220
221+#endif
222+
240223 #endif // defined(hifi_3DConnexionClient_h)
interface/src/scripting/ControllerScriptingInterface.cpp+47−442
@@ -9,120 +9,20 @@ · 8 unmodified lines
99 // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
1010 //
1111
12+#include "ControllerScriptingInterface.h"
13+
1214 #include <avatar/AvatarManager.h>
1315 #include <avatar/MyAvatar.h>
1416 #include <HandData.h>
1517 #include <HFBackEvent.h>
18+#include <plugins/PluginManager.h>
1619
1720 #include "Application.h"
1821 #include "devices/MotionTracker.h"
19−#include "ControllerScriptingInterface.h"
2022
2123 // TODO: this needs to be removed, as well as any related controller-specific information
2224 #include <input-plugins/SixenseManager.h>
2325
24−
25−ControllerScriptingInterface::ControllerScriptingInterface() :
26− _mouseCaptured(false),
27− _touchCaptured(false),
28− _wheelCaptured(false),
29− _actionsCaptured(false)
30−{
31−
32−}
33−
34−static int actionMetaTypeId = qRegisterMetaType<UserInputMapper::Action>();
35−static int inputChannelMetaTypeId = qRegisterMetaType<UserInputMapper::InputChannel>();
36−static int inputMetaTypeId = qRegisterMetaType<UserInputMapper::Input>();
37−static int inputPairMetaTypeId = qRegisterMetaType<UserInputMapper::InputPair>();
38−
39−QScriptValue inputToScriptValue(QScriptEngine* engine, const UserInputMapper::Input& input);
40−void inputFromScriptValue(const QScriptValue& object, UserInputMapper::Input& input);
41−QScriptValue inputChannelToScriptValue(QScriptEngine* engine, const UserInputMapper::InputChannel& inputChannel);
42−void inputChannelFromScriptValue(const QScriptValue& object, UserInputMapper::InputChannel& inputChannel);
43−QScriptValue actionToScriptValue(QScriptEngine* engine, const UserInputMapper::Action& action);
44−void actionFromScriptValue(const QScriptValue& object, UserInputMapper::Action& action);
45−QScriptValue inputPairToScriptValue(QScriptEngine* engine, const UserInputMapper::InputPair& inputPair);
46−void inputPairFromScriptValue(const QScriptValue& object, UserInputMapper::InputPair& inputPair);
47−
48−QScriptValue inputToScriptValue(QScriptEngine* engine, const UserInputMapper::Input& input) {
49− QScriptValue obj = engine->newObject();
50− obj.setProperty("device", input.getDevice());
51− obj.setProperty("channel", input.getChannel());
52− obj.setProperty("type", (unsigned short) input.getType());
53− obj.setProperty("id", input.getID());
54− return obj;
55−}
56−
57−void inputFromScriptValue(const QScriptValue& object, UserInputMapper::Input& input) {
58− input.setDevice(object.property("device").toUInt16());
59− input.setChannel(object.property("channel").toUInt16());
60− input.setType(object.property("type").toUInt16());
61− input.setID(object.property("id").toInt32());
62−}
63−
64−QScriptValue inputChannelToScriptValue(QScriptEngine* engine, const UserInputMapper::InputChannel& inputChannel) {
65− QScriptValue obj = engine->newObject();
66− obj.setProperty("input", inputToScriptValue(engine, inputChannel.getInput()));
67− obj.setProperty("modifier", inputToScriptValue(engine, inputChannel.getModifier()));
68− obj.setProperty("action", inputChannel.getAction());
69− obj.setProperty("scale", inputChannel.getScale());
70− return obj;
71−}
72−
73−void inputChannelFromScriptValue(const QScriptValue& object, UserInputMapper::InputChannel& inputChannel) {
74− UserInputMapper::Input input;
75− UserInputMapper::Input modifier;
76− inputFromScriptValue(object.property("input"), input);
77− inputChannel.setInput(input);
78− inputFromScriptValue(object.property("modifier"), modifier);
79− inputChannel.setModifier(modifier);
80− inputChannel.setAction(UserInputMapper::Action(object.property("action").toVariant().toInt()));
81− inputChannel.setScale(object.property("scale").toVariant().toFloat());
82−}
83−
84−QScriptValue actionToScriptValue(QScriptEngine* engine, const UserInputMapper::Action& action) {
85− QScriptValue obj = engine->newObject();
86− auto userInputMapper = DependencyManager::get<UserInputMapper>();
87− QVector<UserInputMapper::InputChannel> inputChannels = userInputMapper->getInputChannelsForAction(action);
88− QScriptValue _inputChannels = engine->newArray(inputChannels.size());
89− for (int i = 0; i < inputChannels.size(); i++) {
90− _inputChannels.setProperty(i, inputChannelToScriptValue(engine, inputChannels[i]));
91− }
92− obj.setProperty("action", (int) action);
93− obj.setProperty("actionName", userInputMapper->getActionName(action));
94− obj.setProperty("inputChannels", _inputChannels);
95− return obj;
96−}
97−
98−void actionFromScriptValue(const QScriptValue& object, UserInputMapper::Action& action) {
99− action = UserInputMapper::Action(object.property("action").toVariant().toInt());
100−}
101−
102−QScriptValue inputPairToScriptValue(QScriptEngine* engine, const UserInputMapper::InputPair& inputPair) {
103− QScriptValue obj = engine->newObject();
104− obj.setProperty("input", inputToScriptValue(engine, inputPair.first));
105− obj.setProperty("inputName", inputPair.second);
106− return obj;
107−}
108−
109−void inputPairFromScriptValue(const QScriptValue& object, UserInputMapper::InputPair& inputPair) {
110− inputFromScriptValue(object.property("input"), inputPair.first);
111− inputPair.second = QString(object.property("inputName").toVariant().toString());
112−}
113−
114−void ControllerScriptingInterface::registerControllerTypes(ScriptEngine* engine) {
115− qScriptRegisterSequenceMetaType<QVector<UserInputMapper::Action> >(engine);
116− qScriptRegisterSequenceMetaType<QVector<UserInputMapper::InputChannel> >(engine);
117− qScriptRegisterSequenceMetaType<QVector<UserInputMapper::InputPair> >(engine);
118− qScriptRegisterMetaType(engine, actionToScriptValue, actionFromScriptValue);
119− qScriptRegisterMetaType(engine, inputChannelToScriptValue, inputChannelFromScriptValue);
120− qScriptRegisterMetaType(engine, inputToScriptValue, inputFromScriptValue);
121− qScriptRegisterMetaType(engine, inputPairToScriptValue, inputPairFromScriptValue);
122−
123− wireUpControllers(engine);
124−}
125−
12626 void ControllerScriptingInterface::handleMetaEvent(HFMetaEvent* event) {
12727 if (event->type() == HFActionEvent::startType()) {
12828 emit actionStartEvent(static_cast<HFActionEvent&>(*event));
@@ -135,205 +35,6 @@ void ControllerScriptingInterface::handleMetaEvent(HFMetaEvent* event) { · 6 unmodified lines
13535 }
13636 }
13737
138−const PalmData* ControllerScriptingInterface::getPrimaryPalm() const {
139− int leftPalmIndex, rightPalmIndex;
140−
141− const HandData* handData = DependencyManager::get<AvatarManager>()->getMyAvatar()->getHandData();
142− handData->getLeftRightPalmIndices(leftPalmIndex, rightPalmIndex);
143−
144− if (rightPalmIndex != -1) {
145− return &handData->getPalms()[rightPalmIndex];
146− }
147−
148− return NULL;
149−}
150−
151−int ControllerScriptingInterface::getNumberOfActivePalms() const {
152− const HandData* handData = DependencyManager::get<AvatarManager>()->getMyAvatar()->getHandData();
153− int numberOfPalms = handData->getNumPalms();
154− int numberOfActivePalms = 0;
155− for (int i = 0; i < numberOfPalms; i++) {
156− if (getPalm(i)->isActive()) {
157− numberOfActivePalms++;
158− }
159− }
160− return numberOfActivePalms;
161−}
162−
163−const PalmData* ControllerScriptingInterface::getPalm(int palmIndex) const {
164− const HandData* handData = DependencyManager::get<AvatarManager>()->getMyAvatar()->getHandData();
165− return &handData->getPalms()[palmIndex];
166−}
167−
168−const PalmData* ControllerScriptingInterface::getActivePalm(int palmIndex) const {
169− const HandData* handData = DependencyManager::get<AvatarManager>()->getMyAvatar()->getHandData();
170− int numberOfPalms = handData->getNumPalms();
171− int numberOfActivePalms = 0;
172− for (int i = 0; i < numberOfPalms; i++) {
173− if (getPalm(i)->isActive()) {
174− if (numberOfActivePalms == palmIndex) {
175− return &handData->getPalms()[i];
176− }
177− numberOfActivePalms++;
178− }
179− }
180− return NULL;
181−}
182−
183−bool ControllerScriptingInterface::isPrimaryButtonPressed() const {
184− const PalmData* primaryPalm = getPrimaryPalm();
185− if (primaryPalm) {
186− if (primaryPalm->getControllerButtons() & BUTTON_FWD) {
187− return true;
188− }
189− }
190−
191− return false;
192−}
193−
194−glm::vec2 ControllerScriptingInterface::getPrimaryJoystickPosition() const {
195− const PalmData* primaryPalm = getPrimaryPalm();
196− if (primaryPalm) {
197− return glm::vec2(primaryPalm->getJoystickX(), primaryPalm->getJoystickY());
198− }
199−
200− return glm::vec2(0);
201−}
202−
203−int ControllerScriptingInterface::getNumberOfButtons() const {
204− return getNumberOfActivePalms() * NUMBER_OF_BUTTONS_PER_PALM;
205−}
206−
207−bool ControllerScriptingInterface::isButtonPressed(int buttonIndex) const {
208− int palmIndex = buttonIndex / NUMBER_OF_BUTTONS_PER_PALM;
209− int buttonOnPalm = buttonIndex % NUMBER_OF_BUTTONS_PER_PALM;
210− const PalmData* palmData = getActivePalm(palmIndex);
211− if (palmData) {
212− switch (buttonOnPalm) {
213− case 0:
214− return palmData->getControllerButtons() & BUTTON_0;
215− case 1:
216− return palmData->getControllerButtons() & BUTTON_1;
217− case 2:
218− return palmData->getControllerButtons() & BUTTON_2;
219− case 3:
220− return palmData->getControllerButtons() & BUTTON_3;
221− case 4:
222− return palmData->getControllerButtons() & BUTTON_4;
223− case 5:
224− return palmData->getControllerButtons() & BUTTON_FWD;
225− }
226− }
227− return false;
228−}
229−
230−int ControllerScriptingInterface::getNumberOfTriggers() const {
231− return getNumberOfActivePalms() * NUMBER_OF_TRIGGERS_PER_PALM;
232−}
233−
234−float ControllerScriptingInterface::getTriggerValue(int triggerIndex) const {
235− // we know there's one trigger per palm, so the triggerIndex is the palm Index
236− int palmIndex = triggerIndex;
237− const PalmData* palmData = getActivePalm(palmIndex);
238− if (palmData) {
239− return palmData->getTrigger();
240− }
241− return 0.0f;
242−}
243−
244−int ControllerScriptingInterface::getNumberOfJoysticks() const {
245− return getNumberOfActivePalms() * NUMBER_OF_JOYSTICKS_PER_PALM;
246−}
247−
248−glm::vec2 ControllerScriptingInterface::getJoystickPosition(int joystickIndex) const {
249− // we know there's one joystick per palm, so the joystickIndex is the palm Index
250− int palmIndex = joystickIndex;
251− const PalmData* palmData = getActivePalm(palmIndex);
252− if (palmData) {
253− return glm::vec2(palmData->getJoystickX(), palmData->getJoystickY());
254− }
255− return glm::vec2(0);
256−}
257−
258−int ControllerScriptingInterface::getNumberOfSpatialControls() const {
259− return getNumberOfActivePalms() * NUMBER_OF_SPATIALCONTROLS_PER_PALM;
260−}
261−
262−glm::vec3 ControllerScriptingInterface::getSpatialControlPosition(int controlIndex) const {
263− int palmIndex = controlIndex / NUMBER_OF_SPATIALCONTROLS_PER_PALM;
264− int controlOfPalm = controlIndex % NUMBER_OF_SPATIALCONTROLS_PER_PALM;
265− const PalmData* palmData = getActivePalm(palmIndex);
266− if (palmData) {
267− switch (controlOfPalm) {
268− case PALM_SPATIALCONTROL:
269− return palmData->getPosition();
270− case TIP_SPATIALCONTROL:
271− return palmData->getTipPosition();
272− }
273− }
274− return glm::vec3(0); // bad index
275−}
276−
277−glm::vec3 ControllerScriptingInterface::getSpatialControlVelocity(int controlIndex) const {
278− int palmIndex = controlIndex / NUMBER_OF_SPATIALCONTROLS_PER_PALM;
279− int controlOfPalm = controlIndex % NUMBER_OF_SPATIALCONTROLS_PER_PALM;
280− const PalmData* palmData = getActivePalm(palmIndex);
281− if (palmData) {
282− switch (controlOfPalm) {
283− case PALM_SPATIALCONTROL:
284− return palmData->getVelocity();
285− case TIP_SPATIALCONTROL:
286− return palmData->getTipVelocity();
287− }
288− }
289− return glm::vec3(0); // bad index
290−}
291−
292−glm::quat ControllerScriptingInterface::getSpatialControlRawRotation(int controlIndex) const {
293− int palmIndex = controlIndex / NUMBER_OF_SPATIALCONTROLS_PER_PALM;
294− int controlOfPalm = controlIndex % NUMBER_OF_SPATIALCONTROLS_PER_PALM;
295− const PalmData* palmData = getActivePalm(palmIndex);
296− if (palmData) {
297− switch (controlOfPalm) {
298− case PALM_SPATIALCONTROL:
299− return palmData->getRawRotation();
300− case TIP_SPATIALCONTROL:
301− return palmData->getRawRotation(); // currently the tip doesn't have a unique rotation, use the palm rotation
302− }
303− }
304− return glm::quat(); // bad index
305−}
306−
307−glm::vec3 ControllerScriptingInterface::getSpatialControlRawAngularVelocity(int controlIndex) const {
308− int palmIndex = controlIndex / NUMBER_OF_SPATIALCONTROLS_PER_PALM;
309− int controlOfPalm = controlIndex % NUMBER_OF_SPATIALCONTROLS_PER_PALM;
310− const PalmData* palmData = getActivePalm(palmIndex);
311− if (palmData) {
312− switch (controlOfPalm) {
313− case PALM_SPATIALCONTROL:
314− return palmData->getRawAngularVelocity();
315− case TIP_SPATIALCONTROL:
316− return palmData->getRawAngularVelocity(); // Tip = palm angular velocity
317− }
318− }
319− return glm::vec3(0); // bad index
320−}
321−
322−glm::vec3 ControllerScriptingInterface::getSpatialControlNormal(int controlIndex) const {
323− int palmIndex = controlIndex / NUMBER_OF_SPATIALCONTROLS_PER_PALM;
324− int controlOfPalm = controlIndex % NUMBER_OF_SPATIALCONTROLS_PER_PALM;
325− const PalmData* palmData = getActivePalm(palmIndex);
326− if (palmData) {
327− switch (controlOfPalm) {
328− case PALM_SPATIALCONTROL:
329− return palmData->getNormal();
330− case TIP_SPATIALCONTROL:
331− return palmData->getFingerDirection();
332− }
333− }
334− return glm::vec3(0); // bad index
335−}
336−
33738 bool ControllerScriptingInterface::isKeyCaptured(QKeyEvent* event) const {
33839 return isKeyCaptured(KeyEvent(*event));
33940 }
@@ -383,157 +84,45 @@ glm::vec2 ControllerScriptingInterface::getViewportDimensions() const { · 43 unmodified lines
38384 return qApp->getUiSize();
38485 }
38586
386−QString ControllerScriptingInterface::sanatizeName(const QString& name) {
387− QString cleanName { name };
388− cleanName.remove(QRegularExpression{"[\\(\\)\\.\\s]"});
389− return cleanName;
390−}
391−
392−void ControllerScriptingInterface::wireUpControllers(ScriptEngine* engine) {
393−
394− // Controller.Standard.*
395− auto standardDevice = DependencyManager::get<UserInputMapper>()->getStandardDevice();
396− if (standardDevice) {
397− auto deviceName = sanatizeName(standardDevice->getName());
398− auto deviceInputs = standardDevice->getAvailabeInputs();
399− for (const auto& inputMapping : deviceInputs) {
400− auto input = inputMapping.first;
401− auto inputName = sanatizeName(inputMapping.second);
402− QString deviceInputName{ "Controller." + deviceName + "." + inputName };
403− engine->registerValue(deviceInputName, input.getID());
404− }
405− }
406−
407− // Controller.Hardware.*
408− auto devices = DependencyManager::get<UserInputMapper>()->getDevices();
409− for(const auto& deviceMapping : devices) {
410− auto device = deviceMapping.second.get();
411− auto deviceName = sanatizeName(device->getName());
412− auto deviceInputs = device->getAvailabeInputs();
413− for (const auto& inputMapping : deviceInputs) {
414− auto input = inputMapping.first;
415− auto inputName = sanatizeName(inputMapping.second);
416− QString deviceInputName { "Controller.Hardware." + deviceName + "." + inputName };
417− engine->registerValue(deviceInputName, input.getID());
418− }
419− }
420−
421− // Controller.Actions.*
422− auto actionNames = DependencyManager::get<UserInputMapper>()->getActionNames();
423− int actionNumber = 0;
424− for (const auto& actionName : actionNames) {
425− QString safeActionName { "Controller.Actions." + sanatizeName(actionName) };
426− engine->registerValue(safeActionName, actionNumber);
427− actionNumber++;
428− }
429−}
430−
431−AbstractInputController* ControllerScriptingInterface::createInputController(const QString& deviceName, const QString& tracker) {
87+controller::InputController::Pointer ControllerScriptingInterface::createInputController(const QString& deviceName, const QString& tracker) {
43288 // This is where we retreive the Device Tracker category and then the sub tracker within it
433− //TODO C++11 auto icIt = _inputControllers.find(0);
434− InputControllerMap::iterator icIt = _inputControllers.find(0);
435−
89+ auto icIt = _inputControllers.find(0);
43690 if (icIt != _inputControllers.end()) {
43791 return (*icIt).second;
438− } else {
439−
440− // Look for device
441− DeviceTracker::ID deviceID = DeviceTracker::getDeviceID(deviceName.toStdString());
442− if (deviceID < 0) {
443− deviceID = 0;
444− }
445− // TODO in this current implementation, we just pick the device assuming there is one (normally the Leapmotion)
446− // in the near future we need to change that to a real mapping between the devices and the deviceName
447− // ALso we need to expand the spec so we can fall back on the "default" controller per categories
448−
449− if (deviceID >= 0) {
450− // TODO here again the assumption it's the LeapMotion and so it's a MOtionTracker, this would need to be changed to support different types of devices
451− MotionTracker* motionTracker = dynamic_cast< MotionTracker* > (DeviceTracker::getDevice(deviceID));
452− if (motionTracker) {
453− MotionTracker::Index trackerID = motionTracker->findJointIndex(tracker.toStdString());
454− if (trackerID >= 0) {
455− AbstractInputController* inputController = new InputController(deviceID, trackerID, this);
456−
457− _inputControllers.insert(InputControllerMap::value_type(inputController->getKey(), inputController));
458−
459− return inputController;
460− }
92+ }
93+
94+
95+ // Look for device
96+ DeviceTracker::ID deviceID = DeviceTracker::getDeviceID(deviceName.toStdString());
97+ if (deviceID < 0) {
98+ deviceID = 0;
99+ }
100+ // TODO in this current implementation, we just pick the device assuming there is one (normally the Leapmotion)
101+ // in the near future we need to change that to a real mapping between the devices and the deviceName
102+ // ALso we need to expand the spec so we can fall back on the "default" controller per categories
103+
104+ if (deviceID >= 0) {
105+ // TODO here again the assumption it's the LeapMotion and so it's a MOtionTracker, this would need to be changed to support different types of devices
106+ MotionTracker* motionTracker = dynamic_cast< MotionTracker* > (DeviceTracker::getDevice(deviceID));
107+ if (motionTracker) {
108+ MotionTracker::Index trackerID = motionTracker->findJointIndex(tracker.toStdString());
109+ if (trackerID >= 0) {
110+ controller::InputController::Pointer inputController = std::make_shared<InputController>(deviceID, trackerID, this);
111+ controller::InputController::Key key = inputController->getKey();
112+ _inputControllers.insert(InputControllerMap::value_type(key, inputController));
113+ return inputController;
461114 }
462115 }
463−
464− return 0;
465116 }
466−}
467117
468−void ControllerScriptingInterface::releaseInputController(AbstractInputController* input) {
469− _inputControllers.erase(input->getKey());
118+ return controller::InputController::Pointer();
470119 }
471120
472−void ControllerScriptingInterface::updateInputControllers() {
473− //TODO C++11 for (auto it = _inputControllers.begin(); it != _inputControllers.end(); it++) {
474− for (InputControllerMap::iterator it = _inputControllers.begin(); it != _inputControllers.end(); it++) {
475− (*it).second->update();
476− }
477−}
478−
479−QVector<UserInputMapper::Action> ControllerScriptingInterface::getAllActions() {
480− return DependencyManager::get<UserInputMapper>()->getAllActions();
481−}
482−
483−QVector<UserInputMapper::InputChannel> ControllerScriptingInterface::getInputChannelsForAction(UserInputMapper::Action action) {
484− return DependencyManager::get<UserInputMapper>()->getInputChannelsForAction(action);
485−}
486−
487−QString ControllerScriptingInterface::getDeviceName(unsigned int device) {
488− return DependencyManager::get<UserInputMapper>()->getDeviceName((unsigned short)device);
489−}
490−
491−QVector<UserInputMapper::InputChannel> ControllerScriptingInterface::getAllInputsForDevice(unsigned int device) {
492− return DependencyManager::get<UserInputMapper>()->getAllInputsForDevice(device);
493−}
494−
495−bool ControllerScriptingInterface::addInputChannel(UserInputMapper::InputChannel inputChannel) {
496− return DependencyManager::get<UserInputMapper>()->addInputChannel(inputChannel._action, inputChannel._input, inputChannel._modifier, inputChannel._scale);
497−}
498−
499−bool ControllerScriptingInterface::removeInputChannel(UserInputMapper::InputChannel inputChannel) {
500− return DependencyManager::get<UserInputMapper>()->removeInputChannel(inputChannel);
501−}
502−
503−QVector<UserInputMapper::InputPair> ControllerScriptingInterface::getAvailableInputs(unsigned int device) {
504− return DependencyManager::get<UserInputMapper>()->getAvailableInputs((unsigned short)device);
505−}
506−
507−void ControllerScriptingInterface::resetAllDeviceBindings() {
508− DependencyManager::get<UserInputMapper>()->resetAllDeviceBindings();
509−}
510−
511−void ControllerScriptingInterface::resetDevice(unsigned int device) {
512− DependencyManager::get<UserInputMapper>()->resetDevice(device);
513−}
514−
515−int ControllerScriptingInterface::findDevice(QString name) {
516− return DependencyManager::get<UserInputMapper>()->findDevice(name);
517−}
518−
519−QVector<QString> ControllerScriptingInterface::getDeviceNames() {
520− return DependencyManager::get<UserInputMapper>()->getDeviceNames();
521−}
522−
523−float ControllerScriptingInterface::getActionValue(int action) {
524− return DependencyManager::get<UserInputMapper>()->getActionState(UserInputMapper::Action(action));
525−}
526−
527−int ControllerScriptingInterface::findAction(QString actionName) {
528− return DependencyManager::get<UserInputMapper>()->findAction(actionName);
529−}
530−
531−QVector<QString> ControllerScriptingInterface::getActionNames() const {
532− return DependencyManager::get<UserInputMapper>()->getActionNames();
121+void ControllerScriptingInterface::releaseInputController(controller::InputController::Pointer input) {
122+ _inputControllers.erase(input->getKey());
533123 }
534124
535125 InputController::InputController(int deviceTrackerId, int subTrackerId, QObject* parent) :
536− AbstractInputController(),
537126 _deviceTrackerId(deviceTrackerId),
538127 _subTrackerId(subTrackerId),
539128 _isActive(false)
@@ -556,7 +145,7 @@ void InputController::update() { · 16 unmodified lines
556145 joint->getLocFrame().getRotation(_eventCache.locRotation);
557146
558147 _isActive = true;
559− emit spatialEvent(_eventCache);
148+ //emit spatialEvent(_eventCache);
560149 }
561150 }
562151 }
@@ -568,3 +157,19 @@ const unsigned int INPUTCONTROLLER_KEY_DEVICE_MASK = 16; · 5 unmodified lines
568157 InputController::Key InputController::getKey() const {
569158 return (((_deviceTrackerId & INPUTCONTROLLER_KEY_DEVICE_MASK) << INPUTCONTROLLER_KEY_DEVICE_OFFSET) | _subTrackerId);
570159 }
160+
161+
162+void ControllerScriptingInterface::emitKeyPressEvent(QKeyEvent* event) { emit keyPressEvent(KeyEvent(*event)); }
163+void ControllerScriptingInterface::emitKeyReleaseEvent(QKeyEvent* event) { emit keyReleaseEvent(KeyEvent(*event)); }
164+
165+void ControllerScriptingInterface::emitMouseMoveEvent(QMouseEvent* event, unsigned int deviceID) { emit mouseMoveEvent(MouseEvent(*event, deviceID)); }
166+void ControllerScriptingInterface::emitMousePressEvent(QMouseEvent* event, unsigned int deviceID) { emit mousePressEvent(MouseEvent(*event, deviceID)); }
167+void ControllerScriptingInterface::emitMouseDoublePressEvent(QMouseEvent* event, unsigned int deviceID) { emit mouseDoublePressEvent(MouseEvent(*event, deviceID)); }
168+void ControllerScriptingInterface::emitMouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) { emit mouseReleaseEvent(MouseEvent(*event, deviceID)); }
169+
170+void ControllerScriptingInterface::emitTouchBeginEvent(const TouchEvent& event) { emit touchBeginEvent(event); }
171+void ControllerScriptingInterface::emitTouchEndEvent(const TouchEvent& event) { emit touchEndEvent(event); }
172+void ControllerScriptingInterface::emitTouchUpdateEvent(const TouchEvent& event) { emit touchUpdateEvent(event); }
173+
174+void ControllerScriptingInterface::emitWheelEvent(QWheelEvent* event) { emit wheelEvent(*event); }
175+
interface/src/scripting/ControllerScriptingInterface.h+49−89
@@ -14,12 +14,20 @@ · 13 unmodified lines
1414
1515 #include <QtCore/QObject>
1616
17−#include <input-plugins/UserInputMapper.h>
17+#include <controllers/UserInputMapper.h>
18+#include <controllers/ScriptingInterface.h>
19+
20+#include <HFActionEvent.h>
21+#include <KeyEvent.h>
22+#include <MouseEvent.h>
23+#include <SpatialEvent.h>
24+#include <TouchEvent.h>
25+#include <WheelEvent.h>
26+class ScriptEngine;
1827
19−#include <AbstractControllerScriptingInterface.h>
2028 class PalmData;
2129
22−class InputController : public AbstractInputController {
30+class InputController : public controller::InputController {
2331 Q_OBJECT
2432
2533 public:
@@ -50,125 +58,77 @@ signals: · 24 unmodified lines
5058
5159
5260 /// handles scripting of input controller commands from JS
53−class ControllerScriptingInterface : public AbstractControllerScriptingInterface {
61+class ControllerScriptingInterface : public controller::ScriptingInterface {
5462 Q_OBJECT
5563
64+
5665 public:
57− ControllerScriptingInterface();
58−
59− virtual void registerControllerTypes(ScriptEngine* engine);
60−
61− void emitKeyPressEvent(QKeyEvent* event) { emit keyPressEvent(KeyEvent(*event)); }
62− void emitKeyReleaseEvent(QKeyEvent* event) { emit keyReleaseEvent(KeyEvent(*event)); }
66+ virtual ~ControllerScriptingInterface() {}
67+
68+ void emitKeyPressEvent(QKeyEvent* event);
69+ void emitKeyReleaseEvent(QKeyEvent* event);
6370
6471 void handleMetaEvent(HFMetaEvent* event);
6572
66− void emitMouseMoveEvent(QMouseEvent* event, unsigned int deviceID = 0) { emit mouseMoveEvent(MouseEvent(*event, deviceID)); }
67− void emitMousePressEvent(QMouseEvent* event, unsigned int deviceID = 0) { emit mousePressEvent(MouseEvent(*event, deviceID)); }
68− void emitMouseDoublePressEvent(QMouseEvent* event, unsigned int deviceID = 0) { emit mouseDoublePressEvent(MouseEvent(*event, deviceID)); }
69− void emitMouseReleaseEvent(QMouseEvent* event, unsigned int deviceID = 0) { emit mouseReleaseEvent(MouseEvent(*event, deviceID)); }
73+ void emitMouseMoveEvent(QMouseEvent* event, unsigned int deviceID = 0);
74+ void emitMousePressEvent(QMouseEvent* event, unsigned int deviceID = 0);
75+ void emitMouseDoublePressEvent(QMouseEvent* event, unsigned int deviceID = 0);
76+ void emitMouseReleaseEvent(QMouseEvent* event, unsigned int deviceID = 0);
7077
71− void emitTouchBeginEvent(const TouchEvent& event) { emit touchBeginEvent(event); }
72− void emitTouchEndEvent(const TouchEvent& event) { emit touchEndEvent(event); }
73− void emitTouchUpdateEvent(const TouchEvent& event) { emit touchUpdateEvent(event); }
78+ void emitTouchBeginEvent(const TouchEvent& event);
79+ void emitTouchEndEvent(const TouchEvent& event);
80+ void emitTouchUpdateEvent(const TouchEvent& event);
7481
75− void emitWheelEvent(QWheelEvent* event) { emit wheelEvent(*event); }
82+ void emitWheelEvent(QWheelEvent* event);
7683
7784 bool isKeyCaptured(QKeyEvent* event) const;
7885 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; }
8386 bool isJoystickCaptured(int joystickIndex) const;
8487
85− void updateInputControllers();
86−
8788 public slots:
88− Q_INVOKABLE virtual QVector<UserInputMapper::Action> getAllActions();
89−
90− Q_INVOKABLE virtual bool addInputChannel(UserInputMapper::InputChannel inputChannel);
91− Q_INVOKABLE virtual bool removeInputChannel(UserInputMapper::InputChannel inputChannel);
92− Q_INVOKABLE virtual QVector<UserInputMapper::InputChannel> getInputChannelsForAction(UserInputMapper::Action action);
93−
94− Q_INVOKABLE virtual QVector<UserInputMapper::InputPair> getAvailableInputs(unsigned int device);
95− Q_INVOKABLE virtual QVector<UserInputMapper::InputChannel> getAllInputsForDevice(unsigned int device);
96−
97− Q_INVOKABLE virtual QString getDeviceName(unsigned int device);
98−
99− Q_INVOKABLE virtual float getActionValue(int action);
100−
101− Q_INVOKABLE virtual void resetDevice(unsigned int device);
102− Q_INVOKABLE virtual void resetAllDeviceBindings();
103− Q_INVOKABLE virtual int findDevice(QString name);
104− Q_INVOKABLE virtual QVector<QString> getDeviceNames();
105−
106− Q_INVOKABLE virtual int findAction(QString actionName);
107− Q_INVOKABLE virtual QVector<QString> getActionNames() const;
108−
109− virtual bool isPrimaryButtonPressed() const;
110− virtual glm::vec2 getPrimaryJoystickPosition() const;
111−
112− virtual int getNumberOfButtons() const;
113− virtual bool isButtonPressed(int buttonIndex) const;
114−
115− virtual int getNumberOfTriggers() const;
116− virtual float getTriggerValue(int triggerIndex) const;
11789
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;
12790 virtual void captureKeyEvents(const KeyEvent& event);
12891 virtual void releaseKeyEvents(const KeyEvent& event);
12992
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−
14293 virtual void captureJoystick(int joystickIndex);
14394 virtual void releaseJoystick(int joystickIndex);
14495
14596 virtual glm::vec2 getViewportDimensions() const;
14697
14798 /// Factory to create an InputController
148− virtual AbstractInputController* createInputController(const QString& deviceName, const QString& tracker);
99+ virtual controller::InputController::Pointer createInputController(const QString& deviceName, const QString& tracker);
100+ virtual void releaseInputController(controller::InputController::Pointer input);
101+
102+signals:
103+ void keyPressEvent(const KeyEvent& event);
104+ void keyReleaseEvent(const KeyEvent& event);
149105
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);
151122
152123 private:
153124 QString sanatizeName(const QString& name); /// makes a name clean for inclusing in JavaScript
154125
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;
164126 QMultiMap<int,KeyEvent> _capturedKeys;
165127 QSet<int> _capturedJoysticks;
166128
167− typedef std::map< AbstractInputController::Key, AbstractInputController* > InputControllerMap;
129+ using InputKey = controller::InputController::Key;
130+ using InputControllerMap = std::map<InputKey, controller::InputController::Pointer>;
168131 InputControllerMap _inputControllers;
169−
170− void wireUpControllers(ScriptEngine* engine);
171−
172132 };
173133
174134 const 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
2727
2828 #include "Application.h"
2929 #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>
3131
3232
3333 // Used to animate the magnification windows
@@ -320,8 +320,8 @@ void ApplicationCompositor::displayOverlayTextureHmd(RenderArgs* renderArgs, int · 286 unmodified lines
320320 // Only render the hand pointers if the EnableHandMouseInput is enabled
321321 if (Menu::getInstance()->isOptionChecked(MenuOption::EnableHandMouseInput)) {
322322 MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
323− for (int i = 0; i < (int)myAvatar->getHand()->getNumPalms(); i++) {
324− PalmData& palm = myAvatar->getHand()->getPalms()[i];
323+ auto palms = myAvatar->getHand()->getCopyOfPalms();
324+ for (const auto& palm : palms) {
325325 if (palm.isActive()) {
326326 glm::vec2 polar = getPolarCoordinates(palm);
327327 // Convert to quaternion
@@ -446,6 +446,7 @@ void ApplicationCompositor::renderPointers(gpu::Batch& batch) { · 118 unmodified lines
446446 }
447447
448448
449+// FIXME - this is old code that likely needs to be removed and/or reworked to support the new input control model
449450 void ApplicationCompositor::renderControllerPointers(gpu::Batch& batch) {
450451 MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
451452
@@ -455,23 +456,24 @@ void ApplicationCompositor::renderControllerPointers(gpu::Batch& batch) { · 3 unmodified lines
455456 static bool stateWhenPressed[NUMBER_OF_RETICLES] = { false, false, false };
456457
457458 const HandData* handData = DependencyManager::get<AvatarManager>()->getMyAvatar()->getHandData();
459+ auto palms = handData->getCopyOfPalms();
458460
459461 for (unsigned int palmIndex = 2; palmIndex < 4; palmIndex++) {
460462 const int index = palmIndex - 1;
461463
462464 const PalmData* palmData = NULL;
463465
464− if (palmIndex >= handData->getPalms().size()) {
466+ if (palmIndex >= palms.size()) {
465467 return;
466468 }
467469
468− if (handData->getPalms()[palmIndex].isActive()) {
469− palmData = &handData->getPalms()[palmIndex];
470+ if (palms[palmIndex].isActive()) {
471+ palmData = &palms[palmIndex];
470472 } else {
471473 continue;
472474 }
473475
474− int controllerButtons = palmData->getControllerButtons();
476+ int controllerButtons = 0;
475477
476478 //Check for if we should toggle or drag the magnification window
477479 if (controllerButtons & BUTTON_3) {
@@ -521,7 +523,7 @@ void ApplicationCompositor::renderControllerPointers(gpu::Batch& batch) { · 43 unmodified lines
521523 float yAngle = 0.5f - ((atan2f(direction.z, direction.y) + (float)PI_OVER_TWO));
522524
523525 // 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();
525527
526528 mouseX = (canvasSize.x / 2.0f + cursorRange * xAngle);
527529 mouseY = (canvasSize.y / 2.0f + cursorRange * yAngle);
interface/src/ui/PreferencesDialog.cpp+2−2
@@ -198,7 +198,7 @@ void PreferencesDialog::loadPreferences() { · 197 unmodified lines
198198 ui.oculusUIAngularSizeSpin->setValue(qApp->getApplicationCompositor().getHmdUIAngularSize());
199199 #endif
200200
201− ui.sixenseReticleMoveSpeedSpin->setValue(InputDevice::getReticleMoveSpeed());
201+ ui.sixenseReticleMoveSpeedSpin->setValue(controller::InputDevice::getReticleMoveSpeed());
202202
203203 // LOD items
204204 auto lodManager = DependencyManager::get<LODManager>();
@@ -273,7 +273,7 @@ void PreferencesDialog::savePreferences() { · 68 unmodified lines
273273
274274 qApp->getApplicationCompositor().setHmdUIAngularSize(ui.oculusUIAngularSizeSpin->value());
275275
276− InputDevice::setReticleMoveSpeed(ui.sixenseReticleMoveSpeedSpin->value());
276+ controller::InputDevice::setReticleMoveSpeed(ui.sixenseReticleMoveSpeedSpin->value());
277277
278278 auto audio = DependencyManager::get<AudioClient>();
279279 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+
18+QScriptValue AnimVariantMap::animVariantMapToScriptValue(QScriptEngine* engine, const QStringList& names, bool useNames) const {
19+ if (QThread::currentThread() != engine->thread()) {
20+ qCWarning(animation) << "Cannot create Javacript object from non-script thread" << QThread::currentThread();
21+ Q_ASSERT(false);
22+ return QScriptValue();
23+ }
24+ QScriptValue target = engine->newObject();
25+ auto setOne = [&] (const QString& name, const AnimVariant& value) {
26+ switch (value.getType()) {
27+ case AnimVariant::Type::Bool:
28+ target.setProperty(name, value.getBool());
29+ break;
30+ case AnimVariant::Type::Int:
31+ target.setProperty(name, value.getInt());
32+ break;
33+ case AnimVariant::Type::Float:
34+ target.setProperty(name, value.getFloat());
35+ break;
36+ case AnimVariant::Type::String:
37+ target.setProperty(name, value.getString());
38+ break;
39+ case AnimVariant::Type::Vec3:
40+ target.setProperty(name, vec3toScriptValue(engine, value.getVec3()));
41+ break;
42+ case AnimVariant::Type::Quat:
43+ target.setProperty(name, quatToScriptValue(engine, value.getQuat()));
44+ break;
45+ default:
46+ // Note that we don't do mat4 in Javascript currently, and there's not yet a reason to start now.
47+ assert("AnimVariant::Type" == "valid");
48+ }
49+ };
50+ if (useNames) { // copy only the requested names
51+ for (const QString& name : names) {
52+ auto search = _map.find(name);
53+ if (search != _map.end()) { // scripts are allowed to request names that do not exist
54+ setOne(name, search->second);
55+ }
56+ }
57+
58+ } else { // copy all of them
59+ for (auto& pair : _map) {
60+ setOne(pair.first, pair.second);
61+ }
62+ }
63+ return target;
64+}
65+void AnimVariantMap::copyVariantsFrom(const AnimVariantMap& other) {
66+ for (auto& pair : other._map) {
67+ _map[pair.first] = pair.second;
68+ }
69+}
70+
71+void AnimVariantMap::animVariantMapFromScriptValue(const QScriptValue& source) {
72+ if (QThread::currentThread() != source.engine()->thread()) {
73+ qCWarning(animation) << "Cannot examine Javacript object from non-script thread" << QThread::currentThread();
74+ Q_ASSERT(false);
75+ return;
76+ }
77+ // POTENTIAL OPTIMIZATION: cache the types we've seen. I.e, keep a dictionary mapping property names to an enumeration of types.
78+ // Whenever we identify a new outbound type in animVariantMapToScriptValue above, or a new inbound type in the code that follows here,
79+ // we would enter it into the dictionary. Then switch on that type here, with the code that follow being executed only if
80+ // the type is not known. One problem with that is that there is no checking that two different script use the same name differently.
81+ QScriptValueIterator property(source);
82+ // Note: QScriptValueIterator iterates only over source's own properties. It does not follow the prototype chain.
83+ while (property.hasNext()) {
84+ property.next();
85+ QScriptValue value = property.value();
86+ if (value.isBool()) {
87+ set(property.name(), value.toBool());
88+ } else if (value.isString()) {
89+ set(property.name(), value.toString());
90+ } else if (value.isNumber()) {
91+ int asInteger = value.toInt32();
92+ float asFloat = value.toNumber();
93+ if (asInteger == asFloat) {
94+ set(property.name(), asInteger);
95+ } else {
96+ set(property.name(), asFloat);
97+ }
98+ } else { // Try to get x,y,z and possibly w
99+ if (value.isObject()) {
100+ QScriptValue x = value.property("x");
101+ if (x.isNumber()) {
102+ QScriptValue y = value.property("y");
103+ if (y.isNumber()) {
104+ QScriptValue z = value.property("z");
105+ if (z.isNumber()) {
106+ QScriptValue w = value.property("w");
107+ if (w.isNumber()) {
108+ set(property.name(), glm::quat(x.toNumber(), y.toNumber(), z.toNumber(), w.toNumber()));
109+ } else {
110+ set(property.name(), glm::vec3(x.toNumber(), y.toNumber(), z.toNumber()));
111+ }
112+ continue; // we got either a vector or quaternion object, so don't fall through to warning
113+ }
114+ }
115+ }
116+ }
117+ qCWarning(animation) << "Ignoring unrecognized data" << value.toString() << "for animation property" << property.name();
118+ Q_ASSERT(false);
119+ }
120+ }
121+}
libraries/animation/src/AnimVariant.h+17−2
@@ -12,11 +12,14 @@ · 11 unmodified lines
1212 #define hifi_AnimVariant_h
1313
1414 #include <cassert>
15+#include <functional>
1516 #include <glm/glm.hpp>
1617 #include <glm/gtx/quaternion.hpp>
1718 #include <map>
1819 #include <set>
20+#include <QScriptValue>
1921 #include "AnimationLogging.h"
22+#include "StreamUtils.h"
2023
2124 class AnimVariant {
2225 public:
@@ -58,8 +61,9 @@ public: · 35 unmodified lines
5861 void setString(const QString& value) { assert(_type == Type::String); _stringVal = value; }
5962
6063 bool getBool() const { assert(_type == Type::Bool); return _val.boolVal; }
61− int getInt() const { assert(_type == Type::Int); return _val.intVal; }
62− float getFloat() const { assert(_type == Type::Float); return _val.floats[0]; }
64+ int getInt() const { assert(_type == Type::Int || _type == Type::Float); return _type == Type::Float ? (int)_val.floats[0] : _val.intVal; }
65+ float getFloat() const { assert(_type == Type::Float || _type == Type::Int); return _type == Type::Int ? (float)_val.intVal : _val.floats[0]; }
66+
6367 const glm::vec3& getVec3() const { assert(_type == Type::Vec3); return *reinterpret_cast<const glm::vec3*>(&_val); }
6468 const glm::quat& getQuat() const { assert(_type == Type::Quat); return *reinterpret_cast<const glm::quat*>(&_val); }
6569 const glm::mat4& getMat4() const { assert(_type == Type::Mat4); return *reinterpret_cast<const glm::mat4*>(&_val); }
@@ -156,8 +160,15 @@ public: · 90 unmodified lines
156160 void setTrigger(const QString& key) { _triggers.insert(key); }
157161 void clearTriggers() { _triggers.clear(); }
158162
163+ void clearMap() { _map.clear(); }
159164 bool hasKey(const QString& key) const { return _map.find(key) != _map.end(); }
160165
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+
161172 #ifdef NDEBUG
162173 void dump() const {
163174 qCDebug(animation) << "AnimVariantMap =";
@@ -196,4 +207,8 @@ protected: · 32 unmodified lines
196207 std::set<QString> _triggers;
197208 };
198209
210+typedef std::function<void(QScriptValue)> AnimVariantResultHandler;
211+Q_DECLARE_METATYPE(AnimVariantResultHandler);
212+Q_DECLARE_METATYPE(AnimVariantMap)
213+
199214 #endif // hifi_AnimVariant_h
libraries/animation/src/Rig.cpp+67−0
@@ -13,6 +13,7 @@ · 12 unmodified lines
1313
1414 #include <glm/gtx/vector_angle.hpp>
1515 #include <queue>
16+#include <QScriptValueIterator>
1617
1718 #include <NumericalConstants.h>
1819 #include <DebugDraw.h>
@@ -576,6 +577,71 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos · 557 unmodified lines
576577 _lastPosition = worldPosition;
577578 }
578579
580+// Allow script to add/remove handlers and report results, from within their thread.
581+QScriptValue Rig::addAnimationStateHandler(QScriptValue handler, QScriptValue propertiesList) { // called in script thread
582+ QMutexLocker locker(&_stateMutex);
583+ // Find a safe id, even if there are lots of many scripts add and remove handlers repeatedly.
584+ while (!_nextStateHandlerId || _stateHandlers.contains(_nextStateHandlerId)) { // 0 is unused, and don't reuse existing after wrap.
585+ _nextStateHandlerId++;
586+ }
587+ StateHandler& data = _stateHandlers[_nextStateHandlerId];
588+ data.function = handler;
589+ data.useNames = propertiesList.isArray();
590+ if (data.useNames) {
591+ data.propertyNames = propertiesList.toVariant().toStringList();
592+ }
593+ return QScriptValue(_nextStateHandlerId); // suitable for giving to removeAnimationStateHandler
594+}
595+void Rig::removeAnimationStateHandler(QScriptValue identifier) { // called in script thread
596+ QMutexLocker locker(&_stateMutex);
597+ _stateHandlers.remove(identifier.isNumber() ? identifier.toInt32() : 0); // silently continues if handler not present. 0 is unused
598+}
599+void Rig::animationStateHandlerResult(int identifier, QScriptValue result) { // called synchronously from script
600+ QMutexLocker locker(&_stateMutex);
601+ auto found = _stateHandlers.find(identifier);
602+ if (found == _stateHandlers.end()) {
603+ return; // Don't use late-breaking results that got reported after the handler was removed.
604+ }
605+ found.value().results.animVariantMapFromScriptValue(result); // Into our own copy.
606+}
607+
608+void Rig::updateAnimationStateHandlers() { // called on avatar update thread (which may be main thread)
609+ QMutexLocker locker(&_stateMutex);
610+ // It might pay to produce just one AnimVariantMap copy here, with a union of all the requested propertyNames,
611+ // rather than having each callAnimationStateHandler invocation make its own copy.
612+ // However, that copying is done on the script's own time rather than ours, so even if it's less cpu, it would be more
613+ // work on the avatar update thread (which is possibly the main thread).
614+ for (auto data = _stateHandlers.begin(); data != _stateHandlers.end(); data++) {
615+ // call out:
616+ int identifier = data.key();
617+ StateHandler& value = data.value();
618+ QScriptValue& function = value.function;
619+ auto handleResult = [this, identifier](QScriptValue result) { // called in script thread to get the result back to us.
620+ animationStateHandlerResult(identifier, result);
621+ };
622+ // invokeMethod makes a copy of the args, and copies of AnimVariantMap do copy the underlying map, so this will correctly capture
623+ // the state of _animVars and allow continued changes to _animVars in this thread without conflict.
624+ QMetaObject::invokeMethod(function.engine(), "callAnimationStateHandler", Qt::QueuedConnection,
625+ Q_ARG(QScriptValue, function),
626+ Q_ARG(AnimVariantMap, _animVars),
627+ Q_ARG(QStringList, value.propertyNames),
628+ Q_ARG(bool, value.useNames),
629+ Q_ARG(AnimVariantResultHandler, handleResult));
630+ // It turns out that, for thread-safety reasons, ScriptEngine::callAnimationStateHandler will invoke itself if called from other
631+ // than the script thread. Thus the above _could_ be replaced with an ordinary call, which will then trigger the same
632+ // invokeMethod as is done explicitly above. However, the script-engine library depends on this animation library, not vice versa.
633+ // We could create an AnimVariantCallingMixin class in shared, with an abstract virtual slot
634+ // AnimVariantCallingMixin::callAnimationStateHandler (and move AnimVariantMap/AnimVaraintResultHandler to shared), but the
635+ // call site here would look like this instead of the above:
636+ // dynamic_cast<AnimVariantCallingMixin*>(function.engine())->callAnimationStateHandler(function, ..., handleResult);
637+ // This works (I tried it), but the result would be that we would still have same runtime type checks as the invokeMethod above
638+ // (occuring within the ScriptEngine::callAnimationStateHandler invokeMethod trampoline), _plus_ another runtime check for the dynamic_cast.
639+
640+ // gather results in (likely from an earlier update):
641+ _animVars.copyVariantsFrom(value.results); // If multiple handlers write the same anim var, the last registgered wins. (_map preserves order).
642+ }
643+}
644+
579645 void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) {
580646
581647 if (_enableAnimGraph) {
@@ -583,6 +649,7 @@ void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) { · 1 unmodified line
583649 return;
584650 }
585651
652+ updateAnimationStateHandlers();
586653 // evaluate the animation
587654 AnimNode::Triggers triggersOut;
588655 AnimPoseVec poses = _animNode->evaluate(_animVars, deltaTime, triggersOut);
libraries/animation/src/Rig.h+17−0
@@ -37,6 +37,8 @@ · 36 unmodified lines
3737 #define __hifi__Rig__
3838
3939 #include <QObject>
40+#include <QMutex>
41+#include <QScriptValue>
4042
4143 #include "JointState.h" // We might want to change this (later) to something that doesn't depend on gpu, fbx and model. -HRS
4244
@@ -51,6 +53,12 @@ typedef std::shared_ptr<Rig> RigPointer; · 8 unmodified lines
5153
5254 class Rig : public QObject, public std::enable_shared_from_this<Rig> {
5355 public:
56+ struct StateHandler {
57+ AnimVariantMap results;
58+ QStringList propertyNames;
59+ QScriptValue function;
60+ bool useNames;
61+ };
5462
5563 struct HeadParameters {
5664 float leanSideways = 0.0f; // degrees
@@ -199,10 +207,14 @@ public: · 142 unmodified lines
199207 AnimNode::ConstPointer getAnimNode() const { return _animNode; }
200208 AnimSkeleton::ConstPointer getAnimSkeleton() const { return _animSkeleton; }
201209 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);
202213
203214 bool getModelOffset(glm::vec3& modelOffsetOut) const;
204215
205216 protected:
217+ void updateAnimationStateHandlers();
206218
207219 void updateLeanJoint(int index, float leanSideways, float leanForward, float torsoTwist);
208220 void updateNeckJoint(int index, const HeadParameters& params);
@@ -241,6 +253,11 @@ public: · 32 unmodified lines
241253 float _desiredStateAge = 0.0f;
242254 float _leftHandOverlayAlpha = 0.0f;
243255 float _rightHandOverlayAlpha = 0.0f;
256+
257+private:
258+ QMap<int, StateHandler> _stateHandlers;
259+ int _nextStateHandlerId {0};
260+ QMutex _stateMutex;
244261 };
245262
246263 #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
5959 void Sound::downloadFinished(const QByteArray& data) {
6060 // replace our byte array with the downloaded data
6161 QByteArray rawAudioByteArray = QByteArray(data);
62− QString fileName = getURL().fileName();
63−
64− const QString WAV_EXTENSION = ".wav";
62+ QString fileName = getURL().fileName().toLower();
6563
64+ static const QString WAV_EXTENSION = ".wav";
65+ static const QString RAW_EXTENSION = ".raw";
6666 if (fileName.endsWith(WAV_EXTENSION)) {
6767
68− QString headerContentType = "audio/x-wav";
69− //QByteArray headerContentType = reply->rawHeader("Content-Type");
70−
71− // WAV audio file encountered
72− if (headerContentType == "audio/x-wav"
73− || headerContentType == "audio/wav"
74− || headerContentType == "audio/wave"
75− || fileName.endsWith(WAV_EXTENSION)) {
76−
77− QByteArray outputAudioByteArray;
68+ QByteArray outputAudioByteArray;
7869
79− interpretAsWav(rawAudioByteArray, outputAudioByteArray);
80− downSample(outputAudioByteArray);
81− } else {
82− // check if this was a stereo raw file
83− // since it's raw the only way for us to know that is if the file was called .stereo.raw
84− if (fileName.toLower().endsWith("stereo.raw")) {
85− _isStereo = true;
86− qCDebug(audio) << "Processing sound of" << rawAudioByteArray.size() << "bytes from" << getURL() << "as stereo audio file.";
87− }
88−
89− // Process as RAW file
90− downSample(rawAudioByteArray);
70+ interpretAsWav(rawAudioByteArray, outputAudioByteArray);
71+ downSample(outputAudioByteArray);
72+ trimFrames();
73+ } else if (fileName.endsWith(RAW_EXTENSION)) {
74+ // check if this was a stereo raw file
75+ // since it's raw the only way for us to know that is if the file was called .stereo.raw
76+ if (fileName.toLower().endsWith("stereo.raw")) {
77+ _isStereo = true;
78+ qCDebug(audio) << "Processing sound of" << rawAudioByteArray.size() << "bytes from" << getURL() << "as stereo audio file.";
9179 }
80+
81+ // Process as RAW file
82+ downSample(rawAudioByteArray);
9283 trimFrames();
9384 } else {
94− qCDebug(audio) << "Network reply without 'Content-Type'.";
85+ qCDebug(audio) << "Unknown sound file type";
9586 }
9687
9788 _isReady = true;
libraries/avatars/src/HandData.cpp+18−37
@@ -21,62 +21,43 @@ · 20 unmodified lines
2121 HandData::HandData(AvatarData* owningAvatar) :
2222 _owningAvatarData(owningAvatar)
2323 {
24− // Start with two palms
25− addNewPalm();
26− addNewPalm();
24+ addNewPalm(LeftHand);
25+ addNewPalm(RightHand);
2726 }
2827
2928 glm::vec3 HandData::worldToLocalVector(const glm::vec3& worldVector) const {
3029 return glm::inverse(getBaseOrientation()) * worldVector / getBaseScale();
3130 }
3231
33−PalmData& HandData::addNewPalm() {
34− _palms.push_back(PalmData(this));
32+PalmData& HandData::addNewPalm(Hand whichHand) {
33+ QWriteLocker locker(&_palmsLock);
34+ _palms.push_back(PalmData(this, whichHand));
3535 return _palms.back();
3636 }
3737
38−const PalmData* HandData::getPalm(int sixSenseID) const {
39− // the palms are not necessarily added in left-right order,
40− // so we have to search for the right SixSenseID
41− for (unsigned int i = 0; i < _palms.size(); i++) {
42− const PalmData* palm = &(_palms[i]);
43− if (palm->getSixenseID() == sixSenseID) {
44− return palm->isActive() ? palm : NULL;
45− }
46− }
47− return NULL;
48−}
38+PalmData HandData::getCopyOfPalmData(Hand hand) const {
39+ QReadLocker locker(&_palmsLock);
4940
50−void HandData::getLeftRightPalmIndices(int& leftPalmIndex, int& rightPalmIndex) const {
51− leftPalmIndex = -1;
52− rightPalmIndex = -1;
53− for (size_t i = 0; i < _palms.size(); i++) {
54− const PalmData& palm = _palms[i];
55− if (palm.isActive()) {
56− if (palm.getSixenseID() == LEFT_HAND_INDEX) {
57− leftPalmIndex = i;
58− }
59− if (palm.getSixenseID() == RIGHT_HAND_INDEX) {
60− rightPalmIndex = i;
61− }
41+ // the palms are not necessarily added in left-right order,
42+ // so we have to search for the correct hand
43+ for (const auto& palm : _palms) {
44+ if (palm.whichHand() == hand && palm.isActive()) {
45+ return palm;
6246 }
6347 }
48+ return PalmData(); // invalid hand
6449 }
6550
66−PalmData::PalmData(HandData* owningHandData) :
51+PalmData::PalmData(HandData* owningHandData, HandData::Hand hand) :
6752 _rawRotation(0.0f, 0.0f, 0.0f, 1.0f),
6853 _rawPosition(0.0f),
6954 _rawVelocity(0.0f),
7055 _rawAngularVelocity(0.0f),
7156 _totalPenetration(0.0f),
72−_controllerButtons(0),
7357 _isActive(false),
74−_sixenseID(SIXENSEID_INVALID),
7558 _numFramesWithoutData(0),
7659 _owningHandData(owningHandData),
77−_isCollidingWithVoxel(false),
78−_isCollidingWithPalm(false),
79−_collisionlessPaddleExpiry(0) {
60+_hand(hand) {
8061 }
8162
8263 void PalmData::addToPosition(const glm::vec3& delta) {
@@ -85,9 +66,9 @@ void PalmData::addToPosition(const glm::vec3& delta) { · 2 unmodified lines
8566
8667 bool HandData::findSpherePenetration(const glm::vec3& penetratorCenter, float penetratorRadius, glm::vec3& penetration,
8768 const PalmData*& collidingPalm) const {
88−
89− for (size_t i = 0; i < _palms.size(); ++i) {
90− const PalmData& palm = _palms[i];
69+ QReadLocker locker(&_palmsLock);
70+
71+ for (const auto& palm : _palms) {
9172 if (!palm.isActive()) {
9273 continue;
9374 }
libraries/avatars/src/HandData.h+47−52
@@ -12,26 +12,30 @@ · 11 unmodified lines
1212 #ifndef hifi_HandData_h
1313 #define hifi_HandData_h
1414
15+#include <functional>
1516 #include <iostream>
1617 #include <vector>
1718
1819 #include <glm/glm.hpp>
1920 #include <glm/gtc/quaternion.hpp>
2021
22+#include <QReadWriteLock>
23+
2124 #include <NumericalConstants.h>
2225 #include <SharedUtil.h>
2326
2427 class AvatarData;
2528 class PalmData;
2629
27−const int LEFT_HAND_INDEX = 0;
28−const int RIGHT_HAND_INDEX = 1;
29−const int NUM_HANDS = 2;
30−
31−const int SIXENSEID_INVALID = -1;
32−
3330 class HandData {
3431 public:
32+ enum Hand {
33+ LeftHand,
34+ RightHand,
35+ UnknownHand,
36+ NUMBER_OF_HANDS
37+ };
38+
3539 HandData(AvatarData* owningAvatar);
3640 virtual ~HandData() {}
3741
@@ -46,15 +50,9 @@ public: · 8 unmodified lines
4650
4751 glm::vec3 worldToLocalVector(const glm::vec3& worldVector) const;
4852
49− std::vector<PalmData>& getPalms() { return _palms; }
50− const std::vector<PalmData>& getPalms() const { return _palms; }
51− const PalmData* getPalm(int sixSenseID) const;
52− size_t getNumPalms() const { return _palms.size(); }
53− PalmData& addNewPalm();
53+ PalmData getCopyOfPalmData(Hand hand) const;
5454
55− /// Finds the indices of the left and right palms according to their locations, or -1 if either or
56− /// both is not found.
57− void getLeftRightPalmIndices(int& leftPalmIndex, int& rightPalmIndex) const;
55+ std::vector<PalmData> getCopyOfPalms() const { QReadLocker locker(&_palmsLock); return _palms; }
5856
5957 /// Checks for penetration between the described sphere and the hand.
6058 /// \param penetratorCenter the center of the penetration test sphere
@@ -67,14 +65,22 @@ public: · 6 unmodified lines
6765
6866 glm::quat getBaseOrientation() const;
6967
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+
7072 friend class AvatarData;
7173 protected:
7274 AvatarData* _owningAvatarData;
7375 std::vector<PalmData> _palms;
76+ mutable QReadWriteLock _palmsLock{ QReadWriteLock::Recursive };
7477
7578 glm::vec3 getBasePosition() const;
7679 float getBaseScale() const;
77−
80+
81+ PalmData& addNewPalm(Hand whichHand);
82+ PalmData& getPalmData(Hand hand);
83+
7884 private:
7985 // privatize copy ctor and assignment operator so copies of this object cannot be made
8086 HandData(const HandData&);
@@ -84,25 +90,30 @@ private: · 3 unmodified lines
8490
8591 class PalmData {
8692 public:
87− PalmData(HandData* owningHandData);
93+ PalmData(HandData* owningHandData = nullptr, HandData::Hand hand = HandData::UnknownHand);
8894 glm::vec3 getPosition() const { return _owningHandData->localToWorldPosition(_rawPosition); }
8995 glm::vec3 getVelocity() const { return _owningHandData->localToWorldDirection(_rawVelocity); }
9096
9197 const glm::vec3& getRawPosition() const { return _rawPosition; }
9298 bool isActive() const { return _isActive; }
93− int getSixenseID() const { return _sixenseID; }
99+ bool isValid() const { return _owningHandData; }
94100
95101 void setActive(bool active) { _isActive = active; }
96− void setSixenseID(int id) { _sixenseID = id; }
97102
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; };
99107 glm::quat getRawRotation() const { return _rawRotation; }
100108 glm::quat getRotation() const { return _owningHandData->getBaseOrientation() * _rawRotation; }
101109 void setRawPosition(const glm::vec3& pos) { _rawPosition = pos; }
102110 void setRawVelocity(const glm::vec3& velocity) { _rawVelocity = velocity; }
103111 const glm::vec3& getRawVelocity() const { return _rawVelocity; }
112+
104113 void setRawAngularVelocity(const glm::vec3& angularVelocity) { _rawAngularVelocity = angularVelocity; }
105114 const glm::vec3& getRawAngularVelocity() const { return _rawAngularVelocity; }
115+ glm::quat getRawAngularVelocityAsQuat() const { return glm::quat(_rawAngularVelocity); }
116+
106117 void addToPosition(const glm::vec3& delta);
107118
108119 void addToPenetration(const glm::vec3& penetration) { _totalPenetration += penetration; }
@@ -120,31 +131,11 @@ public: · 11 unmodified lines
120131 void resetFramesWithoutData() { _numFramesWithoutData = 0; }
121132 int getFramesWithoutData() const { return _numFramesWithoutData; }
122133
123− // Controller buttons
124− void setControllerButtons(unsigned int controllerButtons) { _controllerButtons = controllerButtons; }
125− void setLastControllerButtons(unsigned int controllerButtons) { _lastControllerButtons = controllerButtons; }
126−
127− unsigned int getControllerButtons() const { return _controllerButtons; }
128− unsigned int getLastControllerButtons() const { return _lastControllerButtons; }
129−
134+ // FIXME - these are used in SkeletonModel::updateRig() the skeleton/rig should probably get this information
135+ // from an action and/or the UserInputMapper instead of piping it through here.
130136 void setTrigger(float trigger) { _trigger = trigger; }
131137 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; }
135138
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−
148139 // return world-frame:
149140 glm::vec3 getFingerDirection() const;
150141 glm::vec3 getNormal() const;
@@ -155,25 +146,29 @@ private: · 4 unmodified lines
155146 glm::vec3 _rawPosition;
156147 glm::vec3 _rawVelocity;
157148 glm::vec3 _rawAngularVelocity;
149+ glm::quat _rawDeltaRotation;
158150 glm::quat _lastRotation;
159151
160152 glm::vec3 _tipPosition;
161153 glm::vec3 _tipVelocity;
162− glm::vec3 _totalPenetration; // accumulator for per-frame penetrations
154+ glm::vec3 _totalPenetration; /// accumulator for per-frame penetrations
163155
164− unsigned int _controllerButtons;
165− unsigned int _lastControllerButtons;
166156 float _trigger;
167− float _joystickX, _joystickY;
168157
169− bool _isActive; // This has current valid data
170− int _sixenseID; // Sixense controller ID for this palm
171− int _numFramesWithoutData; // after too many frames without data, this tracked object assumed lost.
158+ bool _isActive; /// This has current valid data
159+ int _numFramesWithoutData; /// after too many frames without data, this tracked object assumed lost.
172160 HandData* _owningHandData;
173−
174− bool _isCollidingWithVoxel; /// Whether the finger of this palm is inside a leaf voxel
175− bool _isCollidingWithPalm;
176− quint64 _collisionlessPaddleExpiry; /// Timestamp after which paddle starts colliding
161+ HandData::Hand _hand;
177162 };
178163
164+template<typename PalmModifierFunction> void HandData::modifyPalm(Hand whichHand, PalmModifierFunction callback) {
165+ QReadLocker locker(&_palmsLock);
166+ for (auto& palm : _palms) {
167+ if (palm.whichHand() == whichHand && palm.isValid()) {
168+ callback(palm);
169+ return;
170+ }
171+ }
172+}
173+
179174 #endif // hifi_HandData_h
libraries/controllers/CMakeLists.txt+14−0
@@ -0,0 +1,14 @@
1+set(TARGET_NAME controllers)
2+
3+# set a default root dir for each of our optional externals if it was not passed
4+setup_hifi_library(Script)
5+
6+# use setup_hifi_library macro to setup our project and link appropriate Qt modules
7+link_hifi_libraries(shared)
8+
9+GroupSources("src/controllers")
10+
11+add_dependency_external_projects(glm)
12+find_package(GLM REQUIRED)
13+target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS})
14+
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+
13+namespace controller {
14+
15+ Input::NamedPair makePair(ChannelType type, Action action, const QString& name) {
16+ auto input = Input(UserInputMapper::ACTIONS_DEVICE, toInt(action), type);
17+ return Input::NamedPair(input, name);
18+ }
19+
20+ Input::NamedPair makeAxisPair(Action action, const QString& name) {
21+ return makePair(ChannelType::AXIS, action, name);
22+ }
23+
24+ Input::NamedPair makeButtonPair(Action action, const QString& name) {
25+ return makePair(ChannelType::BUTTON, action, name);
26+ }
27+
28+ Input::NamedPair makePosePair(Action action, const QString& name) {
29+ return makePair(ChannelType::POSE, action, name);
30+ }
31+
32+ EndpointPointer ActionsDevice::createEndpoint(const Input& input) const {
33+ return std::make_shared<ActionEndpoint>(input);
34+ }
35+
36+ // Device functions
37+ Input::NamedVector ActionsDevice::getAvailableInputs() const {
38+ static Input::NamedVector availableInputs {
39+ makeAxisPair(Action::TRANSLATE_X, "TranslateX"),
40+ makeAxisPair(Action::TRANSLATE_Y, "TranslateY"),
41+ makeAxisPair(Action::TRANSLATE_Z, "TranslateZ"),
42+ makeAxisPair(Action::ROLL, "Roll"),
43+ makeAxisPair(Action::PITCH, "Pitch"),
44+ makeAxisPair(Action::YAW, "Yaw"),
45+ makeAxisPair(Action::STEP_YAW, "StepYaw"),
46+ makeAxisPair(Action::STEP_PITCH, "StepPitch"),
47+ makeAxisPair(Action::STEP_ROLL, "StepRoll"),
48+ makeAxisPair(Action::STEP_TRANSLATE_X, "StepTranslateX"),
49+ makeAxisPair(Action::STEP_TRANSLATE_X, "StepTranslateY"),
50+ makeAxisPair(Action::STEP_TRANSLATE_X, "StepTranslateZ"),
51+
52+ makePosePair(Action::LEFT_HAND, "LeftHand"),
53+ makePosePair(Action::RIGHT_HAND, "RightHand"),
54+
55+ makeButtonPair(Action::LEFT_HAND_CLICK, "LeftHandClick"),
56+ makeButtonPair(Action::RIGHT_HAND_CLICK, "RightHandClick"),
57+
58+ makeButtonPair(Action::SHIFT, "Shift"),
59+ makeButtonPair(Action::ACTION1, "PrimaryAction"),
60+ makeButtonPair(Action::ACTION2, "SecondaryAction"),
61+ makeButtonPair(Action::CONTEXT_MENU, "ContextMenu"),
62+ makeButtonPair(Action::TOGGLE_MUTE, "ToggleMute"),
63+
64+ // Aliases and bisected versions
65+ makeAxisPair(Action::LONGITUDINAL_BACKWARD, "Backward"),
66+ makeAxisPair(Action::LONGITUDINAL_FORWARD, "Forward"),
67+ makeAxisPair(Action::LATERAL_LEFT, "StrafeLeft"),
68+ makeAxisPair(Action::LATERAL_RIGHT, "StrafeRight"),
69+ makeAxisPair(Action::VERTICAL_DOWN, "Down"),
70+ makeAxisPair(Action::VERTICAL_UP, "Up"),
71+ makeAxisPair(Action::YAW_LEFT, "YawLeft"),
72+ makeAxisPair(Action::YAW_RIGHT, "YawRight"),
73+ makeAxisPair(Action::PITCH_DOWN, "PitchDown"),
74+ makeAxisPair(Action::PITCH_UP, "PitchUp"),
75+ makeAxisPair(Action::BOOM_IN, "BoomIn"),
76+ makeAxisPair(Action::BOOM_OUT, "BoomOut"),
77+
78+ // Deprecated aliases
79+ // FIXME remove after we port all scripts
80+ makeAxisPair(Action::LONGITUDINAL_BACKWARD, "LONGITUDINAL_BACKWARD"),
81+ makeAxisPair(Action::LONGITUDINAL_FORWARD, "LONGITUDINAL_FORWARD"),
82+ makeAxisPair(Action::LATERAL_LEFT, "LATERAL_LEFT"),
83+ makeAxisPair(Action::LATERAL_RIGHT, "LATERAL_RIGHT"),
84+ makeAxisPair(Action::VERTICAL_DOWN, "VERTICAL_DOWN"),
85+ makeAxisPair(Action::VERTICAL_UP, "VERTICAL_UP"),
86+ makeAxisPair(Action::YAW_LEFT, "YAW_LEFT"),
87+ makeAxisPair(Action::YAW_RIGHT, "YAW_RIGHT"),
88+ makeAxisPair(Action::PITCH_DOWN, "PITCH_DOWN"),
89+ makeAxisPair(Action::PITCH_UP, "PITCH_UP"),
90+ makeAxisPair(Action::BOOM_IN, "BOOM_IN"),
91+ makeAxisPair(Action::BOOM_OUT, "BOOM_OUT"),
92+
93+ makePosePair(Action::LEFT_HAND, "LEFT_HAND"),
94+ makePosePair(Action::RIGHT_HAND, "RIGHT_HAND"),
95+
96+ makeButtonPair(Action::LEFT_HAND_CLICK, "LEFT_HAND_CLICK"),
97+ makeButtonPair(Action::RIGHT_HAND_CLICK, "RIGHT_HAND_CLICK"),
98+
99+ makeButtonPair(Action::SHIFT, "SHIFT"),
100+ makeButtonPair(Action::ACTION1, "ACTION1"),
101+ makeButtonPair(Action::ACTION2, "ACTION2"),
102+ makeButtonPair(Action::CONTEXT_MENU, "CONTEXT_MENU"),
103+ makeButtonPair(Action::TOGGLE_MUTE, "TOGGLE_MUTE"),
104+ };
105+ return availableInputs;
106+ }
107+
108+ void ActionsDevice::update(float deltaTime, bool jointsCaptured) {
109+ }
110+
111+ void ActionsDevice::focusOutEvent() {
112+ }
113+
114+ ActionsDevice::ActionsDevice() : InputDevice("Actions") {
115+ _deviceID = UserInputMapper::ACTIONS_DEVICE;
116+ }
117+
118+ ActionsDevice::~ActionsDevice() {}
119+
120+}
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+
18+namespace controller {
19+
20+// Actions are the output channels of the Mapper, that's what the InputChannel map to
21+// For now the Actions are hardcoded, this is bad, but we will fix that in the near future
22+enum class Action {
23+ TRANSLATE_X = 0,
24+ TRANSLATE_Y,
25+ TRANSLATE_Z,
26+ ROTATE_X, PITCH = ROTATE_X,
27+ ROTATE_Y, YAW = ROTATE_Y,
28+ ROTATE_Z, ROLL = ROTATE_Z,
29+
30+ STEP_YAW,
31+ // FIXME does this have a use case?
32+ STEP_PITCH,
33+ // FIXME does this have a use case?
34+ STEP_ROLL,
35+
36+ STEP_TRANSLATE_X,
37+ STEP_TRANSLATE_Y,
38+ STEP_TRANSLATE_Z,
39+
40+ TRANSLATE_CAMERA_Z,
41+ NUM_COMBINED_AXES,
42+
43+ LEFT_HAND = NUM_COMBINED_AXES,
44+ RIGHT_HAND,
45+
46+ LEFT_HAND_CLICK,
47+ RIGHT_HAND_CLICK,
48+
49+ ACTION1,
50+ ACTION2,
51+
52+ CONTEXT_MENU,
53+ TOGGLE_MUTE,
54+
55+ SHIFT,
56+
57+ // Biseced aliases for TRANSLATE_Z
58+ LONGITUDINAL_BACKWARD,
59+ LONGITUDINAL_FORWARD,
60+
61+ // Biseced aliases for TRANSLATE_X
62+ LATERAL_LEFT,
63+ LATERAL_RIGHT,
64+
65+ // Biseced aliases for TRANSLATE_Y
66+ VERTICAL_DOWN,
67+ VERTICAL_UP,
68+
69+ // Biseced aliases for ROTATE_Y
70+ YAW_LEFT,
71+ YAW_RIGHT,
72+
73+ // Biseced aliases for ROTATE_X
74+ PITCH_DOWN,
75+ PITCH_UP,
76+
77+ // Biseced aliases for TRANSLATE_CAMERA_Z
78+ BOOM_IN,
79+ BOOM_OUT,
80+
81+ NUM_ACTIONS,
82+};
83+
84+template <typename T>
85+int toInt(T enumValue) { return static_cast<int>(enumValue); }
86+
87+class ActionsDevice : public QObject, public InputDevice {
88+ Q_OBJECT
89+ Q_PROPERTY(QString name READ getName)
90+
91+public:
92+ virtual EndpointPointer createEndpoint(const Input& input) const override;
93+ virtual Input::NamedVector getAvailableInputs() const override;
94+ virtual void update(float deltaTime, bool jointsCaptured) override;
95+ virtual void focusOutEvent() override;
96+
97+ ActionsDevice();
98+ virtual ~ActionsDevice();
99+};
100+
101+}
102+
103+#endif // hifi_StandardController_h
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+
12+namespace 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+
22+namespace controller {
23+ /*
24+ using Modifiers = std::vector<Input>;
25+ typedef QPair<Input, QString> InputPair;
26+ class Endpoint;
27+ using EndpointPtr = std::shared_ptr<Endpoint>;
28+
29+ template<typename T>
30+ using InputGetter = std::function<T(const Input& input, int timestamp)>;
31+ using ButtonGetter = InputGetter<bool>;
32+ using AxisGetter = InputGetter<float>;
33+ using PoseGetter = InputGetter<Pose>;
34+ using ResetBindings = std::function<bool()>;
35+ using AvailableInputGetter = std::function<Input::NamedVector()>;
36+ using EndpointCreator = std::function<EndpointPtr(const Input&)>;
37+
38+ class DeviceProxy {
39+ public:
40+ using Pointer = std::shared_ptr<DeviceProxy>;
41+
42+ QString _name;
43+ };
44+ */
45+}
46+
47+#endif
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+
13+namespace controller {
14+
15+class Endpoint;
16+using EndpointPointer = std::shared_ptr<Endpoint>;
17+using EndpointList = std::list<EndpointPointer>;
18+
19+class Filter;
20+using FilterPointer = std::shared_ptr<Filter>;
21+using FilterList = std::list<FilterPointer>;
22+
23+class Route;
24+using RoutePointer = std::shared_ptr<Route>;
25+using RouteList = std::list<RoutePointer>;
26+
27+class Conditional;
28+using ConditionalPointer = std::shared_ptr<Conditional>;
29+using ConditionalList = std::list<ConditionalPointer>;
30+
31+class Mapping;
32+using MappingPointer = std::shared_ptr<Mapping>;
33+using MappingList = std::list<MappingPointer>;
34+
35+}
36+
37+#endif
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+
11+namespace controller {
12+ const Input Input::INVALID_INPUT = Input(0x7fffffff);
13+ const uint16_t Input::INVALID_DEVICE = Input::INVALID_INPUT.device;
14+ const uint16_t Input::INVALID_CHANNEL = Input::INVALID_INPUT.channel;
15+ const uint16_t Input::INVALID_TYPE = Input::INVALID_INPUT.type;
16+}
17+
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+
16+namespace controller {
17+
18+enum class ChannelType {
19+ UNKNOWN = 0,
20+ BUTTON,
21+ AXIS,
22+ POSE,
23+ RUMBLE,
24+ INVALID = 0x7
25+};
26+
27+// Input is the unique identifier to find a n input channel of a particular device
28+// Devices are responsible for registering to the UseInputMapper so their input channels can be sued and mapped
29+// to the Action channels
30+struct Input {
31+ union {
32+ uint32_t id{ 0 }; // by default Input is 0 meaning invalid
33+ struct {
34+ uint16_t device; // Up to 64K possible devices
35+ uint16_t channel : 12 ; // 2^12 possible channel per Device
36+ uint16_t type : 3; // 2 bits to store the Type directly in the ID
37+ uint16_t padding : 1; // 2 bits to store the Type directly in the ID
38+ };
39+ };
40+
41+ bool isValid() const { return (id != INVALID_INPUT.id); }
42+
43+ uint16_t getDevice() const { return device; }
44+ uint16_t getChannel() const { return channel; }
45+ uint32_t getID() const { return id; }
46+ ChannelType getType() const { return (ChannelType) type; }
47+
48+ bool isButton() const { return getType() == ChannelType::BUTTON; }
49+ bool isAxis() const { return getType() == ChannelType::AXIS; }
50+ bool isPose() const { return getType() == ChannelType::POSE; }
51+
52+ // WORKAROUND: the explicit initializer here avoids a bug in GCC-4.8.2 (but not found in 4.9.2)
53+ // where the default initializer (a C++-11ism) for the union data above is not applied.
54+ explicit Input() {}
55+ explicit Input(uint32_t id) : id(id) {}
56+ explicit Input(uint16_t device, uint16_t channel, ChannelType type) : device(device), channel(channel), type(uint16_t(type)), padding(0) {}
57+ Input(const Input& src) : id(src.id) {}
58+ Input& operator = (const Input& src) { id = src.id; return (*this); }
59+ bool operator ==(const Input& right) const { return INVALID_INPUT.id != id && INVALID_INPUT.id != right.id && id == right.id; }
60+ bool operator !=(const Input& right) const { return !(*this == right); }
61+ bool operator < (const Input& src) const { return id < src.id; }
62+
63+ static const Input INVALID_INPUT;
64+ static const uint16_t INVALID_DEVICE;
65+ static const uint16_t INVALID_CHANNEL;
66+ static const uint16_t INVALID_TYPE;
67+
68+ using NamedPair = QPair<Input, QString>;
69+ using NamedVector = QVector<NamedPair>;
70+};
71+
72+}
73+
74+#endif
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+
16+namespace controller {
17+
18+ bool InputDevice::_lowVelocityFilter = false;
19+
20+ const float DEFAULT_HAND_RETICLE_MOVE_SPEED = 37.5f;
21+ float InputDevice::_reticleMoveSpeed = DEFAULT_HAND_RETICLE_MOVE_SPEED;
22+
23+ //Constants for getCursorPixelRangeMultiplier()
24+ const float MIN_PIXEL_RANGE_MULT = 0.4f;
25+ const float MAX_PIXEL_RANGE_MULT = 2.0f;
26+ const float RANGE_MULT = (MAX_PIXEL_RANGE_MULT - MIN_PIXEL_RANGE_MULT) * 0.01f;
27+
28+ //Returns a multiplier to be applied to the cursor range for the controllers
29+ float InputDevice::getCursorPixelRangeMult() {
30+ //scales (0,100) to (MINIMUM_PIXEL_RANGE_MULT, MAXIMUM_PIXEL_RANGE_MULT)
31+ return InputDevice::_reticleMoveSpeed * RANGE_MULT + MIN_PIXEL_RANGE_MULT;
32+ }
33+
34+ float InputDevice::getButton(int channel) const {
35+ if (!_buttonPressedMap.empty()) {
36+ if (_buttonPressedMap.find(channel) != _buttonPressedMap.end()) {
37+ return 1.0f;
38+ } else {
39+ return 0.0f;
40+ }
41+ }
42+ return 0.0f;
43+ }
44+
45+ float InputDevice::getAxis(int channel) const {
46+ auto axis = _axisStateMap.find(channel);
47+ if (axis != _axisStateMap.end()) {
48+ return (*axis).second;
49+ } else {
50+ return 0.0f;
51+ }
52+ }
53+
54+ Pose InputDevice::getPose(int channel) const {
55+ auto pose = _poseStateMap.find(channel);
56+ if (pose != _poseStateMap.end()) {
57+ return (*pose).second;
58+ } else {
59+ return Pose();
60+ }
61+ }
62+
63+ Input InputDevice::makeInput(controller::StandardButtonChannel button) const {
64+ return Input(_deviceID, button, ChannelType::BUTTON);
65+ }
66+
67+ Input InputDevice::makeInput(controller::StandardAxisChannel axis) const {
68+ return Input(_deviceID, axis, ChannelType::AXIS);
69+ }
70+
71+ Input InputDevice::makeInput(controller::StandardPoseChannel pose) const {
72+ return Input(_deviceID, pose, ChannelType::POSE);
73+ }
74+
75+ Input::NamedPair InputDevice::makePair(controller::StandardButtonChannel button, const QString& name) const {
76+ return Input::NamedPair(makeInput(button), name);
77+ }
78+
79+ Input::NamedPair InputDevice::makePair(controller::StandardAxisChannel axis, const QString& name) const {
80+ return Input::NamedPair(makeInput(axis), name);
81+ }
82+
83+ Input::NamedPair InputDevice::makePair(controller::StandardPoseChannel pose, const QString& name) const {
84+ return Input::NamedPair(makeInput(pose), name);
85+ }
86+
87+ float InputDevice::getValue(ChannelType channelType, uint16_t channel) const {
88+ switch (channelType) {
89+ case ChannelType::AXIS:
90+ return getAxis(channel);
91+
92+ case ChannelType::BUTTON:
93+ return getButton(channel);
94+
95+ case ChannelType::POSE:
96+ return getPose(channel).valid ? 1.0f : 0.0f;
97+
98+ default:
99+ break;
100+ }
101+
102+ return 0.0f;
103+ }
104+
105+
106+ float InputDevice::getValue(const Input& input) const {
107+ return getValue(input.getType(), input.channel);
108+ }
109+
110+ EndpointPointer InputDevice::createEndpoint(const Input& input) const {
111+ return std::make_shared<InputEndpoint>(input);
112+ }
113+
114+}
libraries/controllers/src/controllers/InputDevice.hrenamed from libraries/input-plugins/src/input-plugins/InputDevice.h+46−11
@@ -10,12 +10,27 @@ · 9 unmodified lines
1010 //
1111 #pragma once
1212
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+
1424
1525 // Event types for each controller
1626 const unsigned int CONTROLLER_0_EVENT = 1500U;
1727 const unsigned int CONTROLLER_1_EVENT = 1501U;
1828
29+namespace controller {
30+
31+class Endpoint;
32+using EndpointPointer = std::shared_ptr<Endpoint>;
33+
1934 // NOTE: If something inherits from both InputDevice and InputPlugin, InputPlugin must go first.
2035 // e.g. class Example : public InputPlugin, public InputDevice
2136 // instead of class Example : public InputDevice, public InputPlugin
@@ -23,17 +38,22 @@ class InputDevice { · 1 unmodified line
2338 public:
2439 InputDevice(const QString& name) : _name(name) {}
2540
41+ using Pointer = std::shared_ptr<InputDevice>;
42+
2643 typedef std::unordered_set<int> ButtonPressedMap;
2744 typedef std::map<int, float> AxisStateMap;
28− typedef std::map<int, UserInputMapper::PoseValue> PoseStateMap;
45+ typedef std::map<int, Pose> PoseStateMap;
2946
3047 // Get current state for each channel
3148 float getButton(int channel) const;
3249 float getAxis(int channel) const;
33− UserInputMapper::PoseValue getPose(int channel) const;
50+ Pose getPose(int channel) const;
3451
35− virtual void registerToUserInputMapper(UserInputMapper& mapper) = 0;
36− virtual void assignDefaultInputMapping(UserInputMapper& mapper) = 0;
52+ float getValue(const Input& input) const;
53+ float getValue(ChannelType channelType, uint16_t channel) const;
54+ Pose getPoseValue(uint16_t channel) const;
55+
56+ const QString& getName() const { return _name; }
3757
3858 // Update call MUST be called once per simulation loop
3959 // It takes care of updating the action states and deltas
@@ -42,20 +62,33 @@ public: · 2 unmodified lines
4262 virtual void focusOutEvent() = 0;
4363
4464 int getDeviceID() { return _deviceID; }
65+ void setDeviceID(int deviceID) { _deviceID = deviceID; }
4566
4667 static float getCursorPixelRangeMult();
47− static float getReticleMoveSpeed() { return reticleMoveSpeed; }
48− static void setReticleMoveSpeed(float sixenseReticleMoveSpeed) { reticleMoveSpeed = sixenseReticleMoveSpeed; }
68+ static float getReticleMoveSpeed() { return _reticleMoveSpeed; }
69+ static void setReticleMoveSpeed(float reticleMoveSpeed) { _reticleMoveSpeed = reticleMoveSpeed; }
4970
5071 static bool getLowVelocityFilter() { return _lowVelocityFilter; };
5172
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;
5279 public slots:
5380 static void setLowVelocityFilter(bool newLowVelocityFilter) { _lowVelocityFilter = newLowVelocityFilter; };
5481
5582 protected:
56− int _deviceID = 0;
83+ friend class UserInputMapper;
84+
85+ virtual Input::NamedVector getAvailableInputs() const = 0;
86+ virtual QString getDefaultMappingConfig() const { return QString(); }
87+ virtual EndpointPointer createEndpoint(const Input& input) const;
5788
58− QString _name;
89+ uint16_t _deviceID { Input::INVALID_DEVICE };
90+
91+ const QString _name;
5992
6093 ButtonPressedMap _buttonPressedMap;
6194 AxisStateMap _axisStateMap;
@@ -64,5 +97,7 @@ protected: · 2 unmodified lines
6497 static bool _lowVelocityFilter;
6598
6699 private:
67− static float reticleMoveSpeed;
68−};
100+ static float _reticleMoveSpeed;
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+
11+Q_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+
14+Q_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+
16+namespace controller {
17+
18+ Pose::Pose(const vec3& translation, const quat& rotation,
19+ const vec3& velocity, const quat& angularVelocity) :
20+ translation(translation), rotation(rotation), velocity(velocity), angularVelocity(angularVelocity), valid (true) { }
21+
22+ bool Pose::operator==(const Pose& right) const {
23+ // invalid poses return false for comparison, even against identical invalid poses, like NaN
24+ if (!valid || !right.valid) {
25+ return false;
26+ }
27+
28+ // FIXME add margin of error? Or add an additional withinEpsilon function?
29+ return translation == right.getTranslation() && rotation == right.getRotation() &&
30+ velocity == right.getVelocity() && angularVelocity == right.getAngularVelocity();
31+ }
32+
33+ QScriptValue Pose::toScriptValue(QScriptEngine* engine, const Pose& pose) {
34+ QScriptValue obj = engine->newObject();
35+ obj.setProperty("translation", vec3toScriptValue(engine, pose.translation));
36+ obj.setProperty("rotation", quatToScriptValue(engine, pose.rotation));
37+ obj.setProperty("velocity", vec3toScriptValue(engine, pose.velocity));
38+ obj.setProperty("angularVelocity", quatToScriptValue(engine, pose.angularVelocity));
39+ obj.setProperty("valid", pose.valid);
40+
41+ return obj;
42+ }
43+
44+ void Pose::fromScriptValue(const QScriptValue& object, Pose& pose) {
45+ // nothing for now...
46+ }
47+
48+}
49+
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+
14+class QScriptEngine;
15+class QScriptValue;
16+
17+#include <GLMHelpers.h>
18+
19+namespace controller {
20+
21+ struct Pose {
22+ public:
23+ vec3 translation;
24+ quat rotation;
25+ vec3 velocity;
26+ quat angularVelocity;
27+ bool valid{ false };
28+
29+ Pose() {}
30+ Pose(const vec3& translation, const quat& rotation,
31+ const vec3& velocity = vec3(), const quat& angularVelocity = quat());
32+
33+ Pose(const Pose&) = default;
34+ Pose& operator = (const Pose&) = default;
35+ bool operator ==(const Pose& right) const;
36+ bool operator !=(const Pose& right) const { return !(*this == right); }
37+ bool isValid() const { return valid; }
38+ vec3 getTranslation() const { return translation; }
39+ quat getRotation() const { return rotation; }
40+ vec3 getVelocity() const { return velocity; }
41+ quat getAngularVelocity() const { return angularVelocity; }
42+
43+ static QScriptValue toScriptValue(QScriptEngine* engine, const Pose& event);
44+ static void fromScriptValue(const QScriptValue& object, Pose& event);
45+ };
46+}
47+
48+//Q_DECLARE_METATYPE(controller::Pose);
49+
50+#endif
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+
28+static QRegularExpression SANITIZE_NAME_EXPRESSION{ "[\\(\\)\\.\\s]" };
29+
30+static QVariantMap createDeviceMap(const controller::InputDevice::Pointer device) {
31+ auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
32+ QVariantMap deviceMap;
33+ for (const auto& inputMapping : userInputMapper->getAvailableInputs(device->getDeviceID())) {
34+ const auto& input = inputMapping.first;
35+ const auto inputName = QString(inputMapping.second).remove(SANITIZE_NAME_EXPRESSION);
36+ qCDebug(controllers) << "\tInput " << input.getChannel() << (int)input.getType()
37+ << QString::number(input.getID(), 16) << ": " << inputName;
38+ deviceMap.insert(inputName, input.getID());
39+ }
40+ return deviceMap;
41+}
42+
43+// FIXME this throws a hissy fit on MSVC if I put it in the main controller namespace block
44+controller::ScriptingInterface::ScriptingInterface() {
45+ auto userInputMapper = DependencyManager::get<UserInputMapper>();
46+
47+ connect(userInputMapper.data(), &UserInputMapper::actionEvent, this, &controller::ScriptingInterface::actionEvent);
48+ connect(userInputMapper.data(), &UserInputMapper::inputEvent, this, &controller::ScriptingInterface::inputEvent);
49+
50+ // FIXME make this thread safe
51+ connect(userInputMapper.data(), &UserInputMapper::hardwareChanged, this, [=] {
52+ updateMaps();
53+ emit hardwareChanged();
54+ });
55+
56+ qCDebug(controllers) << "Setting up standard controller abstraction";
57+ _standard = createDeviceMap(userInputMapper->getStandardDevice());
58+
59+ // FIXME allow custom user actions?
60+ auto actionNames = userInputMapper->getActionNames();
61+ qCDebug(controllers) << "Setting up standard actions";
62+ for (const auto& namedInput : userInputMapper->getActionInputs()) {
63+ const QString& actionName = namedInput.second;
64+ const Input& actionInput = namedInput.first;
65+ qCDebug(controllers) << "\tAction: " << actionName << " " << actionInput.getChannel();
66+
67+ // Expose the IDs to JS
68+ QString cleanActionName = QString(actionName).remove(SANITIZE_NAME_EXPRESSION);
69+ _actions.insert(cleanActionName, actionInput.getID());
70+ }
71+
72+ updateMaps();
73+}
74+
75+namespace controller {
76+
77+ QObject* ScriptingInterface::newMapping(const QString& mappingName) {
78+ auto userInputMapper = DependencyManager::get<UserInputMapper>();
79+ return new MappingBuilderProxy(*userInputMapper, userInputMapper->newMapping(mappingName));
80+ }
81+
82+ void ScriptingInterface::enableMapping(const QString& mappingName, bool enable) {
83+ auto userInputMapper = DependencyManager::get<UserInputMapper>();
84+ userInputMapper->enableMapping(mappingName, enable);
85+ }
86+
87+ float ScriptingInterface::getValue(const int& source) const {
88+ auto userInputMapper = DependencyManager::get<UserInputMapper>();
89+ return userInputMapper->getValue(Input((uint32_t)source));
90+ }
91+
92+ float ScriptingInterface::getButtonValue(StandardButtonChannel source, uint16_t device) const {
93+ return getValue(Input(device, source, ChannelType::BUTTON).getID());
94+ }
95+
96+ float ScriptingInterface::getAxisValue(StandardAxisChannel source, uint16_t device) const {
97+ return getValue(Input(device, source, ChannelType::AXIS).getID());
98+ }
99+
100+ Pose ScriptingInterface::getPoseValue(const int& source) const {
101+ auto userInputMapper = DependencyManager::get<UserInputMapper>();
102+ return userInputMapper->getPose(Input((uint32_t)source));
103+ }
104+
105+ Pose ScriptingInterface::getPoseValue(StandardPoseChannel source, uint16_t device) const {
106+ return getPoseValue(Input(device, source, ChannelType::POSE).getID());
107+ }
108+
109+ //bool ScriptingInterface::isPrimaryButtonPressed() const {
110+ // return isButtonPressed(StandardButtonChannel::A);
111+ //}
112+ //
113+ //glm::vec2 ScriptingInterface::getPrimaryJoystickPosition() const {
114+ // return getJoystickPosition(0);
115+ //}
116+
117+ //int ScriptingInterface::getNumberOfButtons() const {
118+ // return StandardButtonChannel::NUM_STANDARD_BUTTONS;
119+ //}
120+
121+ //bool ScriptingInterface::isButtonPressed(int buttonIndex) const {
122+ // return getButtonValue((StandardButtonChannel)buttonIndex) == 0.0 ? false : true;
123+ //}
124+
125+ //int ScriptingInterface::getNumberOfTriggers() const {
126+ // return StandardCounts::TRIGGERS;
127+ //}
128+
129+ //float ScriptingInterface::getTriggerValue(int triggerIndex) const {
130+ // return getAxisValue(triggerIndex == 0 ? StandardAxisChannel::LT : StandardAxisChannel::RT);
131+ //}
132+
133+ //int ScriptingInterface::getNumberOfJoysticks() const {
134+ // return StandardCounts::ANALOG_STICKS;
135+ //}
136+
137+ //glm::vec2 ScriptingInterface::getJoystickPosition(int joystickIndex) const {
138+ // StandardAxisChannel xid = StandardAxisChannel::LX;
139+ // StandardAxisChannel yid = StandardAxisChannel::LY;
140+ // if (joystickIndex != 0) {
141+ // xid = StandardAxisChannel::RX;
142+ // yid = StandardAxisChannel::RY;
143+ // }
144+ // vec2 result;
145+ // result.x = getAxisValue(xid);
146+ // result.y = getAxisValue(yid);
147+ // return result;
148+ //}
149+
150+ //int ScriptingInterface::getNumberOfSpatialControls() const {
151+ // return StandardCounts::POSES;
152+ //}
153+
154+ //glm::vec3 ScriptingInterface::getSpatialControlPosition(int controlIndex) const {
155+ // // FIXME extract the position from the standard pose
156+ // return vec3();
157+ //}
158+
159+ //glm::vec3 ScriptingInterface::getSpatialControlVelocity(int controlIndex) const {
160+ // // FIXME extract the velocity from the standard pose
161+ // return vec3();
162+ //}
163+
164+ //glm::vec3 ScriptingInterface::getSpatialControlNormal(int controlIndex) const {
165+ // // FIXME extract the normal from the standard pose
166+ // return vec3();
167+ //}
168+ //
169+ //glm::quat ScriptingInterface::getSpatialControlRawRotation(int controlIndex) const {
170+ // // FIXME extract the rotation from the standard pose
171+ // return quat();
172+ //}
173+
174+ QVector<Action> ScriptingInterface::getAllActions() {
175+ return DependencyManager::get<UserInputMapper>()->getAllActions();
176+ }
177+
178+ QString ScriptingInterface::getDeviceName(unsigned int device) {
179+ return DependencyManager::get<UserInputMapper>()->getDeviceName((unsigned short)device);
180+ }
181+
182+ QVector<Input::NamedPair> ScriptingInterface::getAvailableInputs(unsigned int device) {
183+ return DependencyManager::get<UserInputMapper>()->getAvailableInputs((unsigned short)device);
184+ }
185+
186+ int ScriptingInterface::findDevice(QString name) {
187+ return DependencyManager::get<UserInputMapper>()->findDevice(name);
188+ }
189+
190+ QVector<QString> ScriptingInterface::getDeviceNames() {
191+ return DependencyManager::get<UserInputMapper>()->getDeviceNames();
192+ }
193+
194+ float ScriptingInterface::getActionValue(int action) {
195+ return DependencyManager::get<UserInputMapper>()->getActionState(Action(action));
196+ }
197+
198+ int ScriptingInterface::findAction(QString actionName) {
199+ return DependencyManager::get<UserInputMapper>()->findAction(actionName);
200+ }
201+
202+ QVector<QString> ScriptingInterface::getActionNames() const {
203+ return DependencyManager::get<UserInputMapper>()->getActionNames();
204+ }
205+
206+ void ScriptingInterface::updateMaps() {
207+ QVariantMap newHardware;
208+ auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
209+ auto devices = userInputMapper->getDevices();
210+ for (const auto& deviceMapping : devices) {
211+ auto deviceID = deviceMapping.first;
212+ if (deviceID != userInputMapper->getStandardDeviceID()) {
213+ auto device = deviceMapping.second;
214+ auto deviceName = QString(device->getName()).remove(SANITIZE_NAME_EXPRESSION);
215+ qCDebug(controllers) << "Device" << deviceMapping.first << ":" << deviceName;
216+ if (newHardware.contains(deviceName)) {
217+ continue;
218+ }
219+
220+ // Expose the IDs to JS
221+ newHardware.insert(deviceName, createDeviceMap(device));
222+ }
223+ }
224+ _hardware = newHardware;
225+ }
226+
227+
228+ QObject* ScriptingInterface::parseMapping(const QString& json) {
229+ auto userInputMapper = DependencyManager::get<UserInputMapper>();
230+ auto mapping = userInputMapper->parseMapping(json);
231+ return new MappingBuilderProxy(*userInputMapper, mapping);
232+ }
233+
234+ QObject* ScriptingInterface::loadMapping(const QString& jsonUrl) {
235+ return nullptr;
236+ }
237+
238+
239+} // namespace controllers
240+
241+
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+
36+namespace controller {
37+ class InputController : public QObject {
38+ Q_OBJECT
39+
40+ public:
41+ using Key = unsigned int;
42+ using Pointer = std::shared_ptr<InputController>;
43+
44+ virtual void update() = 0;
45+ virtual Key getKey() const = 0;
46+
47+ public slots:
48+ virtual bool isActive() const = 0;
49+ virtual glm::vec3 getAbsTranslation() const = 0;
50+ virtual glm::quat getAbsRotation() const = 0;
51+ virtual glm::vec3 getLocTranslation() const = 0;
52+ virtual glm::quat getLocRotation() const = 0;
53+
54+ signals:
55+ //void spatialEvent(const SpatialEvent& event);
56+ };
57+
58+ /// handles scripting of input controller commands from JS
59+ class ScriptingInterface : public QObject, public Dependency {
60+ Q_OBJECT
61+ Q_PROPERTY(QVariantMap Hardware READ getHardware CONSTANT FINAL)
62+ Q_PROPERTY(QVariantMap Actions READ getActions CONSTANT FINAL)
63+ Q_PROPERTY(QVariantMap Standard READ getStandard CONSTANT FINAL)
64+
65+ public:
66+ ScriptingInterface();
67+ virtual ~ScriptingInterface() {};
68+
69+ Q_INVOKABLE QVector<Action> getAllActions();
70+ Q_INVOKABLE QVector<Input::NamedPair> getAvailableInputs(unsigned int device);
71+ Q_INVOKABLE QString getDeviceName(unsigned int device);
72+ Q_INVOKABLE float getActionValue(int action);
73+ Q_INVOKABLE int findDevice(QString name);
74+ Q_INVOKABLE QVector<QString> getDeviceNames();
75+ Q_INVOKABLE int findAction(QString actionName);
76+ Q_INVOKABLE QVector<QString> getActionNames() const;
77+
78+ Q_INVOKABLE float getValue(const int& source) const;
79+ Q_INVOKABLE float getButtonValue(StandardButtonChannel source, uint16_t device = 0) const;
80+ Q_INVOKABLE float getAxisValue(StandardAxisChannel source, uint16_t device = 0) const;
81+ Q_INVOKABLE Pose getPoseValue(const int& source) const;
82+ Q_INVOKABLE Pose getPoseValue(StandardPoseChannel source, uint16_t device = 0) const;
83+
84+ Q_INVOKABLE QObject* newMapping(const QString& mappingName = QUuid::createUuid().toString());
85+ Q_INVOKABLE void enableMapping(const QString& mappingName, bool enable = true);
86+ Q_INVOKABLE void disableMapping(const QString& mappingName) { enableMapping(mappingName, false); }
87+ Q_INVOKABLE QObject* parseMapping(const QString& json);
88+ Q_INVOKABLE QObject* loadMapping(const QString& jsonUrl);
89+
90+
91+ //Q_INVOKABLE bool isPrimaryButtonPressed() const;
92+ //Q_INVOKABLE glm::vec2 getPrimaryJoystickPosition() const;
93+
94+ //Q_INVOKABLE int getNumberOfButtons() const;
95+ //Q_INVOKABLE bool isButtonPressed(int buttonIndex) const;
96+
97+ //Q_INVOKABLE int getNumberOfTriggers() const;
98+ //Q_INVOKABLE float getTriggerValue(int triggerIndex) const;
99+
100+ //Q_INVOKABLE int getNumberOfJoysticks() const;
101+ //Q_INVOKABLE glm::vec2 getJoystickPosition(int joystickIndex) const;
102+
103+ //Q_INVOKABLE int getNumberOfSpatialControls() const;
104+ //Q_INVOKABLE glm::vec3 getSpatialControlPosition(int controlIndex) const;
105+ //Q_INVOKABLE glm::vec3 getSpatialControlVelocity(int controlIndex) const;
106+ //Q_INVOKABLE glm::vec3 getSpatialControlNormal(int controlIndex) const;
107+ //Q_INVOKABLE glm::quat getSpatialControlRawRotation(int controlIndex) const;
108+
109+ Q_INVOKABLE const QVariantMap& getHardware() { return _hardware; }
110+ Q_INVOKABLE const QVariantMap& getActions() { return _actions; }
111+ Q_INVOKABLE const QVariantMap& getStandard() { return _standard; }
112+
113+ bool isMouseCaptured() const { return _mouseCaptured; }
114+ bool isTouchCaptured() const { return _touchCaptured; }
115+ bool isWheelCaptured() const { return _wheelCaptured; }
116+ bool areActionsCaptured() const { return _actionsCaptured; }
117+
118+ public slots:
119+
120+ virtual void captureMouseEvents() { _mouseCaptured = true; }
121+ virtual void releaseMouseEvents() { _mouseCaptured = false; }
122+
123+ virtual void captureTouchEvents() { _touchCaptured = true; }
124+ virtual void releaseTouchEvents() { _touchCaptured = false; }
125+
126+ virtual void captureWheelEvents() { _wheelCaptured = true; }
127+ virtual void releaseWheelEvents() { _wheelCaptured = false; }
128+
129+ virtual void captureActionEvents() { _actionsCaptured = true; }
130+ virtual void releaseActionEvents() { _actionsCaptured = false; }
131+
132+ signals:
133+ void actionEvent(int action, float state);
134+ void inputEvent(int action, float state);
135+ void hardwareChanged();
136+
137+ private:
138+ // Update the exposed variant maps reporting active hardware
139+ void updateMaps();
140+
141+ QVariantMap _hardware;
142+ QVariantMap _actions;
143+ QVariantMap _standard;
144+
145+ bool _mouseCaptured{ false };
146+ bool _touchCaptured{ false };
147+ bool _wheelCaptured{ false };
148+ bool _actionsCaptured{ false };
149+ };
150+
151+
152+}
153+
154+
155+#endif // hifi_AbstractControllerScriptingInterface_h
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+
19+namespace controller {
20+
21+StandardController::StandardController() : InputDevice("Standard") {
22+ _deviceID = UserInputMapper::STANDARD_DEVICE;
23+}
24+
25+StandardController::~StandardController() {
26+}
27+
28+void StandardController::update(float deltaTime, bool jointsCaptured) {
29+}
30+
31+void StandardController::focusOutEvent() {
32+ _axisStateMap.clear();
33+ _buttonPressedMap.clear();
34+};
35+
36+Input::NamedVector StandardController::getAvailableInputs() const {
37+ static Input::NamedVector availableInputs {
38+ // Buttons
39+ makePair(A, "A"),
40+ makePair(B, "B"),
41+ makePair(X, "X"),
42+ makePair(Y, "Y"),
43+
44+ // DPad
45+ makePair(DU, "DU"),
46+ makePair(DD, "DD"),
47+ makePair(DL, "DL"),
48+ makePair(DR, "DR"),
49+
50+ // Bumpers
51+ makePair(LB, "LB"),
52+ makePair(RB, "RB"),
53+
54+ // Stick press
55+ makePair(LS, "LS"),
56+ makePair(RS, "RS"),
57+
58+ // Center buttons
59+ makePair(START, "Start"),
60+ makePair(BACK, "Back"),
61+
62+ // Analog sticks
63+ makePair(LY, "LY"),
64+ makePair(LX, "LX"),
65+ makePair(RY, "RY"),
66+ makePair(RX, "RX"),
67+
68+ // Triggers
69+ makePair(LT, "LT"),
70+ makePair(RT, "RT"),
71+
72+
73+ // Finger abstractions
74+ makePair(LEFT_PRIMARY_THUMB, "LeftPrimaryThumb"),
75+ makePair(LEFT_SECONDARY_THUMB, "LeftSecondaryThumb"),
76+ makePair(RIGHT_PRIMARY_THUMB, "RightPrimaryThumb"),
77+ makePair(RIGHT_SECONDARY_THUMB, "RightSecondaryThumb"),
78+
79+ makePair(LEFT_PRIMARY_INDEX, "LeftPrimaryIndex"),
80+ makePair(LEFT_SECONDARY_INDEX, "LeftSecondaryIndex"),
81+ makePair(RIGHT_PRIMARY_INDEX, "RightPrimaryIndex"),
82+ makePair(RIGHT_SECONDARY_INDEX, "RightSecondaryIndex"),
83+
84+ makePair(LEFT_GRIP, "LeftGrip"),
85+ makePair(RIGHT_GRIP, "RightGrip"),
86+
87+ // Poses
88+ makePair(LEFT_HAND, "LeftHand"),
89+ makePair(RIGHT_HAND, "RightHand"),
90+
91+
92+ // Aliases, PlayStation style names
93+ makePair(LB, "L1"),
94+ makePair(RB, "R1"),
95+ makePair(LT, "L2"),
96+ makePair(RT, "R2"),
97+ makePair(LS, "L3"),
98+ makePair(RS, "R3"),
99+ makePair(BACK, "Select"),
100+ makePair(A, "Cross"),
101+ makePair(B, "Circle"),
102+ makePair(X, "Square"),
103+ makePair(Y, "Triangle"),
104+ makePair(DU, "Up"),
105+ makePair(DD, "Down"),
106+ makePair(DL, "Left"),
107+ makePair(DR, "Right"),
108+ };
109+ return availableInputs;
110+}
111+
112+EndpointPointer StandardController::createEndpoint(const Input& input) const {
113+ return std::make_shared<StandardEndpoint>(input);
114+}
115+
116+QString StandardController::getDefaultMappingConfig() const {
117+ static const QString DEFAULT_MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/standard.json";
118+ return DEFAULT_MAPPING_JSON;
119+}
120+
121+// FIXME figure out how to move the shifted version to JSON
122+//void StandardController::assignDefaultInputMapping(UserInputMapper& mapper) {
123+// const float JOYSTICK_MOVE_SPEED = 1.0f;
124+// const float DPAD_MOVE_SPEED = 0.5f;
125+// const float JOYSTICK_YAW_SPEED = 0.5f;
126+// const float JOYSTICK_PITCH_SPEED = 0.25f;
127+// const float BOOM_SPEED = 0.1f;
128+//
129+// // Hold front right shoulder button for precision controls
130+// // Left Joystick: Movement, strafing
131+// mapper.addInputChannel(UserInputMapper::TRANSLATE_Z, makeInput(controller::LY), makeInput(controller::RB), JOYSTICK_MOVE_SPEED / 2.0f);
132+// mapper.addInputChannel(UserInputMapper::TRANSLATE_X, makeInput(controller::LY), makeInput(controller::RB), JOYSTICK_MOVE_SPEED / 2.0f);
133+//
134+// // Right Joystick: Camera orientation
135+// mapper.addInputChannel(UserInputMapper::YAW, makeInput(controller::RX), makeInput(controller::RB), JOYSTICK_YAW_SPEED / 2.0f);
136+// mapper.addInputChannel(UserInputMapper::PITCH, makeInput(controller::RY), makeInput(controller::RB), JOYSTICK_PITCH_SPEED / 2.0f);
137+//
138+// // Dpad movement
139+// mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(controller::DU), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f);
140+// mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(controller::DD), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f);
141+// mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(controller::DR), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f);
142+// mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(controller::DL), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f);
143+//
144+// // Button controls
145+// mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(controller::Y), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f);
146+// mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(controller::X), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f);
147+//
148+// // Zoom
149+// mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(controller::RT), makeInput(controller::RB), BOOM_SPEED / 2.0f);
150+// mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(controller::LT), makeInput(controller::RB), BOOM_SPEED / 2.0f);
151+//
152+// mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(controller::RB));
153+//
154+// mapper.addInputChannel(UserInputMapper::ACTION1, makeInput(controller::B));
155+// mapper.addInputChannel(UserInputMapper::ACTION2, makeInput(controller::A));
156+//}
157+
158+}
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+
21+namespace controller {
22+
23+class StandardController : public QObject, public InputDevice {
24+ Q_OBJECT
25+ Q_PROPERTY(QString name READ getName)
26+
27+public:
28+ virtual EndpointPointer createEndpoint(const Input& input) const override;
29+ virtual Input::NamedVector getAvailableInputs() const override;
30+ virtual QString getDefaultMappingConfig() const override;
31+ virtual void update(float deltaTime, bool jointsCaptured) override;
32+ virtual void focusOutEvent() override;
33+
34+ StandardController();
35+ virtual ~StandardController();
36+};
37+
38+}
39+
40+#endif // hifi_StandardController_h
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+
10+namespace controller {
11+
12+ // Needs to match order and values of SDL_GameControllerButton
13+ enum StandardButtonChannel {
14+ // Button quad
15+ A = 0,
16+ B,
17+ X,
18+ Y,
19+
20+ // Center buttons
21+ BACK,
22+ GUIDE,
23+ START,
24+
25+ // Stick press
26+ LS,
27+ RS,
28+
29+ // Bumper press
30+ LB,
31+ RB,
32+
33+ // DPad
34+ DU,
35+ DD,
36+ DL,
37+ DR,
38+
39+ // These don't map to SDL types
40+ LEFT_PRIMARY_THUMB,
41+ LEFT_SECONDARY_THUMB,
42+ RIGHT_PRIMARY_THUMB,
43+ RIGHT_SECONDARY_THUMB,
44+
45+ LEFT_PRIMARY_INDEX,
46+ LEFT_SECONDARY_INDEX,
47+ RIGHT_PRIMARY_INDEX,
48+ RIGHT_SECONDARY_INDEX,
49+
50+ LEFT_GRIP,
51+ RIGHT_GRIP,
52+
53+ NUM_STANDARD_BUTTONS
54+ };
55+
56+ // Needs to match order and values of SDL_GameControllerAxis
57+ enum StandardAxisChannel {
58+ // Left Analog stick
59+ LX = 0,
60+ LY,
61+ LZ,
62+ // Right Analog stick
63+ RX,
64+ RY,
65+ RZ,
66+ // Triggers
67+ LT,
68+ RT,
69+ NUM_STANDARD_AXES
70+ };
71+
72+ // No correlation to SDL
73+ enum StandardPoseChannel {
74+ LEFT_HAND = 0,
75+ RIGHT_HAND,
76+ HEAD,
77+ NUM_STANDARD_POSES
78+ };
79+
80+ enum StandardCounts {
81+ TRIGGERS = 2,
82+ ANALOG_STICKS = 2,
83+ POSES = 2, // FIXME 3? if we want to expose the head?
84+ };
85+}
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+
20+namespace controller {
21+
22+StateController::StateController() : InputDevice("Application") {
23+}
24+
25+StateController::~StateController() {
26+}
27+
28+void StateController::update(float deltaTime, bool jointsCaptured) {}
29+
30+void StateController::focusOutEvent() {}
31+
32+void StateController::addInputVariant(QString name, ReadLambda lambda) {
33+ _namedReadLambdas.push_back(NamedReadLambda(name, lambda));
34+}
35+
36+Input::NamedVector StateController::getAvailableInputs() const {
37+ Input::NamedVector availableInputs;
38+ int i = 0;
39+ for (auto& pair : _namedReadLambdas) {
40+ availableInputs.push_back(Input::NamedPair(Input(_deviceID, i, ChannelType::BUTTON), pair.first));
41+ i++;
42+ }
43+ return availableInputs;
44+}
45+
46+EndpointPointer StateController::createEndpoint(const Input& input) const {
47+ return std::make_shared<LambdaEndpoint>(_namedReadLambdas[input.getChannel()].second);
48+}
49+
50+}
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+
20+namespace controller {
21+
22+class StateController : public QObject, public InputDevice {
23+ Q_OBJECT
24+ Q_PROPERTY(QString name READ getName)
25+
26+public:
27+ const QString& getName() const { return _name; }
28+
29+ // Device functions
30+ virtual Input::NamedVector getAvailableInputs() const override;
31+ virtual void update(float deltaTime, bool jointsCaptured) override;
32+ virtual void focusOutEvent() override;
33+
34+ StateController();
35+ virtual ~StateController();
36+
37+ using ReadLambda = std::function<float()>;
38+ using NamedReadLambda = QPair<QString, ReadLambda>;
39+
40+ void addInputVariant(QString name, ReadLambda lambda);
41+
42+ virtual EndpointPointer createEndpoint(const Input& input) const override;
43+
44+
45+protected:
46+ QVector<NamedReadLambda> _namedReadLambdas;
47+};
48+
49+}
50+
51+#endif // hifi_StateController_h
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+
45+namespace controller {
46+ const uint16_t UserInputMapper::ACTIONS_DEVICE = Input::INVALID_DEVICE - 0xFF;
47+ const uint16_t UserInputMapper::STANDARD_DEVICE = 0;
48+}
49+
50+// Default contruct allocate the poutput size with the current hardcoded action channels
51+controller::UserInputMapper::UserInputMapper() {
52+ registerDevice(std::make_shared<ActionsDevice>());
53+ registerDevice(std::make_shared<StandardController>());
54+}
55+
56+namespace controller {
57+
58+
59+UserInputMapper::~UserInputMapper() {
60+}
61+
62+int UserInputMapper::recordDeviceOfType(const QString& deviceName) {
63+ if (!_deviceCounts.contains(deviceName)) {
64+ _deviceCounts[deviceName] = 0;
65+ }
66+ _deviceCounts[deviceName] += 1;
67+ return _deviceCounts[deviceName];
68+}
69+
70+void UserInputMapper::registerDevice(InputDevice::Pointer device) {
71+ Locker locker(_lock);
72+ if (device->_deviceID == Input::INVALID_DEVICE) {
73+ device->_deviceID = getFreeDeviceID();
74+ }
75+ const auto& deviceID = device->_deviceID;
76+
77+ int numberOfType = recordDeviceOfType(device->getName());
78+
79+ qCDebug(controllers) << "Registered input device <" << device->getName() << "> deviceID = " << deviceID;
80+ for (const auto& inputMapping : device->getAvailableInputs()) {
81+ const auto& input = inputMapping.first;
82+ // Ignore aliases
83+ if (_endpointsByInput.count(input)) {
84+ continue;
85+ }
86+
87+ Endpoint::Pointer endpoint = device->createEndpoint(input);
88+ if (!endpoint) {
89+ if (input.device == STANDARD_DEVICE) {
90+ endpoint = std::make_shared<StandardEndpoint>(input);
91+ } else if (input.device == ACTIONS_DEVICE) {
92+ endpoint = std::make_shared<ActionEndpoint>(input);
93+ } else {
94+ endpoint = std::make_shared<InputEndpoint>(input);
95+ }
96+ }
97+ _inputsByEndpoint[endpoint] = input;
98+ _endpointsByInput[input] = endpoint;
99+ }
100+
101+ _registeredDevices[deviceID] = device;
102+ auto mapping = loadMapping(device->getDefaultMappingConfig());
103+ if (mapping) {
104+ _mappingsByDevice[deviceID] = mapping;
105+ enableMapping(mapping);
106+ }
107+
108+ emit hardwareChanged();
109+}
110+
111+// FIXME remove the associated device mappings
112+void UserInputMapper::removeDevice(int deviceID) {
113+ Locker locker(_lock);
114+ auto proxyEntry = _registeredDevices.find(deviceID);
115+ if (_registeredDevices.end() == proxyEntry) {
116+ qCWarning(controllers) << "Attempted to remove unknown device " << deviceID;
117+ return;
118+ }
119+ auto proxy = proxyEntry->second;
120+ auto mappingsEntry = _mappingsByDevice.find(deviceID);
121+ if (_mappingsByDevice.end() != mappingsEntry) {
122+ disableMapping(mappingsEntry->second);
123+ _mappingsByDevice.erase(mappingsEntry);
124+ }
125+
126+ _registeredDevices.erase(proxyEntry);
127+
128+ emit hardwareChanged();
129+}
130+
131+
132+void UserInputMapper::loadDefaultMapping(uint16 deviceID) {
133+ Locker locker(_lock);
134+ auto proxyEntry = _registeredDevices.find(deviceID);
135+ if (_registeredDevices.end() == proxyEntry) {
136+ qCWarning(controllers) << "Unknown deviceID " << deviceID;
137+ return;
138+ }
139+
140+
141+ auto mapping = loadMapping(proxyEntry->second->getDefaultMappingConfig());
142+ if (mapping) {
143+ auto prevMapping = _mappingsByDevice[deviceID];
144+ disableMapping(prevMapping);
145+
146+ _mappingsByDevice[deviceID] = mapping;
147+ enableMapping(mapping);
148+ }
149+
150+ emit hardwareChanged();
151+}
152+
153+InputDevice::Pointer UserInputMapper::getDevice(const Input& input) {
154+ Locker locker(_lock);
155+ auto device = _registeredDevices.find(input.getDevice());
156+ if (device != _registeredDevices.end()) {
157+ return (device->second);
158+ } else {
159+ return InputDevice::Pointer();
160+ }
161+}
162+
163+QString UserInputMapper::getDeviceName(uint16 deviceID) {
164+ Locker locker(_lock);
165+ if (_registeredDevices.find(deviceID) != _registeredDevices.end()) {
166+ return _registeredDevices[deviceID]->_name;
167+ }
168+ return QString("unknown");
169+}
170+
171+int UserInputMapper::findDevice(QString name) const {
172+ Locker locker(_lock);
173+ for (auto device : _registeredDevices) {
174+ if (device.second->_name == name) {
175+ return device.first;
176+ }
177+ }
178+ return Input::INVALID_DEVICE;
179+}
180+
181+QVector<QString> UserInputMapper::getDeviceNames() {
182+ Locker locker(_lock);
183+ QVector<QString> result;
184+ for (auto device : _registeredDevices) {
185+ QString deviceName = device.second->_name.split(" (")[0];
186+ result << deviceName;
187+ }
188+ return result;
189+}
190+
191+int UserInputMapper::findAction(const QString& actionName) const {
192+ return findDeviceInput("Actions." + actionName).getChannel();
193+}
194+
195+Input UserInputMapper::findDeviceInput(const QString& inputName) const {
196+ Locker locker(_lock);
197+ // Split the full input name as such: deviceName.inputName
198+ auto names = inputName.split('.');
199+
200+ if (names.size() >= 2) {
201+ // Get the device name:
202+ auto deviceName = names[0];
203+ auto inputName = names[1];
204+
205+ int deviceID = findDevice(deviceName);
206+ if (deviceID != Input::INVALID_DEVICE) {
207+ const auto& device = _registeredDevices.at(deviceID);
208+ auto deviceInputs = device->getAvailableInputs();
209+
210+ for (auto input : deviceInputs) {
211+ if (input.second == inputName) {
212+ return input.first;
213+ }
214+ }
215+
216+ qCDebug(controllers) << "Couldn\'t find InputChannel named <" << inputName << "> for device <" << deviceName << ">";
217+
218+ } else {
219+ qCDebug(controllers) << "Couldn\'t find InputDevice named <" << deviceName << ">";
220+ findDevice(deviceName);
221+ }
222+ } else {
223+ qCDebug(controllers) << "Couldn\'t understand <" << inputName << "> as a valid inputDevice.inputName";
224+ }
225+
226+ return Input::INVALID_INPUT;
227+}
228+
229+void fixBisectedAxis(float& full, float& negative, float& positive) {
230+ full = full + (negative * -1.0f) + positive;
231+ negative = full >= 0.0f ? 0.0f : full * -1.0f;
232+ positive = full <= 0.0f ? 0.0f : full;
233+}
234+
235+void UserInputMapper::update(float deltaTime) {
236+ Locker locker(_lock);
237+ // Reset the axis state for next loop
238+ for (auto& channel : _actionStates) {
239+ channel = 0.0f;
240+ }
241+
242+ for (auto& channel : _poseStates) {
243+ channel = Pose();
244+ }
245+
246+ // Run the mappings code
247+ runMappings();
248+
249+ // merge the bisected and non-bisected axes for now
250+ fixBisectedAxis(_actionStates[toInt(Action::TRANSLATE_X)], _actionStates[toInt(Action::LATERAL_LEFT)], _actionStates[toInt(Action::LATERAL_RIGHT)]);
251+ fixBisectedAxis(_actionStates[toInt(Action::TRANSLATE_Y)], _actionStates[toInt(Action::VERTICAL_DOWN)], _actionStates[toInt(Action::VERTICAL_UP)]);
252+ fixBisectedAxis(_actionStates[toInt(Action::TRANSLATE_Z)], _actionStates[toInt(Action::LONGITUDINAL_FORWARD)], _actionStates[toInt(Action::LONGITUDINAL_BACKWARD)]);
253+ fixBisectedAxis(_actionStates[toInt(Action::TRANSLATE_CAMERA_Z)], _actionStates[toInt(Action::BOOM_IN)], _actionStates[toInt(Action::BOOM_OUT)]);
254+ fixBisectedAxis(_actionStates[toInt(Action::ROTATE_Y)], _actionStates[toInt(Action::YAW_LEFT)], _actionStates[toInt(Action::YAW_RIGHT)]);
255+ fixBisectedAxis(_actionStates[toInt(Action::ROTATE_X)], _actionStates[toInt(Action::PITCH_UP)], _actionStates[toInt(Action::PITCH_DOWN)]);
256+
257+ static const float EPSILON = 0.01f;
258+ for (auto i = 0; i < toInt(Action::NUM_ACTIONS); i++) {
259+ _actionStates[i] *= _actionScales[i];
260+ // Emit only on change, and emit when moving back to 0
261+ if (fabsf(_actionStates[i] - _lastActionStates[i]) > EPSILON) {
262+ _lastActionStates[i] = _actionStates[i];
263+ emit actionEvent(i, _actionStates[i]);
264+ }
265+ // TODO: emit signal for pose changes
266+ }
267+
268+ auto standardInputs = getStandardInputs();
269+ if (_lastStandardStates.size() != standardInputs.size()) {
270+ _lastStandardStates.resize(standardInputs.size());
271+ for (auto& lastValue : _lastStandardStates) {
272+ lastValue = 0;
273+ }
274+ }
275+
276+ for (int i = 0; i < standardInputs.size(); ++i) {
277+ const auto& input = standardInputs[i].first;
278+ float value = getValue(input);
279+ float& oldValue = _lastStandardStates[i];
280+ if (value != oldValue) {
281+ oldValue = value;
282+ emit inputEvent(input.id, value);
283+ }
284+ }
285+}
286+
287+Input::NamedVector UserInputMapper::getAvailableInputs(uint16 deviceID) const {
288+ Locker locker(_lock);
289+ auto iterator = _registeredDevices.find(deviceID);
290+ return iterator->second->getAvailableInputs();
291+}
292+
293+QVector<Action> UserInputMapper::getAllActions() const {
294+ Locker locker(_lock);
295+ QVector<Action> actions;
296+ for (auto i = 0; i < toInt(Action::NUM_ACTIONS); i++) {
297+ actions.append(Action(i));
298+ }
299+ return actions;
300+}
301+
302+QString UserInputMapper::getActionName(Action action) const {
303+ Locker locker(_lock);
304+ for (auto actionPair : getActionInputs()) {
305+ if (actionPair.first.channel == toInt(action)) {
306+ return actionPair.second;
307+ }
308+ }
309+ return QString();
310+}
311+
312+
313+QVector<QString> UserInputMapper::getActionNames() const {
314+ Locker locker(_lock);
315+ QVector<QString> result;
316+ for (auto actionPair : getActionInputs()) {
317+ result << actionPair.second;
318+ }
319+ return result;
320+}
321+/*
322+void UserInputMapper::assignDefaulActionScales() {
323+ _actionScales[toInt(Action::LONGITUDINAL_BACKWARD)] = 1.0f; // 1m per unit
324+ _actionScales[toInt(Action::LONGITUDINAL_FORWARD)] = 1.0f; // 1m per unit
325+ _actionScales[toInt(Action::LATERAL_LEFT)] = 1.0f; // 1m per unit
326+ _actionScales[toInt(Action::LATERAL_RIGHT)] = 1.0f; // 1m per unit
327+ _actionScales[toInt(Action::VERTICAL_DOWN)] = 1.0f; // 1m per unit
328+ _actionScales[toInt(Action::VERTICAL_UP)] = 1.0f; // 1m per unit
329+ _actionScales[toInt(Action::YAW_LEFT)] = 1.0f; // 1 degree per unit
330+ _actionScales[toInt(Action::YAW_RIGHT)] = 1.0f; // 1 degree per unit
331+ _actionScales[toInt(Action::PITCH_DOWN)] = 1.0f; // 1 degree per unit
332+ _actionScales[toInt(Action::PITCH_UP)] = 1.0f; // 1 degree per unit
333+ _actionScales[toInt(Action::BOOM_IN)] = 0.5f; // .5m per unit
334+ _actionScales[toInt(Action::BOOM_OUT)] = 0.5f; // .5m per unit
335+ _actionScales[toInt(Action::LEFT_HAND)] = 1.0f; // default
336+ _actionScales[toInt(Action::RIGHT_HAND)] = 1.0f; // default
337+ _actionScales[toInt(Action::LEFT_HAND_CLICK)] = 1.0f; // on
338+ _actionScales[toInt(Action::RIGHT_HAND_CLICK)] = 1.0f; // on
339+ _actionScales[toInt(Action::SHIFT)] = 1.0f; // on
340+ _actionScales[toInt(Action::ACTION1)] = 1.0f; // default
341+ _actionScales[toInt(Action::ACTION2)] = 1.0f; // default
342+ _actionScales[toInt(Action::TRANSLATE_X)] = 1.0f; // default
343+ _actionScales[toInt(Action::TRANSLATE_Y)] = 1.0f; // default
344+ _actionScales[toInt(Action::TRANSLATE_Z)] = 1.0f; // default
345+ _actionScales[toInt(Action::ROLL)] = 1.0f; // default
346+ _actionScales[toInt(Action::PITCH)] = 1.0f; // default
347+ _actionScales[toInt(Action::YAW)] = 1.0f; // default
348+}
349+*/
350+
351+static int actionMetaTypeId = qRegisterMetaType<Action>();
352+static int inputMetaTypeId = qRegisterMetaType<Input>();
353+static int inputPairMetaTypeId = qRegisterMetaType<Input::NamedPair>();
354+static int poseMetaTypeId = qRegisterMetaType<controller::Pose>("Pose");
355+
356+
357+QScriptValue inputToScriptValue(QScriptEngine* engine, const Input& input);
358+void inputFromScriptValue(const QScriptValue& object, Input& input);
359+QScriptValue actionToScriptValue(QScriptEngine* engine, const Action& action);
360+void actionFromScriptValue(const QScriptValue& object, Action& action);
361+QScriptValue inputPairToScriptValue(QScriptEngine* engine, const Input::NamedPair& inputPair);
362+void inputPairFromScriptValue(const QScriptValue& object, Input::NamedPair& inputPair);
363+
364+QScriptValue inputToScriptValue(QScriptEngine* engine, const Input& input) {
365+ QScriptValue obj = engine->newObject();
366+ obj.setProperty("device", input.getDevice());
367+ obj.setProperty("channel", input.getChannel());
368+ obj.setProperty("type", (unsigned short)input.getType());
369+ obj.setProperty("id", input.getID());
370+ return obj;
371+}
372+
373+void inputFromScriptValue(const QScriptValue& object, Input& input) {
374+ input.id = object.property("id").toInt32();
375+}
376+
377+QScriptValue actionToScriptValue(QScriptEngine* engine, const Action& action) {
378+ QScriptValue obj = engine->newObject();
379+ auto userInputMapper = DependencyManager::get<UserInputMapper>();
380+ obj.setProperty("action", (int)action);
381+ obj.setProperty("actionName", userInputMapper->getActionName(action));
382+ return obj;
383+}
384+
385+void actionFromScriptValue(const QScriptValue& object, Action& action) {
386+ action = Action(object.property("action").toVariant().toInt());
387+}
388+
389+QScriptValue inputPairToScriptValue(QScriptEngine* engine, const Input::NamedPair& inputPair) {
390+ QScriptValue obj = engine->newObject();
391+ obj.setProperty("input", inputToScriptValue(engine, inputPair.first));
392+ obj.setProperty("inputName", inputPair.second);
393+ return obj;
394+}
395+
396+void inputPairFromScriptValue(const QScriptValue& object, Input::NamedPair& inputPair) {
397+ inputFromScriptValue(object.property("input"), inputPair.first);
398+ inputPair.second = QString(object.property("inputName").toVariant().toString());
399+}
400+
401+void UserInputMapper::registerControllerTypes(QScriptEngine* engine) {
402+ qScriptRegisterSequenceMetaType<QVector<Action> >(engine);
403+ qScriptRegisterSequenceMetaType<Input::NamedVector>(engine);
404+ qScriptRegisterMetaType(engine, actionToScriptValue, actionFromScriptValue);
405+ qScriptRegisterMetaType(engine, inputToScriptValue, inputFromScriptValue);
406+ qScriptRegisterMetaType(engine, inputPairToScriptValue, inputPairFromScriptValue);
407+
408+ qScriptRegisterMetaType(engine, Pose::toScriptValue, Pose::fromScriptValue);
409+}
410+
411+Input UserInputMapper::makeStandardInput(controller::StandardButtonChannel button) {
412+ return Input(STANDARD_DEVICE, button, ChannelType::BUTTON);
413+}
414+
415+Input UserInputMapper::makeStandardInput(controller::StandardAxisChannel axis) {
416+ return Input(STANDARD_DEVICE, axis, ChannelType::AXIS);
417+}
418+
419+Input UserInputMapper::makeStandardInput(controller::StandardPoseChannel pose) {
420+ return Input(STANDARD_DEVICE, pose, ChannelType::POSE);
421+}
422+
423+static auto lastDebugTime = usecTimestampNow();
424+static auto debugRoutes = false;
425+static auto debuggableRoutes = false;
426+static const auto DEBUG_INTERVAL = USECS_PER_SECOND;
427+
428+void UserInputMapper::runMappings() {
429+ auto now = usecTimestampNow();
430+ if (debuggableRoutes && now - lastDebugTime > DEBUG_INTERVAL) {
431+ lastDebugTime = now;
432+ debugRoutes = true;
433+ }
434+
435+ if (debugRoutes) {
436+ qCDebug(controllers) << "Beginning mapping frame";
437+ }
438+ for (auto endpointEntry : this->_endpointsByInput) {
439+ endpointEntry.second->reset();
440+ }
441+
442+ if (debugRoutes) {
443+ qCDebug(controllers) << "Processing device routes";
444+ }
445+ // Now process the current values for each level of the stack
446+ applyRoutes(_deviceRoutes);
447+
448+ if (debugRoutes) {
449+ qCDebug(controllers) << "Processing standard routes";
450+ }
451+ applyRoutes(_standardRoutes);
452+
453+ if (debugRoutes) {
454+ qCDebug(controllers) << "Done with mappings";
455+ }
456+ debugRoutes = false;
457+}
458+
459+// Encapsulate the logic that routes should not be read before they are written
460+void UserInputMapper::applyRoutes(const Route::List& routes) {
461+ Route::List deferredRoutes;
462+
463+ for (const auto& route : routes) {
464+ if (!route) {
465+ continue;
466+ }
467+
468+ // Try all the deferred routes
469+ deferredRoutes.remove_if([](Route::Pointer route) {
470+ return UserInputMapper::applyRoute(route);
471+ });
472+
473+ if (!applyRoute(route)) {
474+ deferredRoutes.push_back(route);
475+ }
476+ }
477+
478+ bool force = true;
479+ for (const auto& route : deferredRoutes) {
480+ UserInputMapper::applyRoute(route, force);
481+ }
482+}
483+
484+
485+bool UserInputMapper::applyRoute(const Route::Pointer& route, bool force) {
486+ if (debugRoutes && route->debug) {
487+ qCDebug(controllers) << "Applying route " << route->json;
488+ }
489+
490+ // If the source hasn't been written yet, defer processing of this route
491+ auto source = route->source;
492+ auto sourceInput = source->getInput();
493+ if (sourceInput.device == STANDARD_DEVICE && !force && source->writeable()) {
494+ if (debugRoutes && route->debug) {
495+ qCDebug(controllers) << "Source not yet written, deferring";
496+ }
497+ return false;
498+ }
499+
500+ if (route->conditional) {
501+ // FIXME for endpoint conditionals we need to check if they've been written
502+ if (!route->conditional->satisfied()) {
503+ if (debugRoutes && route->debug) {
504+ qCDebug(controllers) << "Conditional failed";
505+ }
506+ return true;
507+ }
508+ }
509+
510+
511+ // Most endpoints can only be read once (though a given mapping can route them to
512+ // multiple places). Consider... If the default is to wire the A button to JUMP
513+ // and someone else wires it to CONTEXT_MENU, I don't want both to occur when
514+ // I press the button. The exception is if I'm wiring a control back to itself
515+ // in order to adjust my interface, like inverting the Y axis on an analog stick
516+ if (!source->readable()) {
517+ if (debugRoutes && route->debug) {
518+ qCDebug(controllers) << "Source unreadable";
519+ }
520+ return true;
521+ }
522+
523+ auto destination = route->destination;
524+ // THis could happen if the route destination failed to create
525+ // FIXME: Maybe do not create the route if the destination failed and avoid this case ?
526+ if (!destination) {
527+ if (debugRoutes && route->debug) {
528+ qCDebug(controllers) << "Bad Destination";
529+ }
530+ return true;
531+ }
532+
533+ if (!destination->writeable()) {
534+ if (debugRoutes && route->debug) {
535+ qCDebug(controllers) << "Destination unwritable";
536+ }
537+ return true;
538+ }
539+
540+ // Fetch the value, may have been overriden by previous loopback routes
541+ if (source->isPose()) {
542+ Pose value = getPose(source);
543+ static const Pose IDENTITY_POSE { vec3(), quat() };
544+ if (debugRoutes && route->debug) {
545+ if (!value.valid) {
546+ qCDebug(controllers) << "Applying invalid pose";
547+ } else if (value == IDENTITY_POSE) {
548+ qCDebug(controllers) << "Applying identity pose";
549+ } else {
550+ qCDebug(controllers) << "Applying valid pose";
551+ }
552+ }
553+ // no filters yet for pose
554+ destination->apply(value, source);
555+ } else {
556+ // Fetch the value, may have been overriden by previous loopback routes
557+ float value = getValue(source);
558+
559+ if (debugRoutes && route->debug) {
560+ qCDebug(controllers) << "Value was " << value;
561+ }
562+ // Apply each of the filters.
563+ for (const auto& filter : route->filters) {
564+ value = filter->apply(value);
565+ }
566+
567+ if (debugRoutes && route->debug) {
568+ qCDebug(controllers) << "Filtered value was " << value;
569+ }
570+
571+ destination->apply(value, source);
572+ }
573+ return true;
574+}
575+
576+Endpoint::Pointer UserInputMapper::endpointFor(const QJSValue& endpoint) {
577+ if (endpoint.isNumber()) {
578+ return endpointFor(Input(endpoint.toInt()));
579+ }
580+
581+ if (endpoint.isCallable()) {
582+ auto result = std::make_shared<JSEndpoint>(endpoint);
583+ return result;
584+ }
585+
586+ qWarning() << "Unsupported input type " << endpoint.toString();
587+ return Endpoint::Pointer();
588+}
589+
590+Endpoint::Pointer UserInputMapper::endpointFor(const QScriptValue& endpoint) {
591+ if (endpoint.isNumber()) {
592+ return endpointFor(Input(endpoint.toInt32()));
593+ }
594+
595+ if (endpoint.isFunction()) {
596+ auto result = std::make_shared<ScriptEndpoint>(endpoint);
597+ return result;
598+ }
599+
600+ if (endpoint.isArray()) {
601+ int length = endpoint.property("length").toInteger();
602+ Endpoint::List children;
603+ for (int i = 0; i < length; i++) {
604+ QScriptValue arrayItem = endpoint.property(i);
605+ Endpoint::Pointer destination = endpointFor(arrayItem);
606+ if (!destination) {
607+ return Endpoint::Pointer();
608+ }
609+ children.push_back(destination);
610+ }
611+ return std::make_shared<AnyEndpoint>(children);
612+ }
613+
614+
615+ qWarning() << "Unsupported input type " << endpoint.toString();
616+ return Endpoint::Pointer();
617+}
618+
619+Endpoint::Pointer UserInputMapper::endpointFor(const Input& inputId) const {
620+ Locker locker(_lock);
621+ auto iterator = _endpointsByInput.find(inputId);
622+ if (_endpointsByInput.end() == iterator) {
623+ qWarning() << "Unknown input: " << QString::number(inputId.getID(), 16);
624+ return Endpoint::Pointer();
625+ }
626+ return iterator->second;
627+}
628+
629+Endpoint::Pointer UserInputMapper::compositeEndpointFor(Endpoint::Pointer first, Endpoint::Pointer second) {
630+ EndpointPair pair(first, second);
631+ Endpoint::Pointer result;
632+ auto iterator = _compositeEndpoints.find(pair);
633+ if (_compositeEndpoints.end() == iterator) {
634+ result = std::make_shared<CompositeEndpoint>(first, second);
635+ _compositeEndpoints[pair] = result;
636+ } else {
637+ result = iterator->second;
638+ }
639+ return result;
640+}
641+
642+
643+Mapping::Pointer UserInputMapper::newMapping(const QString& mappingName) {
644+ Locker locker(_lock);
645+ if (_mappingsByName.count(mappingName)) {
646+ qCWarning(controllers) << "Refusing to recreate mapping named " << mappingName;
647+ }
648+ qDebug() << "Creating new Mapping " << mappingName;
649+ auto mapping = std::make_shared<Mapping>(mappingName);
650+ _mappingsByName[mappingName] = mapping;
651+ return mapping;
652+}
653+
654+// FIXME handle asynchronous loading in the UserInputMapper
655+//QObject* ScriptingInterface::loadMapping(const QString& jsonUrl) {
656+// QObject* result = nullptr;
657+// auto request = ResourceManager::createResourceRequest(nullptr, QUrl(jsonUrl));
658+// if (request) {
659+// QEventLoop eventLoop;
660+// request->setCacheEnabled(false);
661+// connect(request, &ResourceRequest::finished, &eventLoop, &QEventLoop::quit);
662+// request->send();
663+// if (request->getState() != ResourceRequest::Finished) {
664+// eventLoop.exec();
665+// }
666+//
667+// if (request->getResult() == ResourceRequest::Success) {
668+// result = parseMapping(QString(request->getData()));
669+// } else {
670+// qCWarning(controllers) << "Failed to load mapping url <" << jsonUrl << ">" << endl;
671+// }
672+// request->deleteLater();
673+// }
674+// return result;
675+//}
676+
677+void UserInputMapper::enableMapping(const QString& mappingName, bool enable) {
678+ Locker locker(_lock);
679+ qCDebug(controllers) << "Attempting to enable mapping " << mappingName;
680+ auto iterator = _mappingsByName.find(mappingName);
681+ if (_mappingsByName.end() == iterator) {
682+ qCWarning(controllers) << "Request to enable / disable unknown mapping " << mappingName;
683+ return;
684+ }
685+
686+ auto mapping = iterator->second;
687+ if (enable) {
688+ enableMapping(mapping);
689+ } else {
690+ disableMapping(mapping);
691+ }
692+}
693+
694+float UserInputMapper::getValue(const Endpoint::Pointer& endpoint) {
695+ return endpoint->value();
696+}
697+
698+float UserInputMapper::getValue(const Input& input) const {
699+ Locker locker(_lock);
700+ auto endpoint = endpointFor(input);
701+ if (!endpoint) {
702+ return 0;
703+ }
704+ return endpoint->value();
705+}
706+
707+Pose UserInputMapper::getPose(const Endpoint::Pointer& endpoint) {
708+ if (!endpoint->isPose()) {
709+ return Pose();
710+ }
711+ return endpoint->pose();
712+}
713+
714+Pose UserInputMapper::getPose(const Input& input) const {
715+ Locker locker(_lock);
716+ auto endpoint = endpointFor(input);
717+ if (!endpoint) {
718+ return Pose();
719+ }
720+ return getPose(endpoint);
721+}
722+
723+Mapping::Pointer UserInputMapper::loadMapping(const QString& jsonFile) {
724+ Locker locker(_lock);
725+ if (jsonFile.isEmpty()) {
726+ return Mapping::Pointer();
727+ }
728+ QString json;
729+ {
730+ QFile file(jsonFile);
731+ if (file.open(QFile::ReadOnly | QFile::Text)) {
732+ json = QTextStream(&file).readAll();
733+ }
734+ file.close();
735+ }
736+ return parseMapping(json);
737+}
738+
739+
740+
741+static const QString JSON_NAME = QStringLiteral("name");
742+static const QString JSON_CHANNELS = QStringLiteral("channels");
743+static const QString JSON_CHANNEL_FROM = QStringLiteral("from");
744+static const QString JSON_CHANNEL_DEBUG = QStringLiteral("debug");
745+static const QString JSON_CHANNEL_WHEN = QStringLiteral("when");
746+static const QString JSON_CHANNEL_TO = QStringLiteral("to");
747+static const QString JSON_CHANNEL_FILTERS = QStringLiteral("filters");
748+
749+Endpoint::Pointer UserInputMapper::parseEndpoint(const QJsonValue& value) {
750+ Endpoint::Pointer result;
751+ if (value.isString()) {
752+ auto input = findDeviceInput(value.toString());
753+ result = endpointFor(input);
754+ } else if (value.isArray()) {
755+ return parseAny(value);
756+ } else if (value.isObject()) {
757+ auto axisEndpoint = parseAxis(value);
758+ if (axisEndpoint) {
759+ return axisEndpoint;
760+ }
761+ // if we have other types of endpoints that are objects, follow the axisEndpoint example, and place them here
762+
763+ // Endpoint is defined as an object, we expect a js function then
764+ return Endpoint::Pointer();
765+ }
766+
767+ if (!result) {
768+ qWarning() << "Invalid endpoint definition " << value;
769+ }
770+ return result;
771+}
772+
773+
774+Conditional::Pointer UserInputMapper::conditionalFor(const QJSValue& condition) {
775+ return Conditional::Pointer();
776+}
777+
778+Conditional::Pointer UserInputMapper::conditionalFor(const QScriptValue& condition) {
779+ if (condition.isArray()) {
780+ int length = condition.property("length").toInteger();
781+ Conditional::List children;
782+ for (int i = 0; i < length; i++) {
783+ Conditional::Pointer destination = conditionalFor(condition.property(i));
784+ if (!destination) {
785+ return Conditional::Pointer();
786+ }
787+ children.push_back(destination);
788+ }
789+ return std::make_shared<AndConditional>(children);
790+ }
791+
792+ if (condition.isNumber()) {
793+ return conditionalFor(Input(condition.toInt32()));
794+ }
795+
796+ if (condition.isFunction()) {
797+ return std::make_shared<ScriptConditional>(condition);
798+ }
799+
800+ qWarning() << "Unsupported conditional type " << condition.toString();
801+ return Conditional::Pointer();
802+}
803+
804+Conditional::Pointer UserInputMapper::conditionalFor(const Input& inputId) const {
805+ Locker locker(_lock);
806+ auto iterator = _endpointsByInput.find(inputId);
807+ if (_endpointsByInput.end() == iterator) {
808+ qWarning() << "Unknown input: " << QString::number(inputId.getID(), 16);
809+ return Conditional::Pointer();
810+ }
811+ return std::make_shared<EndpointConditional>(iterator->second);
812+}
813+
814+Conditional::Pointer UserInputMapper::parseConditional(const QJsonValue& value) {
815+ if (value.isArray()) {
816+ // Support "when" : [ "GamePad.RB", "GamePad.LB" ]
817+ Conditional::List children;
818+ for (auto arrayItem : value.toArray()) {
819+ Conditional::Pointer childConditional = parseConditional(arrayItem);
820+ if (!childConditional) {
821+ return Conditional::Pointer();
822+ }
823+ children.push_back(childConditional);
824+ }
825+ return std::make_shared<AndConditional>(children);
826+ } else if (value.isString()) {
827+ // Support "when" : "GamePad.RB"
828+ auto input = findDeviceInput(value.toString());
829+ auto endpoint = endpointFor(input);
830+ if (!endpoint) {
831+ return Conditional::Pointer();
832+ }
833+
834+ return std::make_shared<EndpointConditional>(endpoint);
835+ }
836+
837+ return Conditional::parse(value);
838+}
839+
840+Filter::Pointer UserInputMapper::parseFilter(const QJsonValue& value) {
841+ Filter::Pointer result;
842+ if (value.isString()) {
843+ result = Filter::getFactory().create(value.toString());
844+ } else if (value.isObject()) {
845+ result = Filter::parse(value.toObject());
846+ }
847+
848+ if (!result) {
849+ qWarning() << "Invalid filter definition " << value;
850+ }
851+
852+ return result;
853+}
854+
855+Filter::List UserInputMapper::parseFilters(const QJsonValue& value) {
856+ if (value.isNull()) {
857+ return Filter::List();
858+ }
859+
860+ if (value.isArray()) {
861+ Filter::List result;
862+ auto filtersArray = value.toArray();
863+ for (auto filterValue : filtersArray) {
864+ Filter::Pointer filter = parseFilter(filterValue);
865+ if (!filter) {
866+ return Filter::List();
867+ }
868+ result.push_back(filter);
869+ }
870+ return result;
871+ }
872+
873+ Filter::Pointer filter = parseFilter(value);
874+ if (!filter) {
875+ return Filter::List();
876+ }
877+ return Filter::List({ filter });
878+}
879+
880+Endpoint::Pointer UserInputMapper::parseDestination(const QJsonValue& value) {
881+ if (value.isArray()) {
882+ ArrayEndpoint::Pointer result = std::make_shared<ArrayEndpoint>();
883+ for (auto arrayItem : value.toArray()) {
884+ Endpoint::Pointer destination = parseEndpoint(arrayItem);
885+ if (!destination) {
886+ return Endpoint::Pointer();
887+ }
888+ result->_children.push_back(destination);
889+ }
890+ return result;
891+ }
892+
893+ return parseEndpoint(value);
894+}
895+
896+Endpoint::Pointer UserInputMapper::parseAxis(const QJsonValue& value) {
897+ if (value.isObject()) {
898+ auto object = value.toObject();
899+ if (object.contains("makeAxis")) {
900+ auto axisValue = object.value("makeAxis");
901+ if (axisValue.isArray()) {
902+ auto axisArray = axisValue.toArray();
903+ static const int AXIS_ARRAY_SIZE = 2; // axis can only have 2 children
904+ if (axisArray.size() == AXIS_ARRAY_SIZE) {
905+ Endpoint::Pointer first = parseEndpoint(axisArray.first());
906+ Endpoint::Pointer second = parseEndpoint(axisArray.last());
907+ if (first && second) {
908+ return std::make_shared<CompositeEndpoint>(first, second);
909+ }
910+ }
911+ }
912+ }
913+ }
914+ return Endpoint::Pointer();
915+}
916+
917+Endpoint::Pointer UserInputMapper::parseAny(const QJsonValue& value) {
918+ if (value.isArray()) {
919+ Endpoint::List children;
920+ for (auto arrayItem : value.toArray()) {
921+ Endpoint::Pointer destination = parseEndpoint(arrayItem);
922+ if (!destination) {
923+ return Endpoint::Pointer();
924+ }
925+ children.push_back(destination);
926+ }
927+ return std::make_shared<AnyEndpoint>(children);
928+ }
929+ return Endpoint::Pointer();
930+}
931+
932+Endpoint::Pointer UserInputMapper::parseSource(const QJsonValue& value) {
933+ if (value.isObject()) {
934+ auto axisEndpoint = parseAxis(value);
935+ if (axisEndpoint) {
936+ return axisEndpoint;
937+ }
938+ // if we have other types of endpoints that are objects, follow the axisEndpoint example, and place them here
939+ } else if (value.isArray()) {
940+ return parseAny(value);
941+ }
942+ return parseEndpoint(value);
943+}
944+
945+Route::Pointer UserInputMapper::parseRoute(const QJsonValue& value) {
946+ if (!value.isObject()) {
947+ return Route::Pointer();
948+ }
949+
950+ auto obj = value.toObject();
951+ Route::Pointer result = std::make_shared<Route>();
952+
953+ result->json = QString(QJsonDocument(obj).toJson());
954+ result->source = parseSource(obj[JSON_CHANNEL_FROM]);
955+ result->debug = obj[JSON_CHANNEL_DEBUG].toBool();
956+ if (!result->source) {
957+ qWarning() << "Invalid route source " << obj[JSON_CHANNEL_FROM];
958+ return Route::Pointer();
959+ }
960+
961+
962+ result->destination = parseDestination(obj[JSON_CHANNEL_TO]);
963+ if (!result->destination) {
964+ qWarning() << "Invalid route destination " << obj[JSON_CHANNEL_TO];
965+ return Route::Pointer();
966+ }
967+
968+ if (result->source == result->destination) {
969+ qWarning() << "Loopback routes not supported " << obj;
970+ return Route::Pointer();
971+ }
972+
973+ if (obj.contains(JSON_CHANNEL_WHEN)) {
974+ auto conditionalsValue = obj[JSON_CHANNEL_WHEN];
975+ result->conditional = parseConditional(conditionalsValue);
976+ if (!result->conditional) {
977+ qWarning() << "Invalid route conditionals " << conditionalsValue;
978+ return Route::Pointer();
979+ }
980+ }
981+
982+ if (obj.contains(JSON_CHANNEL_FILTERS)) {
983+ auto filtersValue = obj[JSON_CHANNEL_FILTERS];
984+ result->filters = parseFilters(filtersValue);
985+ if (result->filters.empty()) {
986+ qWarning() << "Invalid route filters " << filtersValue;
987+ return Route::Pointer();
988+ }
989+ }
990+
991+ return result;
992+}
993+
994+Mapping::Pointer UserInputMapper::parseMapping(const QJsonValue& json) {
995+ if (!json.isObject()) {
996+ return Mapping::Pointer();
997+ }
998+
999+ auto obj = json.toObject();
1000+ auto mapping = std::make_shared<Mapping>("default");
1001+ mapping->name = obj[JSON_NAME].toString();
1002+ const auto& jsonChannels = obj[JSON_CHANNELS].toArray();
1003+ for (const auto& channelIt : jsonChannels) {
1004+ Route::Pointer route = parseRoute(channelIt);
1005+ if (!route) {
1006+ qWarning() << "Couldn't parse route";
1007+ continue;
1008+ }
1009+ mapping->routes.push_back(route);
1010+ }
1011+ return mapping;
1012+}
1013+
1014+Mapping::Pointer UserInputMapper::parseMapping(const QString& json) {
1015+ Mapping::Pointer result;
1016+ QJsonObject obj;
1017+ QJsonParseError error;
1018+ QJsonDocument doc = QJsonDocument::fromJson(json.toUtf8(), &error);
1019+ // check validity of the document
1020+ if (doc.isNull()) {
1021+ qDebug() << "Invalid JSON...\n";
1022+ qDebug() << error.errorString();
1023+ qDebug() << "JSON was:\n" << json << endl;
1024+ return Mapping::Pointer();
1025+ }
1026+
1027+ if (!doc.isObject()) {
1028+ qWarning() << "Mapping json Document is not an object" << endl;
1029+ qDebug() << "JSON was:\n" << json << endl;
1030+ return Mapping::Pointer();
1031+ }
1032+ return parseMapping(doc.object());
1033+}
1034+
1035+template <typename T>
1036+bool hasDebuggableRoute(const T& routes) {
1037+ for (auto route : routes) {
1038+ if (route->debug) {
1039+ return true;
1040+ }
1041+ }
1042+ return false;
1043+}
1044+
1045+
1046+void UserInputMapper::enableMapping(const Mapping::Pointer& mapping) {
1047+ Locker locker(_lock);
1048+ // New routes for a device get injected IN FRONT of existing routes. Routes
1049+ // are processed in order so this ensures that the standard -> action processing
1050+ // takes place after all of the hardware -> standard or hardware -> action processing
1051+ // because standard -> action is the first set of routes added.
1052+ Route::List standardRoutes = mapping->routes;
1053+ standardRoutes.remove_if([](const Route::Pointer& value) {
1054+ return (value->source->getInput().device != STANDARD_DEVICE);
1055+ });
1056+ _standardRoutes.insert(_standardRoutes.begin(), standardRoutes.begin(), standardRoutes.end());
1057+
1058+ Route::List deviceRoutes = mapping->routes;
1059+ deviceRoutes.remove_if([](const Route::Pointer& value) {
1060+ return (value->source->getInput().device == STANDARD_DEVICE);
1061+ });
1062+ _deviceRoutes.insert(_deviceRoutes.begin(), deviceRoutes.begin(), deviceRoutes.end());
1063+
1064+ if (!debuggableRoutes) {
1065+ debuggableRoutes = hasDebuggableRoute(_deviceRoutes) || hasDebuggableRoute(_standardRoutes);
1066+ }
1067+}
1068+
1069+void UserInputMapper::disableMapping(const Mapping::Pointer& mapping) {
1070+ Locker locker(_lock);
1071+ const auto& deviceRoutes = mapping->routes;
1072+ std::set<Route::Pointer> routeSet(deviceRoutes.begin(), deviceRoutes.end());
1073+ _deviceRoutes.remove_if([&](const Route::Pointer& value){
1074+ return routeSet.count(value) != 0;
1075+ });
1076+ _standardRoutes.remove_if([&](const Route::Pointer& value) {
1077+ return routeSet.count(value) != 0;
1078+ });
1079+
1080+ if (debuggableRoutes) {
1081+ debuggableRoutes = hasDebuggableRoute(_deviceRoutes) || hasDebuggableRoute(_standardRoutes);
1082+ }
1083+}
1084+
1085+}
1086+
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+
34+namespace controller {
35+
36+ class RouteBuilderProxy;
37+ class MappingBuilderProxy;
38+
39+ class UserInputMapper : public QObject, public Dependency {
40+ Q_OBJECT
41+ SINGLETON_DEPENDENCY
42+ Q_ENUMS(Action)
43+
44+ public:
45+ // FIXME move to unordered set / map
46+ using EndpointToInputMap = std::map<EndpointPointer, Input>;
47+ using MappingNameMap = std::map<QString, MappingPointer>;
48+ using MappingDeviceMap = std::map<uint16_t, MappingPointer>;
49+ using MappingStack = std::list<MappingPointer>;
50+ using InputToEndpointMap = std::map<Input, EndpointPointer>;
51+ using EndpointSet = std::unordered_set<EndpointPointer>;
52+ using EndpointPair = std::pair<EndpointPointer, EndpointPointer>;
53+ using EndpointPairMap = std::map<EndpointPair, EndpointPointer>;
54+ using DevicesMap = std::map<int, InputDevice::Pointer>;
55+ using uint16 = uint16_t;
56+ using uint32 = uint32_t;
57+
58+ static const uint16_t ACTIONS_DEVICE;
59+ static const uint16_t STANDARD_DEVICE;
60+
61+ UserInputMapper();
62+ virtual ~UserInputMapper();
63+
64+
65+ static void registerControllerTypes(QScriptEngine* engine);
66+
67+ void registerDevice(InputDevice::Pointer device);
68+ InputDevice::Pointer getDevice(const Input& input);
69+ QString getDeviceName(uint16 deviceID);
70+
71+ Input::NamedVector getAvailableInputs(uint16 deviceID) const;
72+ Input::NamedVector getActionInputs() const { return getAvailableInputs(ACTIONS_DEVICE); }
73+ Input::NamedVector getStandardInputs() const { return getAvailableInputs(STANDARD_DEVICE); }
74+
75+ int findDevice(QString name) const;
76+ QVector<QString> getDeviceNames();
77+ Input findDeviceInput(const QString& inputName) const;
78+
79+ QVector<Action> getAllActions() const;
80+ QString getActionName(Action action) const;
81+ float getActionState(Action action) const { return _actionStates[toInt(action)]; }
82+ Pose getPoseState(Action action) const { return _poseStates[toInt(action)]; }
83+ int findAction(const QString& actionName) const;
84+ QVector<QString> getActionNames() const;
85+ Input inputFromAction(Action action) const { return getActionInputs()[toInt(action)].first; }
86+
87+ void setActionState(Action action, float value) { _actionStates[toInt(action)] = value; }
88+ void deltaActionState(Action action, float delta) { _actionStates[toInt(action)] += delta; }
89+ void setActionState(Action action, const Pose& value) { _poseStates[toInt(action)] = value; }
90+
91+ static Input makeStandardInput(controller::StandardButtonChannel button);
92+ static Input makeStandardInput(controller::StandardAxisChannel axis);
93+ static Input makeStandardInput(controller::StandardPoseChannel pose);
94+
95+ void removeDevice(int device);
96+
97+ // Update means go grab all the device input channels and update the output channel values
98+ void update(float deltaTime);
99+
100+ void setSensorToWorldMat(glm::mat4 sensorToWorldMat) { _sensorToWorldMat = sensorToWorldMat; }
101+ glm::mat4 getSensorToWorldMat() { return _sensorToWorldMat; }
102+
103+ DevicesMap getDevices() { return _registeredDevices; }
104+ uint16 getStandardDeviceID() const { return STANDARD_DEVICE; }
105+ InputDevice::Pointer getStandardDevice() { return _registeredDevices[getStandardDeviceID()]; }
106+
107+ MappingPointer newMapping(const QString& mappingName);
108+ MappingPointer parseMapping(const QString& json);
109+ MappingPointer loadMapping(const QString& jsonFile);
110+
111+ void loadDefaultMapping(uint16 deviceID);
112+ void enableMapping(const QString& mappingName, bool enable = true);
113+ float getValue(const Input& input) const;
114+ Pose getPose(const Input& input) const;
115+
116+ signals:
117+ void actionEvent(int action, float state);
118+ void inputEvent(int input, float state);
119+ void hardwareChanged();
120+
121+ protected:
122+ // GetFreeDeviceID should be called before registering a device to use an ID not used by a different device.
123+ uint16 getFreeDeviceID() { return _nextFreeDeviceID++; }
124+ DevicesMap _registeredDevices;
125+ uint16 _nextFreeDeviceID = STANDARD_DEVICE + 1;
126+
127+ std::vector<float> _actionStates = std::vector<float>(toInt(Action::NUM_ACTIONS), 0.0f);
128+ std::vector<float> _actionScales = std::vector<float>(toInt(Action::NUM_ACTIONS), 1.0f);
129+ std::vector<float> _lastActionStates = std::vector<float>(toInt(Action::NUM_ACTIONS), 0.0f);
130+ std::vector<Pose> _poseStates = std::vector<Pose>(toInt(Action::NUM_ACTIONS));
131+ std::vector<float> _lastStandardStates = std::vector<float>();
132+
133+ glm::mat4 _sensorToWorldMat;
134+
135+ int recordDeviceOfType(const QString& deviceName);
136+ QHash<const QString&, int> _deviceCounts;
137+
138+ static float getValue(const EndpointPointer& endpoint);
139+ static Pose getPose(const EndpointPointer& endpoint);
140+
141+ friend class RouteBuilderProxy;
142+ friend class MappingBuilderProxy;
143+
144+ void runMappings();
145+
146+ static void applyRoutes(const RouteList& route);
147+ static bool applyRoute(const RoutePointer& route, bool force = false);
148+ void enableMapping(const MappingPointer& mapping);
149+ void disableMapping(const MappingPointer& mapping);
150+ EndpointPointer endpointFor(const QJSValue& endpoint);
151+ EndpointPointer endpointFor(const QScriptValue& endpoint);
152+ EndpointPointer endpointFor(const Input& endpoint) const;
153+ EndpointPointer compositeEndpointFor(EndpointPointer first, EndpointPointer second);
154+ ConditionalPointer conditionalFor(const QJSValue& endpoint);
155+ ConditionalPointer conditionalFor(const QScriptValue& endpoint);
156+ ConditionalPointer conditionalFor(const Input& endpoint) const;
157+
158+ MappingPointer parseMapping(const QJsonValue& json);
159+ RoutePointer parseRoute(const QJsonValue& value);
160+ EndpointPointer parseDestination(const QJsonValue& value);
161+ EndpointPointer parseSource(const QJsonValue& value);
162+ EndpointPointer parseAxis(const QJsonValue& value);
163+ EndpointPointer parseAny(const QJsonValue& value);
164+ EndpointPointer parseEndpoint(const QJsonValue& value);
165+ ConditionalPointer parseConditional(const QJsonValue& value);
166+
167+ static FilterPointer parseFilter(const QJsonValue& value);
168+ static FilterList parseFilters(const QJsonValue& value);
169+
170+ InputToEndpointMap _endpointsByInput;
171+ EndpointToInputMap _inputsByEndpoint;
172+ EndpointPairMap _compositeEndpoints;
173+
174+ MappingNameMap _mappingsByName;
175+ MappingDeviceMap _mappingsByDevice;
176+
177+ RouteList _deviceRoutes;
178+ RouteList _standardRoutes;
179+
180+ using Locker = std::unique_lock<std::recursive_mutex>;
181+
182+ mutable std::recursive_mutex _lock;
183+ };
184+
185+}
186+
187+Q_DECLARE_METATYPE(controller::Input::NamedPair)
188+Q_DECLARE_METATYPE(controller::Pose)
189+Q_DECLARE_METATYPE(QVector<controller::Input::NamedPair>)
190+Q_DECLARE_METATYPE(controller::Input)
191+Q_DECLARE_METATYPE(controller::Action)
192+Q_DECLARE_METATYPE(QVector<controller::Action>)
193+
194+// Cheating.
195+using UserInputMapper = controller::UserInputMapper;
196+
197+#endif // hifi_UserInputMapper_h
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+
15+namespace 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+
20+class QJsonValue;
21+
22+namespace controller {
23+ /*
24+ * encapsulates a source, destination and filters to apply
25+ */
26+ class Conditional {
27+ public:
28+ using Pointer = std::shared_ptr<Conditional>;
29+ using List = std::list<Pointer>;
30+ using Factory = hifi::SimpleFactory<Conditional, QString>;
31+ using Lambda = std::function<bool()>;
32+
33+ virtual bool satisfied() = 0;
34+ virtual bool parseParameters(const QJsonValue& parameters) { return true; }
35+
36+ static Pointer parse(const QJsonValue& json);
37+ static void registerBuilder(const QString& name, Factory::Builder builder);
38+ static Factory& getFactory() { return _factory; }
39+ protected:
40+ static Factory _factory;
41+ };
42+
43+}
44+
45+#define REGISTER_CONDITIONAL_CLASS(classEntry) \
46+ private: \
47+ using Registrar = Conditional::Factory::Registrar<classEntry>; \
48+ static Registrar _registrar;
49+
50+#define REGISTER_CONDITIONAL_CLASS_INSTANCE(classEntry, className) \
51+ classEntry::Registrar classEntry::_registrar(className, Conditional::getFactory());
52+
53+
54+#endif
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+
11+namespace 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+
22+class QScriptValue;
23+
24+namespace controller {
25+ /*
26+ * Encapsulates a particular input / output,
27+ * i.e. Hydra.Button0, Standard.X, Action.Yaw
28+ */
29+ class Endpoint : public QObject {
30+ Q_OBJECT;
31+ public:
32+ using Pointer = std::shared_ptr<Endpoint>;
33+ using List = std::list<Pointer>;
34+ using Pair = std::pair<Pointer, Pointer>;
35+ using ReadLambda = std::function<float()>;
36+ using WriteLambda = std::function<void(float)>;
37+
38+ Endpoint(const Input& input) : _input(input) {}
39+ virtual float value() = 0;
40+ virtual void apply(float value, const Pointer& source) = 0;
41+ virtual Pose pose() { return Pose(); }
42+ virtual void apply(const Pose& value, const Pointer& source) {}
43+ virtual const bool isPose() { return _input.isPose(); }
44+
45+ virtual bool writeable() const { return true; }
46+ virtual bool readable() const { return true; }
47+ virtual void reset() { }
48+
49+ const Input& getInput() { return _input; }
50+
51+ protected:
52+ Input _input;
53+ };
54+
55+ class LambdaEndpoint : public Endpoint {
56+ public:
57+ LambdaEndpoint(ReadLambda readLambda, WriteLambda writeLambda = [](float) {})
58+ : Endpoint(Input::INVALID_INPUT), _readLambda(readLambda), _writeLambda(writeLambda) { }
59+
60+ virtual float value() override { return _readLambda(); }
61+ virtual void apply(float value, const Pointer& source) override { _writeLambda(value); }
62+
63+ private:
64+ ReadLambda _readLambda;
65+ WriteLambda _writeLambda;
66+ };
67+
68+
69+ class VirtualEndpoint : public Endpoint {
70+ public:
71+ VirtualEndpoint(const Input& id = Input::INVALID_INPUT)
72+ : Endpoint(id) {
73+ }
74+
75+ virtual float value() override { return _currentValue; }
76+ virtual void apply(float value, const Pointer& source) override { _currentValue = value; }
77+
78+ virtual Pose pose() override { return _currentPose; }
79+ virtual void apply(const Pose& value, const Pointer& source) override {
80+ _currentPose = value;
81+ }
82+ protected:
83+ float _currentValue { 0.0f };
84+ Pose _currentPose {};
85+ };
86+
87+}
88+
89+#endif
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+
28+using namespace controller;
29+
30+Filter::Factory Filter::_factory;
31+
32+REGISTER_FILTER_CLASS_INSTANCE(ClampFilter, "clamp")
33+REGISTER_FILTER_CLASS_INSTANCE(ConstrainToIntegerFilter, "constrainToInteger")
34+REGISTER_FILTER_CLASS_INSTANCE(ConstrainToPositiveIntegerFilter, "constrainToPositiveInteger")
35+REGISTER_FILTER_CLASS_INSTANCE(DeadZoneFilter, "deadZone")
36+REGISTER_FILTER_CLASS_INSTANCE(HysteresisFilter, "hysteresis")
37+REGISTER_FILTER_CLASS_INSTANCE(InvertFilter, "invert")
38+REGISTER_FILTER_CLASS_INSTANCE(ScaleFilter, "scale")
39+REGISTER_FILTER_CLASS_INSTANCE(PulseFilter, "pulse")
40+
41+const QString JSON_FILTER_TYPE = QStringLiteral("type");
42+const QString JSON_FILTER_PARAMS = QStringLiteral("params");
43+
44+
45+Filter::Pointer Filter::parse(const QJsonValue& json) {
46+ Filter::Pointer filter;
47+ if (json.isString()) {
48+ filter = Filter::getFactory().create(json.toString());
49+ } else if (json.isObject()) {
50+ QJsonObject jsonObj = json.toObject();
51+ // The filter is an object, now let s check for type and potential arguments
52+ auto filterType = jsonObj[JSON_FILTER_TYPE];
53+ filter = Filter::getFactory().create(filterType.toString());
54+ if (filter) {
55+ QJsonValue params = jsonObj;
56+ if (jsonObj.contains(JSON_FILTER_PARAMS)) {
57+ params = jsonObj[JSON_FILTER_PARAMS];
58+ }
59+ if (!filter->parseParameters(params)) {
60+ qWarning() << "Unable to parse filter parameters " << params;
61+ return Filter::Pointer();
62+ }
63+ }
64+ }
65+ return filter;
66+}
67+
68+bool Filter::parseSingleFloatParameter(const QJsonValue& parameters, const QString& name, float& output) {
69+ if (parameters.isDouble()) {
70+ output = parameters.toDouble();
71+ return true;
72+ } else if (parameters.isArray()) {
73+ auto arrayParameters = parameters.toArray();
74+ if (arrayParameters.size() > 1) {
75+ output = arrayParameters[0].toDouble();
76+ return true;
77+ }
78+ } else if (parameters.isObject()) {
79+ static const QString JSON_MIN = QStringLiteral("interval");
80+ auto objectParameters = parameters.toObject();
81+ if (objectParameters.contains(name)) {
82+ output = objectParameters[name].toDouble();
83+ return true;
84+ }
85+ }
86+ return false;
87+}
88+
89+
90+
91+#if 0
92+
93+namespace controller {
94+
95+ class LambdaFilter : public Filter {
96+ public:
97+ // LambdaFilter() {}12
98+ LambdaFilter(Lambda f) : _function(f) {};
99+
100+ virtual float apply(float value) const {
101+ return _function(value);
102+ }
103+
104+ virtual bool parseParameters(const QJsonArray& parameters) { return true; }
105+
106+ // REGISTER_FILTER_CLASS(LambdaFilter);
107+ private:
108+ Lambda _function;
109+ };
110+
111+ class ScriptFilter : public Filter {
112+ public:
113+
114+ };
115+
116+
117+
118+ //class EasingFilter : public Filter {
119+ //public:
120+ // virtual float apply(float value) const override;
121+
122+ //private:
123+ // QEasingCurve _curve;
124+ //};
125+
126+ //// GLSL style filters
127+ //class StepFilter : public Filter {
128+ //public:
129+ // StepFilter(float edge) : _edge(edge) {};
130+ // virtual float apply(float value) const override;
131+
132+ //private:
133+ // const float _edge;
134+ //};
135+
136+ //class PowFilter : public Filter {
137+ //public:
138+ // PowFilter(float exponent) : _exponent(exponent) {};
139+ // virtual float apply(float value) const override;
140+
141+ //private:
142+ // const float _exponent;
143+ //};
144+
145+ //class AbsFilter : public Filter {
146+ //public:
147+ // virtual float apply(float value) const override;
148+ //};
149+
150+ //class SignFilter : public Filter {
151+ //public:
152+ // virtual float apply(float value) const override;
153+ //};
154+
155+ //class FloorFilter : public Filter {
156+ //public:
157+ // virtual float apply(float value) const override {
158+ // return floor(newValue);
159+ // }
160+ //};
161+
162+ //class CeilFilter : public Filter {
163+ //public:
164+ // virtual float apply(float value) const override {
165+ // return ceil(newValue);
166+ // }
167+ //};
168+
169+ //class FractFilter : public Filter {
170+ //public:
171+ // virtual float apply(float value) const override {
172+ // return fract(newValue);
173+ // }
174+ //};
175+
176+ //class MinFilter : public Filter {
177+ //public:
178+ // MinFilter(float mine) : _min(min) {};
179+
180+ // virtual float apply(float value) const override {
181+ // return glm::min(_min, newValue);
182+ // }
183+
184+ //private:
185+ // const float _min;
186+ //};
187+
188+ //class MaxFilter : public Filter {
189+ //public:
190+ // MaxFilter(float max) : _max(max) {};
191+ // virtual float apply(float newValue, float oldValue) override;
192+ //private:
193+ // const float _max;
194+ //};
195+}
196+#endif
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+
24+class QJsonValue;
25+
26+namespace controller {
27+
28+ // Encapsulates part of a filter chain
29+ class Filter {
30+ public:
31+ using Pointer = std::shared_ptr<Filter>;
32+ using List = std::list<Pointer>;
33+ using Lambda = std::function<float(float)>;
34+ using Factory = hifi::SimpleFactory<Filter, QString>;
35+
36+ virtual float apply(float value) const = 0;
37+ // Factory features
38+ virtual bool parseParameters(const QJsonValue& parameters) { return true; }
39+
40+ static Pointer parse(const QJsonValue& json);
41+ static void registerBuilder(const QString& name, Factory::Builder builder);
42+ static Factory& getFactory() { return _factory; }
43+
44+ static bool parseSingleFloatParameter(const QJsonValue& parameters, const QString& name, float& output);
45+ protected:
46+ static Factory _factory;
47+ };
48+}
49+
50+#define REGISTER_FILTER_CLASS(classEntry) \
51+ private: \
52+ using Registrar = Filter::Factory::Registrar<classEntry>; \
53+ static Registrar _registrar;
54+
55+#define REGISTER_FILTER_CLASS_INSTANCE(classEntry, className) \
56+ classEntry::Registrar classEntry::_registrar(className, Filter::getFactory());
57+
58+
59+#endif
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+
22+namespace controller {
23+
24+ class Mapping {
25+ public:
26+ using Pointer = std::shared_ptr<Mapping>;
27+ using List = Route::List;
28+
29+ Mapping(const QString& name) : name(name) {}
30+
31+ List routes;
32+ QString name;
33+ };
34+
35+}
36+
37+#endif
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+
21+using namespace controller;
22+
23+QObject* MappingBuilderProxy::fromQml(const QJSValue& source) {
24+ qCDebug(controllers) << "Creating new Route builder proxy from " << source.toString();
25+ auto sourceEndpoint = _parent.endpointFor(source);
26+ return from(sourceEndpoint);
27+}
28+
29+QObject* MappingBuilderProxy::from(const QScriptValue& source) {
30+ qCDebug(controllers) << "Creating new Route builder proxy from " << source.toString();
31+ auto sourceEndpoint = _parent.endpointFor(source);
32+ return from(sourceEndpoint);
33+}
34+
35+QObject* MappingBuilderProxy::from(const Endpoint::Pointer& source) {
36+ if (source) {
37+ auto route = Route::Pointer(new Route());
38+ route->source = source;
39+ return new RouteBuilderProxy(_parent, _mapping, route);
40+ } else {
41+ qCDebug(controllers) << "MappingBuilderProxy::from : source is null so no route created";
42+ return nullptr;
43+ }
44+}
45+
46+QObject* MappingBuilderProxy::makeAxisQml(const QJSValue& source1, const QJSValue& source2) {
47+ auto source1Endpoint = _parent.endpointFor(source1);
48+ auto source2Endpoint = _parent.endpointFor(source2);
49+ return from(_parent.compositeEndpointFor(source1Endpoint, source2Endpoint));
50+}
51+
52+QObject* MappingBuilderProxy::makeAxis(const QScriptValue& source1, const QScriptValue& source2) {
53+ auto source1Endpoint = _parent.endpointFor(source1);
54+ auto source2Endpoint = _parent.endpointFor(source2);
55+ return from(_parent.compositeEndpointFor(source1Endpoint, source2Endpoint));
56+}
57+
58+QObject* MappingBuilderProxy::enable(bool enable) {
59+ _parent.enableMapping(_mapping->name, enable);
60+ return this;
61+}
62+
63+
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+
18+class QJSValue;
19+class QScriptValue;
20+class QJsonValue;
21+
22+namespace controller {
23+
24+class ScriptingInterface;
25+class UserInputMapper;
26+
27+// TODO migrate functionality to a MappingBuilder class and make the proxy defer to that
28+// (for easier use in both C++ and JS)
29+class MappingBuilderProxy : public QObject {
30+ Q_OBJECT
31+public:
32+ MappingBuilderProxy(UserInputMapper& parent, Mapping::Pointer mapping)
33+ : _parent(parent), _mapping(mapping) { }
34+
35+ Q_INVOKABLE QObject* fromQml(const QJSValue& source);
36+ Q_INVOKABLE QObject* makeAxisQml(const QJSValue& source1, const QJSValue& source2);
37+
38+ Q_INVOKABLE QObject* from(const QScriptValue& source);
39+ Q_INVOKABLE QObject* makeAxis(const QScriptValue& source1, const QScriptValue& source2);
40+
41+ Q_INVOKABLE QObject* enable(bool enable = true);
42+ Q_INVOKABLE QObject* disable() { return enable(false); }
43+
44+protected:
45+ QObject* from(const Endpoint::Pointer& source);
46+
47+ friend class RouteBuilderProxy;
48+ UserInputMapper& _parent;
49+ Mapping::Pointer _mapping;
50+
51+
52+ void parseRoute(const QJsonValue& json);
53+
54+};
55+
56+}
57+
58+#endif
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+
17+namespace controller {
18+ /*
19+ * encapsulates a source, destination and filters to apply
20+ */
21+ class Route {
22+ public:
23+ Endpoint::Pointer source;
24+ Endpoint::Pointer destination;
25+ Conditional::Pointer conditional;
26+ Filter::List filters;
27+ QString json;
28+ bool debug { false };
29+
30+ using Pointer = std::shared_ptr<Route>;
31+ using List = std::list<Pointer>;
32+ };
33+}
34+
35+#endif
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+
30+using namespace controller;
31+
32+void RouteBuilderProxy::toQml(const QJSValue& destination) {
33+ qCDebug(controllers) << "Completing route " << destination.toString();
34+ auto destinationEndpoint = _parent.endpointFor(destination);
35+ return to(destinationEndpoint);
36+}
37+
38+void RouteBuilderProxy::to(const QScriptValue& destination) {
39+ qCDebug(controllers) << "Completing route " << destination.toString();
40+ auto destinationEndpoint = _parent.endpointFor(destination);
41+ return to(destinationEndpoint);
42+}
43+
44+void RouteBuilderProxy::to(const Endpoint::Pointer& destination) {
45+ _route->destination = destination;
46+ _mapping->routes.push_back(_route);
47+ deleteLater();
48+}
49+
50+QObject* RouteBuilderProxy::debug(bool enable) {
51+ _route->debug = enable;
52+ return this;
53+}
54+
55+QObject* RouteBuilderProxy::when(const QScriptValue& expression) {
56+ _route->conditional = _parent.conditionalFor(expression);
57+ return this;
58+}
59+
60+QObject* RouteBuilderProxy::whenQml(const QJSValue& expression) {
61+ _route->conditional = _parent.conditionalFor(expression);
62+ return this;
63+}
64+
65+QObject* RouteBuilderProxy::clamp(float min, float max) {
66+ addFilter(std::make_shared<ClampFilter>(min, max));
67+ return this;
68+}
69+
70+QObject* RouteBuilderProxy::scale(float multiplier) {
71+ addFilter(std::make_shared<ScaleFilter>(multiplier));
72+ return this;
73+}
74+
75+QObject* RouteBuilderProxy::invert() {
76+ addFilter(std::make_shared<InvertFilter>());
77+ return this;
78+}
79+
80+QObject* RouteBuilderProxy::hysteresis(float min, float max) {
81+ addFilter(std::make_shared<HysteresisFilter>(min, max));
82+ return this;
83+}
84+
85+QObject* RouteBuilderProxy::deadZone(float min) {
86+ addFilter(std::make_shared<DeadZoneFilter>(min));
87+ return this;
88+}
89+
90+QObject* RouteBuilderProxy::constrainToInteger() {
91+ addFilter(std::make_shared<ConstrainToIntegerFilter>());
92+ return this;
93+}
94+
95+QObject* RouteBuilderProxy::constrainToPositiveInteger() {
96+ addFilter(std::make_shared<ConstrainToPositiveIntegerFilter>());
97+ return this;
98+}
99+
100+QObject* RouteBuilderProxy::pulse(float interval) {
101+ addFilter(std::make_shared<PulseFilter>(interval));
102+ return this;
103+}
104+
105+void RouteBuilderProxy::addFilter(Filter::Pointer filter) {
106+ _route->filters.push_back(filter);
107+}
108+
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+
20+class QJSValue;
21+class QScriptValue;
22+class QJsonValue;
23+
24+namespace controller {
25+
26+class ScriptingInterface;
27+
28+// TODO migrate functionality to a RouteBuilder class and make the proxy defer to that
29+// (for easier use in both C++ and JS)
30+class RouteBuilderProxy : public QObject {
31+ Q_OBJECT
32+ public:
33+ RouteBuilderProxy(UserInputMapper& parent, Mapping::Pointer mapping, Route::Pointer route)
34+ : _parent(parent), _mapping(mapping), _route(route) { }
35+
36+ Q_INVOKABLE void toQml(const QJSValue& destination);
37+ Q_INVOKABLE QObject* whenQml(const QJSValue& expression);
38+
39+ Q_INVOKABLE void to(const QScriptValue& destination);
40+ Q_INVOKABLE QObject* debug(bool enable = true);
41+ Q_INVOKABLE QObject* when(const QScriptValue& expression);
42+ Q_INVOKABLE QObject* clamp(float min, float max);
43+ Q_INVOKABLE QObject* hysteresis(float min, float max);
44+ Q_INVOKABLE QObject* pulse(float interval);
45+ Q_INVOKABLE QObject* scale(float multiplier);
46+ Q_INVOKABLE QObject* invert();
47+ Q_INVOKABLE QObject* deadZone(float min);
48+ Q_INVOKABLE QObject* constrainToInteger();
49+ Q_INVOKABLE QObject* constrainToPositiveInteger();
50+
51+private:
52+ void to(const Endpoint::Pointer& destination);
53+ void conditional(const Conditional::Pointer& conditional);
54+ void addFilter(Filter::Pointer filter);
55+ UserInputMapper& _parent;
56+ Mapping::Pointer _mapping;
57+ Route::Pointer _route;
58+ };
59+
60+}
61+#endif
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+
11+using namespace controller;
12+
13+bool AndConditional::satisfied() {
14+ for (auto& conditional : _children) {
15+ if (!conditional->satisfied()) {
16+ return false;
17+ }
18+ }
19+ return true;
20+}
21+
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+
15+namespace controller {
16+
17+class AndConditional : public Conditional {
18+public:
19+ using Pointer = std::shared_ptr<AndConditional>;
20+
21+ AndConditional(Conditional::List children) : _children(children) { }
22+
23+ virtual bool satisfied() override;
24+
25+private:
26+ Conditional::List _children;
27+};
28+
29+}
30+
31+#endif
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+
16+namespace controller {
17+
18+class EndpointConditional : public Conditional {
19+public:
20+ EndpointConditional(Endpoint::Pointer endpoint) : _endpoint(endpoint) {}
21+ virtual bool satisfied() override { return _endpoint && _endpoint->value() != 0.0; }
22+private:
23+ Endpoint::Pointer _endpoint;
24+};
25+
26+}
27+#endif
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+
13+using namespace controller;
14+
15+bool ScriptConditional::satisfied() {
16+ updateValue();
17+ return _lastValue;
18+}
19+
20+void ScriptConditional::updateValue() {
21+ if (QThread::currentThread() != thread()) {
22+ QMetaObject::invokeMethod(this, "updateValue", Qt::QueuedConnection);
23+ return;
24+ }
25+
26+ _lastValue = _callable.call().toBool();
27+}
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+
19+namespace controller {
20+
21+class ScriptConditional : public QObject, public Conditional {
22+ Q_OBJECT;
23+public:
24+ ScriptConditional(const QScriptValue& callable) : _callable(callable) { }
25+ virtual bool satisfied() override;
26+protected:
27+ Q_INVOKABLE void updateValue();
28+private:
29+ QScriptValue _callable;
30+ bool _lastValue { false };
31+};
32+
33+}
34+#endif
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+
15+using namespace controller;
16+
17+void ActionEndpoint::apply(float newValue, const Pointer& source) {
18+ _currentValue += newValue;
19+ if (_input != Input::INVALID_INPUT) {
20+ auto userInputMapper = DependencyManager::get<UserInputMapper>();
21+ userInputMapper->deltaActionState(Action(_input.getChannel()), newValue);
22+ }
23+}
24+
25+void ActionEndpoint::apply(const Pose& value, const Pointer& source) {
26+ _currentPose = value;
27+ if (!_currentPose.isValid()) {
28+ return;
29+ }
30+ if (_input != Input::INVALID_INPUT) {
31+ auto userInputMapper = DependencyManager::get<UserInputMapper>();
32+ userInputMapper->setActionState(Action(_input.getChannel()), _currentPose);
33+ }
34+}
35+
36+void ActionEndpoint::reset() {
37+ _currentValue = 0.0f;
38+ _currentPose = Pose();
39+}
40+
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+
20+namespace controller {
21+
22+class ActionEndpoint : public Endpoint {
23+public:
24+ ActionEndpoint(const Input& id = Input::INVALID_INPUT) : Endpoint(id) { }
25+
26+ virtual float value() override { return _currentValue; }
27+ virtual void apply(float newValue, const Pointer& source) override;
28+
29+ virtual Pose pose() override { return _currentPose; }
30+ virtual void apply(const Pose& value, const Pointer& source) override;
31+
32+ virtual void reset() override;
33+
34+private:
35+ float _currentValue{ 0.0f };
36+ Pose _currentPose{};
37+};
38+
39+}
40+
41+#endif
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+
13+using namespace controller;
14+
15+AnyEndpoint::AnyEndpoint(Endpoint::List children) : Endpoint(Input::INVALID_INPUT), _children(children) {
16+ bool standard = true;
17+ // Ensure if we're building a composite of standard devices the composite itself
18+ // is treated as a standard device for rule processing order
19+ for (auto endpoint : children) {
20+ if (endpoint->getInput().device != UserInputMapper::STANDARD_DEVICE) {
21+ standard = false;
22+ break;
23+ }
24+ }
25+ if (standard) {
26+ this->_input.device = UserInputMapper::STANDARD_DEVICE;
27+ }
28+}
29+
30+float AnyEndpoint::value() {
31+ float result = 0;
32+ for (auto& child : _children) {
33+ float childResult = child->value();
34+ if (childResult != 0.0f) {
35+ result = childResult;
36+ }
37+ }
38+ return result;
39+}
40+
41+void AnyEndpoint::apply(float newValue, const Endpoint::Pointer& source) {
42+ qFatal("AnyEndpoint is read only");
43+}
44+
45+// AnyEndpoint is used for reading, so return false if any child returns false (has been written to)
46+bool AnyEndpoint::writeable() const {
47+ for (auto& child : _children) {
48+ if (!child->writeable()) {
49+ return false;
50+ }
51+ }
52+ return true;
53+}
54+
55+bool AnyEndpoint::readable() const {
56+ for (auto& child : _children) {
57+ if (!child->readable()) {
58+ return false;
59+ }
60+ }
61+ return true;
62+}
63+
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+
15+namespace controller {
16+
17+class AnyEndpoint : public Endpoint {
18+ friend class UserInputMapper;
19+public:
20+ AnyEndpoint(Endpoint::List children);
21+ virtual float value() override;
22+ virtual void apply(float newValue, const Endpoint::Pointer& source) override;
23+ virtual bool writeable() const override;
24+ virtual bool readable() const override;
25+
26+private:
27+ Endpoint::List _children;
28+};
29+
30+}
31+
32+#endif
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+
15+namespace controller {
16+
17+class ArrayEndpoint : public Endpoint {
18+ friend class UserInputMapper;
19+public:
20+ using Pointer = std::shared_ptr<ArrayEndpoint>;
21+ ArrayEndpoint() : Endpoint(Input::INVALID_INPUT) { }
22+
23+ virtual float value() override {
24+ return 0.0;
25+ }
26+
27+ virtual void apply(float value, const Endpoint::Pointer& source) override {
28+ for (auto& child : _children) {
29+ if (child->writeable()) {
30+ child->apply(value, source);
31+ }
32+ }
33+ }
34+
35+ virtual bool readable() const override { return false; }
36+
37+private:
38+ Endpoint::List _children;
39+};
40+
41+}
42+
43+#endif
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+
13+namespace controller {
14+
15+CompositeEndpoint::CompositeEndpoint(Endpoint::Pointer first, Endpoint::Pointer second)
16+ : Endpoint(Input::INVALID_INPUT), Pair(first, second) {
17+ if (first->getInput().device == UserInputMapper::STANDARD_DEVICE &&
18+ second->getInput().device == UserInputMapper::STANDARD_DEVICE) {
19+ this->_input.device = UserInputMapper::STANDARD_DEVICE;
20+ }
21+}
22+
23+bool CompositeEndpoint::readable() const {
24+ return first->readable() && second->readable();
25+}
26+
27+float CompositeEndpoint::value() {
28+ float result = first->value() * -1.0f + second->value();
29+ return result;
30+}
31+
32+void CompositeEndpoint::apply(float newValue, const Pointer& source) {
33+ // Composites are read only
34+}
35+
36+}
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+
15+namespace controller {
16+ class CompositeEndpoint : public Endpoint, Endpoint::Pair {
17+ public:
18+ CompositeEndpoint(Endpoint::Pointer first, Endpoint::Pointer second);
19+
20+ virtual float value() override;
21+ virtual void apply(float newValue, const Pointer& source) override;
22+ virtual bool readable() const override;
23+
24+ };
25+
26+}
27+
28+#endif
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+
15+using namespace controller;
16+float InputEndpoint::value(){
17+ _read = true;
18+ if (isPose()) {
19+ return pose().valid ? 1.0f : 0.0f;
20+ }
21+ auto userInputMapper = DependencyManager::get<UserInputMapper>();
22+ auto deviceProxy = userInputMapper->getDevice(_input);
23+ if (!deviceProxy) {
24+ return 0.0f;
25+ }
26+ return deviceProxy->getValue(_input);
27+}
28+
29+Pose InputEndpoint::pose() {
30+ _read = true;
31+ if (!isPose()) {
32+ return Pose();
33+ }
34+ auto userInputMapper = DependencyManager::get<UserInputMapper>();
35+ auto deviceProxy = userInputMapper->getDevice(_input);
36+ if (!deviceProxy) {
37+ return Pose();
38+ }
39+ return deviceProxy->getPose(_input.channel);
40+}
41+
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+
15+namespace controller {
16+
17+class InputEndpoint : public Endpoint {
18+public:
19+ InputEndpoint(const Input& id = Input::INVALID_INPUT)
20+ : Endpoint(id) {
21+ }
22+
23+ virtual float value() override;
24+ // FIXME need support for writing back to vibration / force feedback effects
25+ virtual void apply(float newValue, const Pointer& source) override {}
26+ virtual Pose pose() override;
27+ virtual void apply(const Pose& value, const Pointer& source) override { }
28+
29+ virtual bool writeable() const { return false; }
30+ virtual bool readable() const { return !_read; }
31+ virtual void reset() { _read = false; }
32+
33+private:
34+ bool _read { false };
35+};
36+
37+}
38+
39+#endif
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+
18+namespace controller {
19+
20+class JSEndpoint : public Endpoint {
21+public:
22+ JSEndpoint(const QJSValue& callable)
23+ : Endpoint(Input::INVALID_INPUT), _callable(callable) {
24+ }
25+
26+ virtual float value() {
27+ float result = (float)_callable.call().toNumber();
28+ return result;
29+ }
30+
31+ virtual void apply(float newValue, const Pointer& source) {
32+ _callable.call(QJSValueList({ QJSValue(newValue) }));
33+ }
34+
35+private:
36+ QJSValue _callable;
37+};
38+
39+}
40+
41+#endif
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+
13+using namespace controller;
14+
15+float ScriptEndpoint::value() {
16+ updateValue();
17+ return _lastValueRead;
18+}
19+
20+void ScriptEndpoint::updateValue() {
21+ if (QThread::currentThread() != thread()) {
22+ QMetaObject::invokeMethod(this, "updateValue", Qt::QueuedConnection);
23+ return;
24+ }
25+
26+ _lastValueRead = (float)_callable.call().toNumber();
27+}
28+
29+void ScriptEndpoint::apply(float value, const Pointer& source) {
30+ if (value == _lastValueWritten) {
31+ return;
32+ }
33+ internalApply(value, source->getInput().getID());
34+}
35+
36+void ScriptEndpoint::internalApply(float value, int sourceID) {
37+ _lastValueWritten = value;
38+ if (QThread::currentThread() != thread()) {
39+ QMetaObject::invokeMethod(this, "internalApply", Qt::QueuedConnection,
40+ Q_ARG(float, value),
41+ Q_ARG(int, sourceID));
42+ return;
43+ }
44+ _callable.call(QScriptValue(),
45+ QScriptValueList({ QScriptValue(value), QScriptValue(sourceID) }));
46+}
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+
17+namespace controller {
18+
19+class ScriptEndpoint : public Endpoint {
20+ Q_OBJECT;
21+public:
22+ ScriptEndpoint(const QScriptValue& callable)
23+ : Endpoint(Input::INVALID_INPUT), _callable(callable) {
24+ }
25+
26+ virtual float value();
27+ virtual void apply(float newValue, const Pointer& source);
28+
29+protected:
30+ Q_INVOKABLE void updateValue();
31+ Q_INVOKABLE virtual void internalApply(float newValue, int sourceID);
32+private:
33+ QScriptValue _callable;
34+ float _lastValueRead { 0.0f };
35+ float _lastValueWritten { 0.0f };
36+};
37+
38+}
39+
40+#endif
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+
15+namespace controller {
16+
17+class StandardEndpoint : public VirtualEndpoint {
18+public:
19+ StandardEndpoint(const Input& input) : VirtualEndpoint(input) {}
20+ virtual bool writeable() const override { return !_written; }
21+ virtual bool readable() const override { return !_read; }
22+ virtual void reset() override {
23+ apply(0.0f, Endpoint::Pointer());
24+ apply(Pose(), Endpoint::Pointer());
25+ _written = _read = false;
26+ }
27+
28+ virtual float value() override {
29+ _read = true;
30+ return VirtualEndpoint::value();
31+ }
32+
33+ virtual void apply(float value, const Pointer& source) override {
34+ // For standard endpoints, the first NON-ZERO write counts.
35+ if (value != 0.0) {
36+ _written = true;
37+ }
38+ VirtualEndpoint::apply(value, source);
39+ }
40+
41+ virtual Pose pose() override {
42+ _read = true;
43+ return VirtualEndpoint::pose();
44+ }
45+
46+ virtual void apply(const Pose& value, const Pointer& source) override {
47+ if (value != Pose()) {
48+ _written = true;
49+ }
50+ VirtualEndpoint::apply(value, source);
51+ }
52+
53+private:
54+ bool _written { false };
55+ bool _read { false };
56+};
57+
58+}
59+
60+#endif
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+
14+using namespace controller;
15+
16+bool ClampFilter::parseParameters(const QJsonValue& parameters) {
17+ if (parameters.isArray()) {
18+ auto arrayParameters = parameters.toArray();
19+ if (arrayParameters.size() > 1) {
20+ _min = arrayParameters[0].toDouble();
21+ }
22+ if (arrayParameters.size() > 2) {
23+ _max = arrayParameters[1].toDouble();
24+ }
25+ } else if (parameters.isObject()) {
26+ static const QString JSON_MAX = QStringLiteral("max");
27+ static const QString JSON_MIN = QStringLiteral("min");
28+
29+ auto objectParameters = parameters.toObject();
30+ if (objectParameters.contains(JSON_MIN)) {
31+ _min = objectParameters[JSON_MIN].toDouble();
32+ }
33+ if (objectParameters.contains(JSON_MAX)) {
34+ _max = objectParameters[JSON_MAX].toDouble();
35+ }
36+ }
37+ return true;
38+}
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+
15+namespace controller {
16+
17+class ClampFilter : public Filter {
18+ REGISTER_FILTER_CLASS(ClampFilter);
19+public:
20+ ClampFilter(float min = 0.0, float max = 1.0) : _min(min), _max(max) {};
21+ virtual float apply(float value) const override {
22+ return glm::clamp(value, _min, _max);
23+ }
24+ virtual bool parseParameters(const QJsonValue& parameters) override;
25+protected:
26+ float _min = 0.0f;
27+ float _max = 1.0f;
28+};
29+
30+}
31+
32+#endif
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+
15+namespace controller {
16+
17+class ConstrainToIntegerFilter : public Filter {
18+ REGISTER_FILTER_CLASS(ConstrainToIntegerFilter);
19+public:
20+ ConstrainToIntegerFilter() {};
21+
22+ virtual float apply(float value) const override {
23+ return glm::sign(value);
24+ }
25+protected:
26+};
27+
28+}
29+
30+#endif
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+
15+namespace controller {
16+
17+class ConstrainToPositiveIntegerFilter : public Filter {
18+ REGISTER_FILTER_CLASS(ConstrainToPositiveIntegerFilter);
19+public:
20+ ConstrainToPositiveIntegerFilter() {};
21+
22+ virtual float apply(float value) const override {
23+ return (value <= 0.0f) ? 0.0f : 1.0f;
24+ }
25+protected:
26+};
27+
28+}
29+
30+#endif
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+
14+using namespace controller;
15+float DeadZoneFilter::apply(float value) const {
16+ float scale = 1.0f / (1.0f - _min);
17+ if (std::abs(value) < _min) {
18+ return 0.0f;
19+ }
20+ return (value - _min) * scale;
21+}
22+
23+bool DeadZoneFilter::parseParameters(const QJsonValue& parameters) {
24+ static const QString JSON_MIN = QStringLiteral("min");
25+ return parseSingleFloatParameter(parameters, JSON_MIN, _min);
26+}
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+
15+namespace controller {
16+
17+class DeadZoneFilter : public Filter {
18+ REGISTER_FILTER_CLASS(DeadZoneFilter);
19+public:
20+ DeadZoneFilter(float min = 0.0) : _min(min) {};
21+
22+ virtual float apply(float value) const override;
23+ virtual bool parseParameters(const QJsonValue& parameters) override;
24+protected:
25+ float _min = 0.0f;
26+};
27+
28+
29+}
30+
31+#endif
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+
14+using namespace controller;
15+
16+HysteresisFilter::HysteresisFilter(float min, float max) : _min(min), _max(max) {
17+ if (_min > _max) {
18+ std::swap(_min, _max);
19+ }
20+};
21+
22+
23+float HysteresisFilter::apply(float value) const {
24+ if (_signaled) {
25+ if (value <= _min) {
26+ _signaled = false;
27+ }
28+ } else {
29+ if (value >= _max) {
30+ _signaled = true;
31+ }
32+ }
33+ return _signaled ? 1.0f : 0.0f;
34+}
35+
36+bool HysteresisFilter::parseParameters(const QJsonValue& parameters) {
37+ if (parameters.isArray()) {
38+ auto arrayParameters = parameters.toArray();
39+ if (arrayParameters.size() > 1) {
40+ _min = arrayParameters[0].toDouble();
41+ }
42+ if (arrayParameters.size() > 2) {
43+ _max = arrayParameters[1].toDouble();
44+ }
45+ } else if (parameters.isObject()) {
46+ static const QString JSON_MAX = QStringLiteral("max");
47+ static const QString JSON_MIN = QStringLiteral("min");
48+
49+ auto objectParameters = parameters.toObject();
50+ if (objectParameters.contains(JSON_MIN)) {
51+ _min = objectParameters[JSON_MIN].toDouble();
52+ }
53+ if (objectParameters.contains(JSON_MAX)) {
54+ _max = objectParameters[JSON_MAX].toDouble();
55+ }
56+ } else {
57+ return false;
58+ }
59+
60+ if (_min > _max) {
61+ std::swap(_min, _max);
62+ }
63+ return true;
64+}
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+
15+namespace controller {
16+
17+class HysteresisFilter : public Filter {
18+ REGISTER_FILTER_CLASS(HysteresisFilter);
19+public:
20+ HysteresisFilter(float min = 0.25, float max = 0.75);
21+ virtual float apply(float value) const override;
22+ virtual bool parseParameters(const QJsonValue& parameters) override;
23+protected:
24+ float _min;
25+ float _max;
26+ mutable bool _signaled { false };
27+};
28+
29+}
30+
31+#endif
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+
15+namespace controller {
16+
17+class InvertFilter : public ScaleFilter {
18+ REGISTER_FILTER_CLASS(InvertFilter);
19+public:
20+ InvertFilter() : ScaleFilter(-1.0f) {}
21+
22+ virtual bool parseParameters(const QJsonArray& parameters) { return true; }
23+
24+private:
25+};
26+
27+}
28+
29+#endif
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+
14+using namespace controller;
15+
16+
17+
18+float PulseFilter::apply(float value) const {
19+ float result = 0.0f;
20+
21+ if (0.0f != value) {
22+ float now = secTimestampNow();
23+ float delta = now - _lastEmitTime;
24+ if (delta >= _interval) {
25+ _lastEmitTime = now;
26+ result = value;
27+ }
28+ }
29+
30+ return result;
31+}
32+
33+bool PulseFilter::parseParameters(const QJsonValue& parameters) {
34+ static const QString JSON_MIN = QStringLiteral("interval");
35+ return parseSingleFloatParameter(parameters, JSON_MIN, _interval);
36+}
37+
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+
15+namespace controller {
16+
17+
18+class PulseFilter : public Filter {
19+ REGISTER_FILTER_CLASS(PulseFilter);
20+public:
21+ PulseFilter() {}
22+ PulseFilter(float interval) : _interval(interval) {}
23+
24+
25+ virtual float apply(float value) const override;
26+
27+ virtual bool parseParameters(const QJsonValue& parameters);
28+
29+private:
30+ mutable float _lastEmitTime { -::std::numeric_limits<float>::max() };
31+ float _interval = 1.0f;
32+};
33+
34+}
35+
36+#endif
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+
14+using namespace controller;
15+
16+bool ScaleFilter::parseParameters(const QJsonValue& parameters) {
17+ static const QString JSON_SCALE = QStringLiteral("scale");
18+ return parseSingleFloatParameter(parameters, JSON_SCALE, _scale);
19+}
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+
15+namespace controller {
16+
17+class ScaleFilter : public Filter {
18+ REGISTER_FILTER_CLASS(ScaleFilter);
19+public:
20+ ScaleFilter() {}
21+ ScaleFilter(float scale) : _scale(scale) {}
22+
23+ virtual float apply(float value) const override {
24+ return value * _scale;
25+ }
26+ virtual bool parseParameters(const QJsonValue& parameters);
27+
28+private:
29+ float _scale = 1.0f;
30+};
31+
32+}
33+
34+#endif
libraries/display-plugins/src/display-plugins/NullDisplayPlugin.cpp+0−3
@@ -27,7 +27,4 @@ void NullDisplayPlugin::preRender() {} · 26 unmodified lines
2727 void NullDisplayPlugin::preDisplay() {}
2828 void NullDisplayPlugin::display(GLuint sceneTexture, const glm::uvec2& sceneSize) {}
2929 void NullDisplayPlugin::finishFrame() {}
30−
31−void NullDisplayPlugin::activate() {}
32−void NullDisplayPlugin::deactivate() {}
3330 void NullDisplayPlugin::stop() {}
libraries/display-plugins/src/display-plugins/NullDisplayPlugin.h+0−2
@@ -15,8 +15,6 @@ public: · 14 unmodified lines
1515 virtual ~NullDisplayPlugin() final {}
1616 virtual const QString & getName() const override;
1717
18− void activate() override;
19− void deactivate() override;
2018 void stop() override;
2119
2220 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
5757 }
5858
5959 void OpenGLDisplayPlugin::activate() {
60− _active = true;
60+ DisplayPlugin::activate();
6161 _timer.start(1);
6262 }
6363
6464 void OpenGLDisplayPlugin::stop() {
65− _active = false;
65+ DisplayPlugin::activate();
6666 _timer.stop();
6767 }
6868
libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h+0−1
@@ -44,7 +44,6 @@ protected: · 43 unmodified lines
4444 mutable QTimer _timer;
4545 ProgramPtr _program;
4646 ShapeWrapperPtr _plane;
47− bool _active{ false };
4847 bool _vsyncSupported{ false };
4948 };
5049
libraries/entities-renderer/src/EntityTreeRenderer.cpp+1−2
@@ -107,8 +107,7 @@ void EntityTreeRenderer::init() { · 106 unmodified lines
107107 entityTree->setFBXService(this);
108108
109109 if (_wantScripts) {
110− _entitiesScriptEngine = new ScriptEngine(NO_SCRIPT, "Entities",
111− _scriptingServices->getControllerScriptingInterface());
110+ _entitiesScriptEngine = new ScriptEngine(NO_SCRIPT, "Entities");
112111 _scriptingServices->registerScriptEngineWithApplicationServices(_entitiesScriptEngine);
113112 _entitiesScriptEngine->runInThread();
114113 DependencyManager::get<EntityScriptingInterface>()->setEntitiesScriptEngine(_entitiesScriptEngine);
libraries/entities/src/EntityPropertyFlags.h+1−1
@@ -189,7 +189,7 @@ enum EntityPropertyList { · 188 unmodified lines
189189 PROP_BACKGROUND_MODE = PROP_MODEL_URL,
190190 PROP_SKYBOX_COLOR = PROP_ANIMATION_URL,
191191 PROP_SKYBOX_URL = PROP_ANIMATION_FPS,
192− PROP_KEYLIGHT_AMBIENT_URL = PROP_ANIMATION_FRAME_INDEX,
192+ PROP_KEYLIGHT_AMBIENT_URL = PROP_ANIMATION_PLAYING,
193193
194194 // Aliases/Piggyback properties for Web. These properties intentionally reuse the enum values for
195195 // 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
4444 qDebug() << "GL Renderer: " << QString((const char*) glGetString(GL_RENDERER));
4545 });
4646
47− QOpenGLContext * currentContext = QOpenGLContext::currentContext();
48− Q_ASSERT(_context == currentContext);
47+ Q_ASSERT(_context == QOpenGLContext::currentContext());
4948
5049 return makeCurrentResult;
5150 }
libraries/gl/src/gl/OffscreenQmlSurface.cpp+6−0
@@ -19,6 +19,7 @@ · 18 unmodified lines
1919 #include <QMutex>
2020
2121 #include <PerfStat.h>
22+#include <DependencyManager.h>
2223 #include <NumericalConstants.h>
2324
2425 #include "GLEscrow.h"
@@ -611,3 +612,8 @@ QQuickWindow* OffscreenQmlSurface::getWindow() { · 586 unmodified lines
611612 QSize OffscreenQmlSurface::size() const {
612613 return _renderer->_quickWindow->geometry().size();
613614 }
615+
616+QQmlContext* OffscreenQmlSurface::getRootContext() {
617+ return _qmlEngine->rootContext();
618+}
619+
libraries/gl/src/gl/OffscreenQmlSurface.h+1−0
@@ -60,6 +60,7 @@ public: · 59 unmodified lines
6060 QQuickItem* getRootItem();
6161 QQuickWindow* getWindow();
6262 QObject* getEventHandler();
63+ QQmlContext* getRootContext();
6364
6465 QPointF mapToVirtualScreen(const QPointF& originalPoint, QObject* originalWidget);
6566 virtual bool eventFilter(QObject* originalDestination, QEvent* event);
libraries/input-plugins/CMakeLists.txt+3−53
@@ -1,6 +1,6 @@
11 set(TARGET_NAME input-plugins)
22 setup_hifi_library()
3−link_hifi_libraries(shared plugins gpu render-utils)
3+link_hifi_libraries(shared plugins controllers script-engine render-utils)
44
55 GroupSources("src/input-plugins")
66
@@ -11,55 +11,5 @@ if (WIN32) · 4 unmodified lines
1111 target_link_libraries(${TARGET_NAME} ${OPENVR_LIBRARIES})
1212 endif()
1313
14−#add_dependency_external_projects(Sixense)
15−#find_package(Sixense REQUIRED)
16−#target_include_directories(${TARGET_NAME} PRIVATE ${SIXENSE_INCLUDE_DIRS})
17−#target_link_libraries(${TARGET_NAME} ${SIXENSE_LIBRARIES})
18−
19−# set a default root dir for each of our optional externals if it was not passed
20−set(OPTIONAL_EXTERNALS "SDL2" "Sixense")
21−foreach(EXTERNAL ${OPTIONAL_EXTERNALS})
22− string(TOUPPER ${EXTERNAL} ${EXTERNAL}_UPPERCASE)
23− if (NOT ${${EXTERNAL}_UPPERCASE}_ROOT_DIR)
24− string(TOLOWER ${EXTERNAL} ${EXTERNAL}_LOWERCASE)
25− set(${${EXTERNAL}_UPPERCASE}_ROOT_DIR "${CMAKE_SOURCE_DIR}/interface/external/${${EXTERNAL}_LOWERCASE}")
26− endif ()
27−endforeach()
28−
29−# perform standard include and linking for found externals
30−foreach(EXTERNAL ${OPTIONAL_EXTERNALS})
31−
32− if (${${EXTERNAL}_UPPERCASE}_REQUIRED)
33− find_package(${EXTERNAL} REQUIRED)
34− else ()
35− find_package(${EXTERNAL})
36− endif ()
37−
38− if (${${EXTERNAL}_UPPERCASE}_FOUND AND NOT DISABLE_${${EXTERNAL}_UPPERCASE})
39− add_definitions(-DHAVE_${${EXTERNAL}_UPPERCASE})
40−
41− # include the library directories (ignoring warnings)
42− if (NOT ${${EXTERNAL}_UPPERCASE}_INCLUDE_DIRS)
43− set(${${EXTERNAL}_UPPERCASE}_INCLUDE_DIRS ${${${EXTERNAL}_UPPERCASE}_INCLUDE_DIR})
44− endif ()
45−
46− include_directories(SYSTEM ${${${EXTERNAL}_UPPERCASE}_INCLUDE_DIRS})
47−
48− # perform the system include hack for OS X to ignore warnings
49− if (APPLE)
50− foreach(EXTERNAL_INCLUDE_DIR ${${${EXTERNAL}_UPPERCASE}_INCLUDE_DIRS})
51− SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -isystem ${EXTERNAL_INCLUDE_DIR}")
52− endforeach()
53− endif ()
54−
55− if (NOT ${${EXTERNAL}_UPPERCASE}_LIBRARIES)
56− set(${${EXTERNAL}_UPPERCASE}_LIBRARIES ${${${EXTERNAL}_UPPERCASE}_LIBRARY})
57− endif ()
58−
59− if (NOT APPLE OR NOT ${${EXTERNAL}_UPPERCASE} MATCHES "SIXENSE")
60− target_link_libraries(${TARGET_NAME} ${${${EXTERNAL}_UPPERCASE}_LIBRARIES})
61− elseif (APPLE AND NOT INSTALLER_BUILD)
62− add_definitions(-DSIXENSE_LIB_FILENAME=\"${${${EXTERNAL}_UPPERCASE}_LIBRARY_RELEASE}\")
63− endif ()
64− endif ()
65−endforeach()
14+target_sdl2()
15+target_sixense()
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−
13−bool InputDevice::_lowVelocityFilter = false;
14−
15−const float DEFAULT_HAND_RETICLE_MOVE_SPEED = 37.5f;
16−float InputDevice::reticleMoveSpeed = DEFAULT_HAND_RETICLE_MOVE_SPEED;
17−
18−//Constants for getCursorPixelRangeMultiplier()
19−const float MIN_PIXEL_RANGE_MULT = 0.4f;
20−const float MAX_PIXEL_RANGE_MULT = 2.0f;
21−const float RANGE_MULT = (MAX_PIXEL_RANGE_MULT - MIN_PIXEL_RANGE_MULT) * 0.01f;
22−
23−//Returns a multiplier to be applied to the cursor range for the controllers
24−float InputDevice::getCursorPixelRangeMult() {
25− //scales (0,100) to (MINIMUM_PIXEL_RANGE_MULT, MAXIMUM_PIXEL_RANGE_MULT)
26− return InputDevice::reticleMoveSpeed * RANGE_MULT + MIN_PIXEL_RANGE_MULT;
27−}
28−
29−float InputDevice::getButton(int channel) const {
30− if (!_buttonPressedMap.empty()) {
31− if (_buttonPressedMap.find(channel) != _buttonPressedMap.end()) {
32− return 1.0f;
33− }
34− else {
35− return 0.0f;
36− }
37− }
38− return 0.0f;
39−}
40−
41−float InputDevice::getAxis(int channel) const {
42− auto axis = _axisStateMap.find(channel);
43− if (axis != _axisStateMap.end()) {
44− return (*axis).second;
45− }
46− else {
47− return 0.0f;
48− }
49−}
50−
51−UserInputMapper::PoseValue InputDevice::getPose(int channel) const {
52− auto pose = _poseStateMap.find(channel);
53− if (pose != _poseStateMap.end()) {
54− return (*pose).second;
55− }
56− else {
57− return UserInputMapper::PoseValue();
58− }
59−}
libraries/input-plugins/src/input-plugins/Joystick.cpp+59−156
@@ -9,19 +9,17 @@ · 8 unmodified lines
99 // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
1010 //
1111
12−#include <limits>
13−
14−#include <glm/glm.hpp>
15−
1612 #include "Joystick.h"
1713
14+#include <PathUtils.h>
15+
1816 const float CONTROLLER_THRESHOLD = 0.3f;
1917
2018 #ifdef HAVE_SDL2
2119 const float MAX_AXIS = 32768.0f;
2220
23−Joystick::Joystick(SDL_JoystickID instanceId, const QString& name, SDL_GameController* sdlGameController) :
24− InputDevice(name),
21+Joystick::Joystick(SDL_JoystickID instanceId, SDL_GameController* sdlGameController) :
22+ InputDevice("GamePad"),
2523 _sdlGameController(sdlGameController),
2624 _sdlJoystick(SDL_GameControllerGetJoystick(_sdlGameController)),
2725 _instanceId(instanceId)
@@ -55,39 +53,14 @@ void Joystick::focusOutEvent() { · 27 unmodified lines
5553 };
5654
5755 #ifdef HAVE_SDL2
56+
5857 void Joystick::handleAxisEvent(const SDL_ControllerAxisEvent& event) {
5958 SDL_GameControllerAxis axis = (SDL_GameControllerAxis) event.axis;
60−
61− switch (axis) {
62− case SDL_CONTROLLER_AXIS_LEFTX:
63− _axisStateMap[makeInput(LEFT_AXIS_X_POS).getChannel()] = (event.value > 0.0f) ? event.value / MAX_AXIS : 0.0f;
64− _axisStateMap[makeInput(LEFT_AXIS_X_NEG).getChannel()] = (event.value < 0.0f) ? -event.value / MAX_AXIS : 0.0f;
65− break;
66− case SDL_CONTROLLER_AXIS_LEFTY:
67− _axisStateMap[makeInput(LEFT_AXIS_Y_POS).getChannel()] = (event.value > 0.0f) ? event.value / MAX_AXIS : 0.0f;
68− _axisStateMap[makeInput(LEFT_AXIS_Y_NEG).getChannel()] = (event.value < 0.0f) ? -event.value / MAX_AXIS : 0.0f;
69− break;
70− case SDL_CONTROLLER_AXIS_RIGHTX:
71− _axisStateMap[makeInput(RIGHT_AXIS_X_POS).getChannel()] = (event.value > 0.0f) ? event.value / MAX_AXIS : 0.0f;
72− _axisStateMap[makeInput(RIGHT_AXIS_X_NEG).getChannel()] = (event.value < 0.0f) ? -event.value / MAX_AXIS : 0.0f;
73− break;
74− case SDL_CONTROLLER_AXIS_RIGHTY:
75− _axisStateMap[makeInput(RIGHT_AXIS_Y_POS).getChannel()] = (event.value > 0.0f) ? event.value / MAX_AXIS : 0.0f;
76− _axisStateMap[makeInput(RIGHT_AXIS_Y_NEG).getChannel()] = (event.value < 0.0f) ? -event.value / MAX_AXIS : 0.0f;
77− break;
78− case SDL_CONTROLLER_AXIS_TRIGGERRIGHT:
79− _axisStateMap[makeInput(RIGHT_SHOULDER).getChannel()] = event.value / MAX_AXIS;
80− break;
81− case SDL_CONTROLLER_AXIS_TRIGGERLEFT:
82− _axisStateMap[makeInput(LEFT_SHOULDER).getChannel()] = event.value / MAX_AXIS;
83− break;
84− default:
85− break;
86− }
59+ _axisStateMap[makeInput((controller::StandardAxisChannel)axis).getChannel()] = (float)event.value / MAX_AXIS;
8760 }
8861
8962 void Joystick::handleButtonEvent(const SDL_ControllerButtonEvent& event) {
90− auto input = makeInput((SDL_GameControllerButton) event.button);
63+ auto input = makeInput((controller::StandardButtonChannel)event.button);
9164 bool newValue = event.state == SDL_PRESSED;
9265 if (newValue) {
9366 _buttonPressedMap.insert(input.getChannel());
@@ -95,131 +68,61 @@ void Joystick::handleButtonEvent(const SDL_ControllerButtonEvent& event) { · 1 unmodified line
9568 _buttonPressedMap.erase(input.getChannel());
9669 }
9770 }
98−#endif
99−
100−
101−void Joystick::registerToUserInputMapper(UserInputMapper& mapper) {
102− // Grab the current free device ID
103− _deviceID = mapper.getFreeDeviceID();
104−
105− auto proxy = std::make_shared<UserInputMapper::DeviceProxy>(_name);
106− proxy->getButton = [this] (const UserInputMapper::Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); };
107− proxy->getAxis = [this] (const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); };
108− proxy->getAvailabeInputs = [this] () -> QVector<UserInputMapper::InputPair> {
109− QVector<UserInputMapper::InputPair> availableInputs;
110−#ifdef HAVE_SDL2
111− availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_A), "Bottom Button"));
112− availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_B), "Right Button"));
113− availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_X), "Left Button"));
114− availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_Y), "Top Button"));
115−
116− availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_DPAD_UP), "DPad Up"));
117− availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_DPAD_DOWN), "DPad Down"));
118− availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_DPAD_LEFT), "DPad Left"));
119− availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_DPAD_RIGHT), "DPad Right"));
120−
121− availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_LEFTSHOULDER), "L1"));
122− availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), "R1"));
123− availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_SHOULDER), "L2"));
124− availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_SHOULDER), "R2"));
125−
126− availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_AXIS_Y_NEG), "Left Stick Up"));
127− availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_AXIS_Y_POS), "Left Stick Down"));
128− availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_AXIS_X_POS), "Left Stick Right"));
129− availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_AXIS_X_NEG), "Left Stick Left"));
130− availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_AXIS_Y_NEG), "Right Stick Up"));
131− availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_AXIS_Y_POS), "Right Stick Down"));
132− availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_AXIS_X_POS), "Right Stick Right"));
133− availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_AXIS_X_NEG), "Right Stick Left"));
13471
13572 #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−}
14573
146−void Joystick::assignDefaultInputMapping(UserInputMapper& mapper) {
147−#ifdef HAVE_SDL2
148− const float JOYSTICK_MOVE_SPEED = 1.0f;
149− const float DPAD_MOVE_SPEED = 0.5f;
150− const float JOYSTICK_YAW_SPEED = 0.5f;
151− const float JOYSTICK_PITCH_SPEED = 0.25f;
152− const float BOOM_SPEED = 0.1f;
153−
154− // Y axes are flipped (up is negative)
155− // Left Joystick: Movement, strafing
156− mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(LEFT_AXIS_Y_NEG), JOYSTICK_MOVE_SPEED);
157− mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(LEFT_AXIS_Y_POS), JOYSTICK_MOVE_SPEED);
158− mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(LEFT_AXIS_X_POS), JOYSTICK_MOVE_SPEED);
159− mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(LEFT_AXIS_X_NEG), JOYSTICK_MOVE_SPEED);
160−
161− // Right Joystick: Camera orientation
162− mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(RIGHT_AXIS_X_POS), JOYSTICK_YAW_SPEED);
163− mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(RIGHT_AXIS_X_NEG), JOYSTICK_YAW_SPEED);
164− mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(RIGHT_AXIS_Y_NEG), JOYSTICK_PITCH_SPEED);
165− mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(RIGHT_AXIS_Y_POS), JOYSTICK_PITCH_SPEED);
166−
167− // Dpad movement
168− mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(SDL_CONTROLLER_BUTTON_DPAD_UP), DPAD_MOVE_SPEED);
169− mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(SDL_CONTROLLER_BUTTON_DPAD_DOWN), DPAD_MOVE_SPEED);
170− mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(SDL_CONTROLLER_BUTTON_DPAD_RIGHT), DPAD_MOVE_SPEED);
171− mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(SDL_CONTROLLER_BUTTON_DPAD_LEFT), DPAD_MOVE_SPEED);
172−
173− // Button controls
174− mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(SDL_CONTROLLER_BUTTON_Y), DPAD_MOVE_SPEED);
175− mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(SDL_CONTROLLER_BUTTON_X), DPAD_MOVE_SPEED);
176−
177− // Zoom
178− mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(RIGHT_SHOULDER), BOOM_SPEED);
179− mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(LEFT_SHOULDER), BOOM_SPEED);
180−
181−
182− // Hold front right shoulder button for precision controls
183− // Left Joystick: Movement, strafing
184− mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(LEFT_AXIS_Y_NEG), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_MOVE_SPEED/2.0f);
185− mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(LEFT_AXIS_Y_POS), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_MOVE_SPEED/2.0f);
186− mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(LEFT_AXIS_X_POS), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_MOVE_SPEED/2.0f);
187− mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(LEFT_AXIS_X_NEG), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_MOVE_SPEED/2.0f);
188−
189− // Right Joystick: Camera orientation
190− mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(RIGHT_AXIS_X_POS), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_YAW_SPEED/2.0f);
191− mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(RIGHT_AXIS_X_NEG), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_YAW_SPEED/2.0f);
192− mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(RIGHT_AXIS_Y_NEG), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_PITCH_SPEED/2.0f);
193− mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(RIGHT_AXIS_Y_POS), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_PITCH_SPEED/2.0f);
194−
195− // Dpad movement
196− mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(SDL_CONTROLLER_BUTTON_DPAD_UP), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f);
197− mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(SDL_CONTROLLER_BUTTON_DPAD_DOWN), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f);
198− mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(SDL_CONTROLLER_BUTTON_DPAD_RIGHT), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f);
199− mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(SDL_CONTROLLER_BUTTON_DPAD_LEFT), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f);
200−
201− // Button controls
202− mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(SDL_CONTROLLER_BUTTON_Y), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f);
203− mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(SDL_CONTROLLER_BUTTON_X), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f);
204−
205− // Zoom
206− mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(RIGHT_SHOULDER), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), BOOM_SPEED/2.0f);
207− mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(LEFT_SHOULDER), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), BOOM_SPEED/2.0f);
208−
209− mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(SDL_CONTROLLER_BUTTON_LEFTSHOULDER));
210−
211− mapper.addInputChannel(UserInputMapper::ACTION1, makeInput(SDL_CONTROLLER_BUTTON_B));
212− mapper.addInputChannel(UserInputMapper::ACTION2, makeInput(SDL_CONTROLLER_BUTTON_A));
213−#endif
214−}
215−
216−#ifdef HAVE_SDL2
217−UserInputMapper::Input Joystick::makeInput(SDL_GameControllerButton button) {
218− return UserInputMapper::Input(_deviceID, button, UserInputMapper::ChannelType::BUTTON);
74+controller::Input::NamedVector Joystick::getAvailableInputs() const {
75+ using namespace controller;
76+ static const Input::NamedVector availableInputs{
77+ makePair(A, "A"),
78+ makePair(B, "B"),
79+ makePair(X, "X"),
80+ makePair(Y, "Y"),
81+ // DPad
82+ makePair(DU, "DU"),
83+ makePair(DD, "DD"),
84+ makePair(DL, "DL"),
85+ makePair(DR, "DR"),
86+ // Bumpers
87+ makePair(LB, "LB"),
88+ makePair(RB, "RB"),
89+ // Stick press
90+ makePair(LS, "LS"),
91+ makePair(RS, "RS"),
92+ // Center buttons
93+ makePair(START, "Start"),
94+ makePair(BACK, "Back"),
95+ // Analog sticks
96+ makePair(LX, "LX"),
97+ makePair(LY, "LY"),
98+ makePair(RX, "RX"),
99+ makePair(RY, "RY"),
100+
101+ // Triggers
102+ makePair(LT, "LT"),
103+ makePair(RT, "RT"),
104+
105+ // Aliases, PlayStation style names
106+ makePair(LB, "L1"),
107+ makePair(RB, "R1"),
108+ makePair(LT, "L2"),
109+ makePair(RT, "R2"),
110+ makePair(LS, "L3"),
111+ makePair(RS, "R3"),
112+ makePair(BACK, "Select"),
113+ makePair(A, "Cross"),
114+ makePair(B, "Circle"),
115+ makePair(X, "Square"),
116+ makePair(Y, "Triangle"),
117+ makePair(DU, "Up"),
118+ makePair(DD, "Down"),
119+ makePair(DL, "Left"),
120+ makePair(DR, "Right"),
121+ };
122+ return availableInputs;
219123 }
220−#endif
221124
222−UserInputMapper::Input Joystick::makeInput(Joystick::JoystickAxisChannel axis) {
223− return UserInputMapper::Input(_deviceID, axis, UserInputMapper::ChannelType::AXIS);
125+QString Joystick::getDefaultMappingConfig() const {
126+ static const QString MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/xbox.json";
127+ return MAPPING_JSON;
224128 }
225−
libraries/input-plugins/src/input-plugins/Joystick.h+8−23
@@ -20,9 +20,10 @@ · 19 unmodified lines
2020 #undef main
2121 #endif
2222
23−#include "InputDevice.h"
23+#include <controllers/InputDevice.h>
24+#include <controllers/StandardControls.h>
2425
25−class Joystick : public QObject, public InputDevice {
26+class Joystick : public QObject, public controller::InputDevice {
2627 Q_OBJECT
2728 Q_PROPERTY(QString name READ getName)
2829
@@ -31,37 +32,21 @@ class Joystick : public QObject, public InputDevice { · 2 unmodified lines
3132 #endif
3233
3334 public:
34− enum JoystickAxisChannel {
35− LEFT_AXIS_X_POS = 0,
36− LEFT_AXIS_X_NEG,
37− LEFT_AXIS_Y_POS,
38− LEFT_AXIS_Y_NEG,
39− RIGHT_AXIS_X_POS,
40− RIGHT_AXIS_X_NEG,
41− RIGHT_AXIS_Y_POS,
42− RIGHT_AXIS_Y_NEG,
43− RIGHT_SHOULDER,
44− LEFT_SHOULDER,
45− };
35+ using Pointer = std::shared_ptr<Joystick>;
4636
4737 const QString& getName() const { return _name; }
4838
4939 // Device functions
50− virtual void registerToUserInputMapper(UserInputMapper& mapper) override;
51− virtual void assignDefaultInputMapping(UserInputMapper& mapper) override;
40+ virtual controller::Input::NamedVector getAvailableInputs() const override;
41+ virtual QString getDefaultMappingConfig() const override;
5242 virtual void update(float deltaTime, bool jointsCaptured) override;
5343 virtual void focusOutEvent() override;
5444
55− Joystick() : InputDevice("Joystick") {}
45+ Joystick() : InputDevice("GamePad") {}
5646 ~Joystick();
5747
5848 #ifdef HAVE_SDL2
59− UserInputMapper::Input makeInput(SDL_GameControllerButton button);
60−#endif
61− UserInputMapper::Input makeInput(Joystick::JoystickAxisChannel axis);
62−
63−#ifdef HAVE_SDL2
64− Joystick(SDL_JoystickID instanceId, const QString& name, SDL_GameController* sdlGameController);
49+ Joystick(SDL_JoystickID instanceId, SDL_GameController* sdlGameController);
6550 #endif
6651
6752 void closeJoystick();
libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp+130−135
@@ -10,6 +10,13 @@ · 9 unmodified lines
1010 //
1111 #include "KeyboardMouseDevice.h"
1212
13+#include <QtGui/QKeyEvent>
14+#include <QtGui/QMouseEvent>
15+#include <QtGui/QTouchEvent>
16+
17+#include <controllers/UserInputMapper.h>
18+#include <PathUtils.h>
19+
1320 const QString KeyboardMouseDevice::NAME = "Keyboard/Mouse";
1421
1522 void KeyboardMouseDevice::update(float deltaTime, bool jointsCaptured) {
@@ -81,7 +88,7 @@ void KeyboardMouseDevice::wheelEvent(QWheelEvent* event) { · 65 unmodified lines
8188 _axisStateMap[makeInput(MOUSE_AXIS_WHEEL_Y_NEG).getChannel()] = (currentMove.y() < 0 ? -currentMove.y() : 0.0f);
8289 }
8390
84−glm::vec2 KeyboardMouseDevice::evalAverageTouchPoints(const QList<QTouchEvent::TouchPoint>& points) const {
91+glm::vec2 evalAverageTouchPoints(const QList<QTouchEvent::TouchPoint>& points) {
8592 glm::vec2 averagePoint(0.0f);
8693 if (points.count() > 0) {
8794 for (auto& point : points) {
@@ -123,163 +130,151 @@ void KeyboardMouseDevice::touchUpdateEvent(const QTouchEvent* event) { · 35 unmodified lines
123130 _lastTouch = currentPos;
124131 }
125132
126−UserInputMapper::Input KeyboardMouseDevice::makeInput(Qt::Key code) {
127− auto shortCode = (UserInputMapper::uint16)(code & KEYBOARD_MASK);
133+controller::Input KeyboardMouseDevice::makeInput(Qt::Key code) const {
134+ auto shortCode = (uint16_t)(code & KEYBOARD_MASK);
128135 if (shortCode != code) {
129136 shortCode |= 0x0800; // add this bit instead of the way Qt::Key add a bit on the 3rd byte for some keys
130137 }
131− return UserInputMapper::Input(_deviceID, shortCode, UserInputMapper::ChannelType::BUTTON);
138+ return controller::Input(_deviceID, shortCode, controller::ChannelType::BUTTON);
132139 }
133140
134−UserInputMapper::Input KeyboardMouseDevice::makeInput(Qt::MouseButton code) {
141+controller::Input KeyboardMouseDevice::makeInput(Qt::MouseButton code) const {
135142 switch (code) {
136143 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);
138145 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);
140147 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);
142149 default:
143− return UserInputMapper::Input();
150+ return controller::Input();
144151 };
145152 }
146153
147−UserInputMapper::Input KeyboardMouseDevice::makeInput(KeyboardMouseDevice::MouseAxisChannel axis) {
148− return UserInputMapper::Input(_deviceID, axis, UserInputMapper::ChannelType::AXIS);
154+controller::Input KeyboardMouseDevice::makeInput(KeyboardMouseDevice::MouseAxisChannel axis) const {
155+ return controller::Input(_deviceID, axis, controller::ChannelType::AXIS);
149156 }
150157
151−UserInputMapper::Input KeyboardMouseDevice::makeInput(KeyboardMouseDevice::TouchAxisChannel axis) {
152− return UserInputMapper::Input(_deviceID, axis, UserInputMapper::ChannelType::AXIS);
158+controller::Input KeyboardMouseDevice::makeInput(KeyboardMouseDevice::TouchAxisChannel axis) const {
159+ return controller::Input(_deviceID, axis, controller::ChannelType::AXIS);
153160 }
154161
155−UserInputMapper::Input KeyboardMouseDevice::makeInput(KeyboardMouseDevice::TouchButtonChannel button) {
156− return UserInputMapper::Input(_deviceID, button, UserInputMapper::ChannelType::BUTTON);
162+controller::Input KeyboardMouseDevice::makeInput(KeyboardMouseDevice::TouchButtonChannel button) const {
163+ return controller::Input(_deviceID, button, controller::ChannelType::BUTTON);
157164 }
158165
159−void KeyboardMouseDevice::registerToUserInputMapper(UserInputMapper& mapper) {
160− // Grab the current free device ID
161− _deviceID = mapper.getFreeDeviceID();
162−
163− auto proxy = std::make_shared<UserInputMapper::DeviceProxy>(_name);
164− proxy->getButton = [this] (const UserInputMapper::Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); };
165− proxy->getAxis = [this] (const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); };
166− proxy->getAvailabeInputs = [this] () -> QVector<UserInputMapper::InputPair> {
167− QVector<UserInputMapper::InputPair> availableInputs;
168− for (int i = (int) Qt::Key_0; i <= (int) Qt::Key_9; i++) {
169− availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key(i)), QKeySequence(Qt::Key(i)).toString()));
166+controller::Input::NamedVector KeyboardMouseDevice::getAvailableInputs() const {
167+ using namespace controller;
168+ static QVector<Input::NamedPair> availableInputs;
169+ static std::once_flag once;
170+ std::call_once(once, [&] {
171+ for (int i = (int)Qt::Key_0; i <= (int)Qt::Key_9; i++) {
172+ availableInputs.append(Input::NamedPair(makeInput(Qt::Key(i)), QKeySequence(Qt::Key(i)).toString()));
170173 }
171− for (int i = (int) Qt::Key_A; i <= (int) Qt::Key_Z; i++) {
172− availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key(i)), QKeySequence(Qt::Key(i)).toString()));
174+ for (int i = (int)Qt::Key_A; i <= (int)Qt::Key_Z; i++) {
175+ availableInputs.append(Input::NamedPair(makeInput(Qt::Key(i)), QKeySequence(Qt::Key(i)).toString()));
173176 }
174− for (int i = (int) Qt::Key_Left; i <= (int) Qt::Key_Down; i++) {
175− availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key(i)), QKeySequence(Qt::Key(i)).toString()));
177+ for (int i = (int)Qt::Key_Left; i <= (int)Qt::Key_Down; i++) {
178+ availableInputs.append(Input::NamedPair(makeInput(Qt::Key(i)), QKeySequence(Qt::Key(i)).toString()));
176179 }
177− availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key_Space), QKeySequence(Qt::Key_Space).toString()));
178− availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key_Shift), "Shift"));
179− availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key_PageUp), QKeySequence(Qt::Key_PageUp).toString()));
180− availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key_PageDown), QKeySequence(Qt::Key_PageDown).toString()));
181−
182− availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::LeftButton), "Left Mouse Click"));
183− availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::MiddleButton), "Middle Mouse Click"));
184− availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::RightButton), "Right Mouse Click"));
185−
186− availableInputs.append(UserInputMapper::InputPair(makeInput(MOUSE_AXIS_X_POS), "Mouse Move Right"));
187− availableInputs.append(UserInputMapper::InputPair(makeInput(MOUSE_AXIS_X_NEG), "Mouse Move Left"));
188− availableInputs.append(UserInputMapper::InputPair(makeInput(MOUSE_AXIS_Y_POS), "Mouse Move Up"));
189− availableInputs.append(UserInputMapper::InputPair(makeInput(MOUSE_AXIS_Y_NEG), "Mouse Move Down"));
190−
191− availableInputs.append(UserInputMapper::InputPair(makeInput(MOUSE_AXIS_WHEEL_Y_POS), "Mouse Wheel Right"));
192− availableInputs.append(UserInputMapper::InputPair(makeInput(MOUSE_AXIS_WHEEL_Y_NEG), "Mouse Wheel Left"));
193− availableInputs.append(UserInputMapper::InputPair(makeInput(MOUSE_AXIS_WHEEL_X_POS), "Mouse Wheel Up"));
194− availableInputs.append(UserInputMapper::InputPair(makeInput(MOUSE_AXIS_WHEEL_X_NEG), "Mouse Wheel Down"));
195−
196− availableInputs.append(UserInputMapper::InputPair(makeInput(TOUCH_AXIS_X_POS), "Touchpad Right"));
197− availableInputs.append(UserInputMapper::InputPair(makeInput(TOUCH_AXIS_X_NEG), "Touchpad Left"));
198− availableInputs.append(UserInputMapper::InputPair(makeInput(TOUCH_AXIS_Y_POS), "Touchpad Up"));
199− availableInputs.append(UserInputMapper::InputPair(makeInput(TOUCH_AXIS_Y_NEG), "Touchpad Down"));
200−
201− return availableInputs;
202− };
203− proxy->resetDeviceBindings = [this, &mapper] () -> bool {
204− mapper.removeAllInputChannelsForDevice(_deviceID);
205− this->assignDefaultInputMapping(mapper);
206− return true;
207− };
208− mapper.registerDevice(_deviceID, proxy);
180+ availableInputs.append(Input::NamedPair(makeInput(Qt::Key_Space), QKeySequence(Qt::Key_Space).toString()));
181+ availableInputs.append(Input::NamedPair(makeInput(Qt::Key_Shift), "Shift"));
182+ availableInputs.append(Input::NamedPair(makeInput(Qt::Key_PageUp), QKeySequence(Qt::Key_PageUp).toString()));
183+ availableInputs.append(Input::NamedPair(makeInput(Qt::Key_PageDown), QKeySequence(Qt::Key_PageDown).toString()));
184+
185+ availableInputs.append(Input::NamedPair(makeInput(Qt::LeftButton), "LeftMouseClick"));
186+ availableInputs.append(Input::NamedPair(makeInput(Qt::MiddleButton), "MiddleMouseClick"));
187+ availableInputs.append(Input::NamedPair(makeInput(Qt::RightButton), "RightMouseClick"));
188+
189+ availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_X_POS), "MouseMoveRight"));
190+ availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_X_NEG), "MouseMoveLeft"));
191+ availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_Y_POS), "MouseMoveUp"));
192+ availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_Y_NEG), "MouseMoveDown"));
193+
194+ availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_WHEEL_Y_POS), "MouseWheelRight"));
195+ availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_WHEEL_Y_NEG), "MouseWheelLeft"));
196+ availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_WHEEL_X_POS), "MouseWheelUp"));
197+ availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_WHEEL_X_NEG), "MouseWheelDown"));
198+
199+ availableInputs.append(Input::NamedPair(makeInput(TOUCH_AXIS_X_POS), "TouchpadRight"));
200+ availableInputs.append(Input::NamedPair(makeInput(TOUCH_AXIS_X_NEG), "TouchpadLeft"));
201+ availableInputs.append(Input::NamedPair(makeInput(TOUCH_AXIS_Y_POS), "TouchpadUp"));
202+ availableInputs.append(Input::NamedPair(makeInput(TOUCH_AXIS_Y_NEG), "TouchpadDown"));
203+ });
204+ return availableInputs;
209205 }
210206
211−void KeyboardMouseDevice::assignDefaultInputMapping(UserInputMapper& mapper) {
212− const float BUTTON_MOVE_SPEED = 1.0f;
213− const float BUTTON_YAW_SPEED = 0.75f;
214− const float BUTTON_PITCH_SPEED = 0.5f;
215− const float MOUSE_YAW_SPEED = 0.5f;
216− const float MOUSE_PITCH_SPEED = 0.25f;
217− const float TOUCH_YAW_SPEED = 0.5f;
218− const float TOUCH_PITCH_SPEED = 0.25f;
219− const float BUTTON_BOOM_SPEED = 0.1f;
220−
221− // AWSD keys mapping
222− mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(Qt::Key_S), BUTTON_MOVE_SPEED);
223− mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(Qt::Key_W), BUTTON_MOVE_SPEED);
224− mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(Qt::Key_A), BUTTON_MOVE_SPEED);
225− mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(Qt::Key_D), BUTTON_MOVE_SPEED);
226− mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(Qt::Key_C), BUTTON_MOVE_SPEED);
227− mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(Qt::Key_E), BUTTON_MOVE_SPEED);
228−
229− mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(Qt::Key_E), makeInput(Qt::Key_Shift), BUTTON_BOOM_SPEED);
230− mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(Qt::Key_C), makeInput(Qt::Key_Shift), BUTTON_BOOM_SPEED);
231− mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(Qt::Key_A), makeInput(Qt::RightButton), BUTTON_YAW_SPEED);
232− mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(Qt::Key_D), makeInput(Qt::RightButton), BUTTON_YAW_SPEED);
233− mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(Qt::Key_A), makeInput(Qt::Key_Shift), BUTTON_YAW_SPEED);
234− mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(Qt::Key_D), makeInput(Qt::Key_Shift), BUTTON_YAW_SPEED);
235− mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(Qt::Key_S), makeInput(Qt::Key_Shift), BUTTON_PITCH_SPEED);
236− mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(Qt::Key_W), makeInput(Qt::Key_Shift), BUTTON_PITCH_SPEED);
237−
238− // Arrow keys mapping
239− mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(Qt::Key_Down), BUTTON_MOVE_SPEED);
240− mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(Qt::Key_Up), BUTTON_MOVE_SPEED);
241− mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(Qt::Key_Left), BUTTON_MOVE_SPEED);
242− mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(Qt::Key_Right), BUTTON_MOVE_SPEED);
243− mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(Qt::Key_PageDown), BUTTON_MOVE_SPEED);
244− mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(Qt::Key_PageUp), BUTTON_MOVE_SPEED);
245−
246− mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(Qt::Key_Left), makeInput(Qt::RightButton), BUTTON_YAW_SPEED);
247− mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(Qt::Key_Right), makeInput(Qt::RightButton), BUTTON_YAW_SPEED);
248− mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(Qt::Key_Left), makeInput(Qt::Key_Shift), BUTTON_YAW_SPEED);
249− mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(Qt::Key_Right), makeInput(Qt::Key_Shift), BUTTON_YAW_SPEED);
250− mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(Qt::Key_Down), makeInput(Qt::Key_Shift), BUTTON_PITCH_SPEED);
251− mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(Qt::Key_Up), makeInput(Qt::Key_Shift), BUTTON_PITCH_SPEED);
252−
253− // Mouse move
254− mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(MOUSE_AXIS_Y_NEG), makeInput(Qt::RightButton), MOUSE_PITCH_SPEED);
255− mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(MOUSE_AXIS_Y_POS), makeInput(Qt::RightButton), MOUSE_PITCH_SPEED);
256− mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(MOUSE_AXIS_X_NEG), makeInput(Qt::RightButton), MOUSE_YAW_SPEED);
257− mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(MOUSE_AXIS_X_POS), makeInput(Qt::RightButton), MOUSE_YAW_SPEED);
258−
259−
260−#ifdef Q_OS_MAC
261− // wheel event modifier on Mac collide with the touchpad scroll event
262− mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(TOUCH_AXIS_Y_NEG), TOUCH_PITCH_SPEED);
263− mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(TOUCH_AXIS_Y_POS), TOUCH_PITCH_SPEED);
264− mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(TOUCH_AXIS_X_NEG), TOUCH_YAW_SPEED);
265− mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(TOUCH_AXIS_X_POS), TOUCH_YAW_SPEED);
266−#else
267− // Touch pad yaw pitch
268− mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(TOUCH_AXIS_Y_NEG), TOUCH_PITCH_SPEED);
269− mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(TOUCH_AXIS_Y_POS), TOUCH_PITCH_SPEED);
270− mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(TOUCH_AXIS_X_NEG), TOUCH_YAW_SPEED);
271− mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(TOUCH_AXIS_X_POS), TOUCH_YAW_SPEED);
272−
273− // Wheel move
274− mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(MOUSE_AXIS_WHEEL_Y_POS), BUTTON_BOOM_SPEED);
275− mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(MOUSE_AXIS_WHEEL_Y_NEG), BUTTON_BOOM_SPEED);
276− mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(MOUSE_AXIS_WHEEL_X_NEG), BUTTON_YAW_SPEED);
277− mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(MOUSE_AXIS_WHEEL_X_POS), BUTTON_YAW_SPEED);
278−
279−#endif
280−
281− mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(Qt::Key_Space));
282− mapper.addInputChannel(UserInputMapper::ACTION1, makeInput(Qt::Key_R));
283− mapper.addInputChannel(UserInputMapper::ACTION2, makeInput(Qt::Key_T));
207+QString KeyboardMouseDevice::getDefaultMappingConfig() const {
208+ static const QString MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/keyboardMouse.json";
209+ return MAPPING_JSON;
284210 }
285211
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
1212 #ifndef hifi_KeyboardMouseDevice_h
1313 #define hifi_KeyboardMouseDevice_h
1414
15−#include <QTouchEvent>
1615 #include <chrono>
17−#include "InputDevice.h"
16+
17+#include <QtCore/QPoint>
18+
19+#include <controllers/InputDevice.h>
1820 #include "InputPlugin.h"
1921
20−class KeyboardMouseDevice : public InputPlugin, public InputDevice {
22+class QTouchEvent;
23+class QKeyEvent;
24+class QMouseEvent;
25+class QWheelEvent;
26+
27+class KeyboardMouseDevice : public InputPlugin, public controller::InputDevice {
2128 Q_OBJECT
2229 public:
2330 enum KeyboardChannel {
@@ -61,15 +68,12 @@ public: · 37 unmodified lines
6168 virtual bool isJointController() const override { return false; }
6269 const QString& getName() const override { return NAME; }
6370
64− virtual void activate() override {};
65− virtual void deactivate() override {};
66−
6771 virtual void pluginFocusOutEvent() override { focusOutEvent(); }
6872 virtual void pluginUpdate(float deltaTime, bool jointsCaptured) override { update(deltaTime, jointsCaptured); }
6973
7074 // Device functions
71− virtual void registerToUserInputMapper(UserInputMapper& mapper) override;
72− virtual void assignDefaultInputMapping(UserInputMapper& mapper) override;
75+ virtual controller::Input::NamedVector getAvailableInputs() const override;
76+ virtual QString getDefaultMappingConfig() const override;
7377 virtual void update(float deltaTime, bool jointsCaptured) override;
7478 virtual void focusOutEvent() override;
7579
@@ -87,11 +91,11 @@ public: · 11 unmodified lines
8791 void wheelEvent(QWheelEvent* event);
8892
8993 // Let's make it easy for Qt because we assume we love Qt forever
90− UserInputMapper::Input makeInput(Qt::Key code);
91− UserInputMapper::Input makeInput(Qt::MouseButton code);
92− UserInputMapper::Input makeInput(KeyboardMouseDevice::MouseAxisChannel axis);
93− UserInputMapper::Input makeInput(KeyboardMouseDevice::TouchAxisChannel axis);
94− UserInputMapper::Input makeInput(KeyboardMouseDevice::TouchButtonChannel button);
94+ controller::Input makeInput(Qt::Key code) const;
95+ controller::Input makeInput(Qt::MouseButton code) const;
96+ controller::Input makeInput(MouseAxisChannel axis) const;
97+ controller::Input makeInput(TouchAxisChannel axis) const;
98+ controller::Input makeInput(TouchButtonChannel button) const;
9599
96100 static const QString NAME;
97101
@@ -100,7 +104,6 @@ protected: · 2 unmodified lines
100104 glm::vec2 _lastTouch;
101105 bool _isTouching = false;
102106
103− glm::vec2 evalAverageTouchPoints(const QList<QTouchEvent::TouchPoint>& points) const;
104107 std::chrono::high_resolution_clock _clock;
105108 std::chrono::high_resolution_clock::time_point _lastTouchTime;
106109 };
libraries/input-plugins/src/input-plugins/SDL2Manager.cpp+41−18
@@ -14,6 +14,7 @@ · 13 unmodified lines
1414 #include <HFActionEvent.h>
1515 #include <HFBackEvent.h>
1616 #include <PerfStat.h>
17+#include <controllers/UserInputMapper.h>
1718
1819 #include "SDL2Manager.h"
1920
@@ -47,12 +48,12 @@ void SDL2Manager::init() { · 27 unmodified lines
4748 if (controller) {
4849 SDL_JoystickID id = getInstanceId(controller);
4950 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);
5153 _openJoysticks[id] = joystick;
52− auto userInputMapper = DependencyManager::get<UserInputMapper>();
53− joystick->registerToUserInputMapper(*userInputMapper);
54− joystick->assignDefaultInputMapping(*userInputMapper);
55− emit joystickAdded(joystick);
54+ auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
55+ userInputMapper->registerDevice(joystick);
56+ emit joystickAdded(joystick.get());
5657 }
5758 }
5859 }
@@ -67,12 +68,33 @@ void SDL2Manager::init() { · 8 unmodified lines
6768
6869 void SDL2Manager::deinit() {
6970 #ifdef HAVE_SDL2
70− qDeleteAll(_openJoysticks);
71+ _openJoysticks.clear();
7172
7273 SDL_Quit();
7374 #endif
7475 }
7576
77+void SDL2Manager::activate() {
78+#ifdef HAVE_SDL2
79+ auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
80+ for (auto joystick : _openJoysticks) {
81+ userInputMapper->registerDevice(joystick);
82+ emit joystickAdded(joystick.get());
83+ }
84+#endif
85+}
86+
87+void SDL2Manager::deactivate() {
88+#ifdef HAVE_SDL2
89+ auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
90+ for (auto joystick : _openJoysticks) {
91+ userInputMapper->removeDevice(joystick->getDeviceID());
92+ emit joystickRemoved(joystick.get());
93+ }
94+#endif
95+}
96+
97+
7698 bool SDL2Manager::isSupported() const {
7799 #ifdef HAVE_SDL2
78100 return true;
@@ -92,7 +114,7 @@ void SDL2Manager::pluginFocusOutEvent() { · 13 unmodified lines
92114 void SDL2Manager::pluginUpdate(float deltaTime, bool jointsCaptured) {
93115 #ifdef HAVE_SDL2
94116 if (_isInitialized) {
95− auto userInputMapper = DependencyManager::get<UserInputMapper>();
117+ auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
96118 for (auto joystick : _openJoysticks) {
97119 joystick->update(deltaTime, jointsCaptured);
98120 }
@@ -102,12 +124,12 @@ void SDL2Manager::pluginUpdate(float deltaTime, bool jointsCaptured) { · 3 unmodified lines
102124 SDL_Event event;
103125 while (SDL_PollEvent(&event)) {
104126 if (event.type == SDL_CONTROLLERAXISMOTION) {
105− Joystick* joystick = _openJoysticks[event.caxis.which];
127+ Joystick::Pointer joystick = _openJoysticks[event.caxis.which];
106128 if (joystick) {
107129 joystick->handleAxisEvent(event.caxis);
108130 }
109131 } 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];
111133 if (joystick) {
112134 joystick->handleButtonEvent(event.cbutton);
113135 }
@@ -124,20 +146,21 @@ void SDL2Manager::pluginUpdate(float deltaTime, bool jointsCaptured) { · 10 unmodified lines
124146
125147 } else if (event.type == SDL_CONTROLLERDEVICEADDED) {
126148 SDL_GameController* controller = SDL_GameControllerOpen(event.cdevice.which);
127−
128149 SDL_JoystickID id = getInstanceId(controller);
129150 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);
131153 _openJoysticks[id] = joystick;
132− joystick->registerToUserInputMapper(*userInputMapper);
133− joystick->assignDefaultInputMapping(*userInputMapper);
134− emit joystickAdded(joystick);
154+ userInputMapper->registerDevice(joystick);
155+ emit joystickAdded(joystick.get());
135156 }
136157 } else if (event.type == SDL_CONTROLLERDEVICEREMOVED) {
137− Joystick* joystick = _openJoysticks[event.cdevice.which];
138− _openJoysticks.remove(event.cdevice.which);
139− userInputMapper->removeDevice(joystick->getDeviceID());
140− emit joystickRemoved(joystick);
158+ if (_openJoysticks.contains(event.cdevice.which)) {
159+ Joystick::Pointer joystick = _openJoysticks[event.cdevice.which];
160+ _openJoysticks.remove(event.cdevice.which);
161+ userInputMapper->removeDevice(joystick->getDeviceID());
162+ emit joystickRemoved(joystick.get());
163+ }
141164 }
142165 }
143166 }
libraries/input-plugins/src/input-plugins/SDL2Manager.h+9−6
@@ -16,9 +16,9 @@ · 15 unmodified lines
1616 #include <SDL.h>
1717 #endif
1818
19−#include "InputPlugin.h"
20−#include "UserInputMapper.h"
19+#include <controllers/UserInputMapper.h>
2120
21+#include "InputPlugin.h"
2222 #include "Joystick.h"
2323
2424 class SDL2Manager : public InputPlugin {
@@ -34,9 +34,12 @@ public: · 9 unmodified lines
3434
3535 virtual void init() override;
3636 virtual void deinit() override;
37− virtual void activate() override {};
38− virtual void deactivate() override {};
39−
37+
38+ /// Called when a plugin is being activated for use. May be called multiple times.
39+ virtual void activate() override;
40+ /// Called when a plugin is no longer being used. May be called multiple times.
41+ virtual void deactivate() override;
42+
4043 virtual void pluginFocusOutEvent() override;
4144 virtual void pluginUpdate(float deltaTime, bool jointsCaptured) override;
4245
@@ -78,7 +81,7 @@ private: · 35 unmodified lines
7881 int buttonPressed() const { return SDL_PRESSED; }
7982 int buttonRelease() const { return SDL_RELEASED; }
8083
81− QMap<SDL_JoystickID, Joystick*> _openJoysticks;
84+ QMap<SDL_JoystickID, Joystick::Pointer> _openJoysticks;
8285 #endif
8386 bool _isInitialized;
8487 static const QString NAME;
libraries/input-plugins/src/input-plugins/SixenseManager.cpp+182−155
@@ -18,10 +18,13 @@ · 17 unmodified lines
1818 #include <PerfStat.h>
1919 #include <SettingHandle.h>
2020 #include <plugins/PluginContainer.h>
21+#include <PathUtils.h>
22+#include <NumericalConstants.h>
23+#include <UserActivityLogger.h>
24+#include <controllers/UserInputMapper.h>
2125
22−#include "NumericalConstants.h"
2326 #include "SixenseManager.h"
24−#include "UserActivityLogger.h"
27+
2528
2629 #ifdef HAVE_SIXENSE
2730 #include "sixense.h"
@@ -32,10 +35,6 @@ · 4 unmodified lines
3235 Q_DECLARE_LOGGING_CATEGORY(inputplugins)
3336 Q_LOGGING_CATEGORY(inputplugins, "hifi.inputplugins")
3437
35−// These bits aren't used for buttons, so they can be used as masks:
36−const unsigned int LEFT_MASK = 0;
37−const unsigned int RIGHT_MASK = 1U << 1;
38−
3938 #ifdef HAVE_SIXENSE
4039
4140 const int CALIBRATION_STATE_IDLE = 0;
@@ -65,15 +64,12 @@ const QString MENU_PATH = MENU_PARENT + ">" + MENU_NAME; · 23 unmodified lines
6564 const QString TOGGLE_SMOOTH = "Smooth Sixense Movement";
6665 const float DEFAULT_REACH_LENGTH = 1.5f;
6766
68−
67+static std::shared_ptr<SixenseManager> instance;
6968 SixenseManager::SixenseManager() :
7069 InputDevice("Hydra"),
71− _reachLength(DEFAULT_REACH_LENGTH),
72−#ifdef __APPLE__
73− _sixenseLibrary(nullptr),
74−#endif
75− _hydrasConnected(false)
70+ _reachLength(DEFAULT_REACH_LENGTH)
7671 {
72+ instance = std::shared_ptr<SixenseManager>(this);
7773 }
7874
7975 bool SixenseManager::isSupported() const {
@@ -85,6 +81,7 @@ bool SixenseManager::isSupported() const { · 5 unmodified lines
8581 }
8682
8783 void SixenseManager::activate() {
84+ InputPlugin::activate();
8885 #ifdef HAVE_SIXENSE
8986 _calibrationState = CALIBRATION_STATE_IDLE;
9087 _avatarPosition = DEFAULT_AVATAR_POSITION;
@@ -94,6 +91,9 @@ void SixenseManager::activate() { · 3 unmodified lines
9491 [this] (bool clicked) { this->setSixenseFilter(clicked); },
9592 true, true);
9693
94+ auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
95+ userInputMapper->registerDevice(instance);
96+
9797 #ifdef __APPLE__
9898
9999 if (!_sixenseLibrary) {
@@ -125,11 +125,19 @@ void SixenseManager::activate() { · 25 unmodified lines
125125 }
126126
127127 void SixenseManager::deactivate() {
128+ InputPlugin::deactivate();
129+
128130 #ifdef HAVE_SIXENSE
129131 CONTAINER->removeMenuItem(MENU_NAME, TOGGLE_SMOOTH);
130132 CONTAINER->removeMenu(MENU_PATH);
131133
132134 _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+ }
133141
134142 #ifdef __APPLE__
135143 SixenseBaseFunction sixenseExit = (SixenseBaseFunction)_sixenseLibrary->resolve("sixenseExit");
@@ -155,6 +163,12 @@ void SixenseManager::setSixenseFilter(bool filter) { · 19 unmodified lines
155163 }
156164
157165 void SixenseManager::update(float deltaTime, bool jointsCaptured) {
166+ // FIXME - Some of the code in update() will crash if you haven't actually activated the
167+ // plugin. But we want register with the UserInputMapper if we don't call this.
168+ // We need to clean this up.
169+ //if (!_activated) {
170+ // return;
171+ //}
158172 #ifdef HAVE_SIXENSE
159173 _buttonPressedMap.clear();
160174
@@ -163,33 +177,34 @@ void SixenseManager::update(float deltaTime, bool jointsCaptured) { · 2 unmodified lines
163177 (SixenseBaseFunction) _sixenseLibrary->resolve("sixenseGetNumActiveControllers");
164178 #endif
165179
166− auto userInputMapper = DependencyManager::get<UserInputMapper>();
180+ auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
167181
182+ static const float MAX_DISCONNECTED_TIME = 2.0f;
183+ static bool disconnected { false };
184+ static float disconnectedInterval { 0.0f };
168185 if (sixenseGetNumActiveControllers() == 0) {
169− if (_hydrasConnected) {
170− qCDebug(inputplugins) << "hydra disconnected" << _badDataCount;
171− if (_badDataCount++ < _allowedBadDataCount) { // gotta get some no-active in a row before we shut things down
172− return;
173− }
186+ if (!disconnected) {
187+ disconnectedInterval += deltaTime;
174188 }
175− _hydrasConnected = false;
176− if (_deviceID != 0) {
177− userInputMapper->removeDevice(_deviceID);
178− _deviceID = 0;
189+ if (disconnectedInterval > MAX_DISCONNECTED_TIME) {
190+ disconnected = true;
191+ _axisStateMap.clear();
192+ _buttonPressedMap.clear();
179193 _poseStateMap.clear();
194+ _collectedSamples.clear();
180195 }
181196 return;
182197 }
183198
184− PerformanceTimer perfTimer("sixense");
185− if (!_hydrasConnected) {
186− _hydrasConnected = true;
187− _badDataCount = 0;
188− registerToUserInputMapper(*userInputMapper);
189− assignDefaultInputMapping(*userInputMapper);
190− UserActivityLogger::getInstance().connectedDevice("spatial_controller", "hydra");
199+ if (disconnected) {
200+ disconnected = 0;
201+ disconnectedInterval = 0.0f;
191202 }
192203
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+
193208 #ifdef __APPLE__
194209 SixenseBaseFunction sixenseGetMaxControllers =
195210 (SixenseBaseFunction) _sixenseLibrary->resolve("sixenseGetMaxControllers");
@@ -220,28 +235,31 @@ void SixenseManager::update(float deltaTime, bool jointsCaptured) { · 24 unmodified lines
220235 // NOTE: Sixense API returns pos data in millimeters but we IMMEDIATELY convert to meters.
221236 glm::vec3 position(data->pos[0], data->pos[1], data->pos[2]);
222237 position *= METERS_PER_MILLIMETER;
223−
238+ bool left = i == 0;
239+ using namespace controller;
224240 // Check to see if this hand/controller is on the base
225241 const float CONTROLLER_AT_BASE_DISTANCE = 0.075f;
226242 if (glm::length(position) >= CONTROLLER_AT_BASE_DISTANCE) {
227− handleButtonEvent(data->buttons, numActiveControllers - 1);
228− handleAxisEvent(data->joystick_x, data->joystick_y, data->trigger, numActiveControllers - 1);
243+ handleButtonEvent(data->buttons, left);
244+ _axisStateMap[left ? LX : RX] = data->joystick_x;
245+ _axisStateMap[left ? LY : RY] = data->joystick_y;
246+ _axisStateMap[left ? LT : RT] = data->trigger;
229247
230248 if (!jointsCaptured) {
231249 // Rotation of Palm
232250 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+
234253 } else {
235254 _poseStateMap.clear();
255+ _collectedSamples.clear();
236256 }
237257 } 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();
239262 }
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);
245263 }
246264
247265 if (numActiveControllers == 2) {
@@ -361,40 +379,38 @@ void SixenseManager::focusOutEvent() { · 113 unmodified lines
361379 _buttonPressedMap.clear();
362380 };
363381
364−void SixenseManager::handleAxisEvent(float stickX, float stickY, float trigger, int index) {
365− _axisStateMap[makeInput(AXIS_Y_POS, index).getChannel()] = (stickY > 0.0f) ? stickY : 0.0f;
366− _axisStateMap[makeInput(AXIS_Y_NEG, index).getChannel()] = (stickY < 0.0f) ? -stickY : 0.0f;
367− _axisStateMap[makeInput(AXIS_X_POS, index).getChannel()] = (stickX > 0.0f) ? stickX : 0.0f;
368− _axisStateMap[makeInput(AXIS_X_NEG, index).getChannel()] = (stickX < 0.0f) ? -stickX : 0.0f;
369− _axisStateMap[makeInput(BACK_TRIGGER, index).getChannel()] = trigger;
382+void SixenseManager::handleAxisEvent(float stickX, float stickY, float trigger, bool left) {
370383 }
371384
372−void SixenseManager::handleButtonEvent(unsigned int buttons, int index) {
385+void SixenseManager::handleButtonEvent(unsigned int buttons, bool left) {
386+ using namespace controller;
373387 if (buttons & BUTTON_0) {
374− _buttonPressedMap.insert(makeInput(BUTTON_0, index).getChannel());
388+ _buttonPressedMap.insert(left ? BACK : START);
375389 }
376390 if (buttons & BUTTON_1) {
377− _buttonPressedMap.insert(makeInput(BUTTON_1, index).getChannel());
391+ _buttonPressedMap.insert(left ? DL : X);
378392 }
379393 if (buttons & BUTTON_2) {
380− _buttonPressedMap.insert(makeInput(BUTTON_2, index).getChannel());
394+ _buttonPressedMap.insert(left ? DD : A);
381395 }
382396 if (buttons & BUTTON_3) {
383− _buttonPressedMap.insert(makeInput(BUTTON_3, index).getChannel());
397+ _buttonPressedMap.insert(left ? DR : B);
384398 }
385399 if (buttons & BUTTON_4) {
386− _buttonPressedMap.insert(makeInput(BUTTON_4, index).getChannel());
400+ _buttonPressedMap.insert(left ? DU : Y);
387401 }
388402 if (buttons & BUTTON_FWD) {
389− _buttonPressedMap.insert(makeInput(BUTTON_FWD, index).getChannel());
403+ _buttonPressedMap.insert(left ? LB : RB);
390404 }
391405 if (buttons & BUTTON_TRIGGER) {
392− _buttonPressedMap.insert(makeInput(BUTTON_TRIGGER, index).getChannel());
406+ _buttonPressedMap.insert(left ? LS : RS);
393407 }
394408 }
395409
396−void SixenseManager::handlePoseEvent(glm::vec3 position, glm::quat rotation, int index) {
410+void SixenseManager::handlePoseEvent(float deltaTime, glm::vec3 position, glm::quat rotation, bool left) {
397411 #ifdef HAVE_SIXENSE
412+ auto hand = left ? controller::StandardPoseChannel::LEFT_HAND : controller::StandardPoseChannel::RIGHT_HAND;
413+
398414 // From ABOVE the sixense coordinate frame looks like this:
399415 //
400416 // |
@@ -409,6 +425,7 @@ void SixenseManager::handlePoseEvent(glm::vec3 position, glm::quat rotation, int · 8 unmodified lines
409425 // |
410426 // |
411427 // z
428+ auto prevPose = _poseStateMap[hand];
412429
413430 // Transform the measured position into body frame.
414431 position = _avatarRotation * (position + _avatarPosition);
@@ -437,7 +454,7 @@ void SixenseManager::handlePoseEvent(glm::vec3 position, glm::quat rotation, int · 22 unmodified lines
437454 // In addition to Qsh each hand has pre-offset introduced by the shape of the sixense controllers
438455 // and how they fit into the hand in their relaxed state. This offset is a quarter turn about
439456 // 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;
441458 const glm::quat preOffset = glm::angleAxis(sign * PI / 2.0f, Vectors::UNIT_Z);
442459
443460 // 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
452469 // TODO: find a shortcut with fewer rotations.
453470 rotation = _avatarRotation * postOffset * glm::inverse(sixenseToHand) * rotation * preOffset * sixenseToHand;
454471
455− _poseStateMap[makeInput(JointChannel(index)).getChannel()] = UserInputMapper::PoseValue(position, rotation);
456−#endif // HAVE_SIXENSE
457−}
458−
459−void SixenseManager::registerToUserInputMapper(UserInputMapper& mapper) {
460− // Grab the current free device ID
461− _deviceID = mapper.getFreeDeviceID();
462−
463− auto proxy = std::make_shared<UserInputMapper::DeviceProxy>(_name);
464− proxy->getButton = [this] (const UserInputMapper::Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); };
465− proxy->getAxis = [this] (const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); };
466− proxy->getPose = [this](const UserInputMapper::Input& input, int timestamp) -> UserInputMapper::PoseValue { return this->getPose(input.getChannel()); };
467− proxy->getAvailabeInputs = [this] () -> QVector<UserInputMapper::InputPair> {
468− QVector<UserInputMapper::InputPair> availableInputs;
469− availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_0, 0), "Left Start"));
470− availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_1, 0), "Left Button 1"));
471− availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_2, 0), "Left Button 2"));
472− availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_3, 0), "Left Button 3"));
473− availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_4, 0), "Left Button 4"));
474−
475− availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_FWD, 0), "L1"));
476− availableInputs.append(UserInputMapper::InputPair(makeInput(BACK_TRIGGER, 0), "L2"));
477−
478− availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_Y_POS, 0), "Left Stick Up"));
479− availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_Y_NEG, 0), "Left Stick Down"));
480− availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_X_POS, 0), "Left Stick Right"));
481− availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_X_NEG, 0), "Left Stick Left"));
482− availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_TRIGGER, 0), "Left Trigger Press"));
483−
484− availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_0, 1), "Right Start"));
485− availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_1, 1), "Right Button 1"));
486− availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_2, 1), "Right Button 2"));
487− availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_3, 1), "Right Button 3"));
488− availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_4, 1), "Right Button 4"));
489−
490− availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_FWD, 1), "R1"));
491− availableInputs.append(UserInputMapper::InputPair(makeInput(BACK_TRIGGER, 1), "R2"));
492−
493− availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_Y_POS, 1), "Right Stick Up"));
494− availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_Y_NEG, 1), "Right Stick Down"));
495− availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_X_POS, 1), "Right Stick Right"));
496− availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_X_NEG, 1), "Right Stick Left"));
497− availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_TRIGGER, 1), "Right Trigger Press"));
498−
499− return availableInputs;
500− };
501− proxy->resetDeviceBindings = [this, &mapper] () -> bool {
502− mapper.removeAllInputChannelsForDevice(_deviceID);
503− this->assignDefaultInputMapping(mapper);
504− return true;
505− };
506− mapper.registerDevice(_deviceID, proxy);
507−}
508−
509−void SixenseManager::assignDefaultInputMapping(UserInputMapper& mapper) {
510− const float JOYSTICK_MOVE_SPEED = 1.0f;
511− const float JOYSTICK_YAW_SPEED = 0.5f;
512− const float JOYSTICK_PITCH_SPEED = 0.25f;
513− const float BUTTON_MOVE_SPEED = 1.0f;
514− const float BOOM_SPEED = 0.1f;
472+ glm::vec3 velocity(0.0f);
473+ glm::quat angularVelocity;
515474
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);
521475
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);
527476
528− // Buttons
529− mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(BUTTON_3, 0), BOOM_SPEED);
530− mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(BUTTON_1, 0), BOOM_SPEED);
477+ if (prevPose.isValid() && deltaTime > std::numeric_limits<float>::epsilon()) {
531478
532− mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(BUTTON_3, 1), BUTTON_MOVE_SPEED);
533− mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(BUTTON_1, 1), BUTTON_MOVE_SPEED);
479+ velocity = (position - prevPose.getTranslation()) / deltaTime;
534480
535− mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(BUTTON_2, 0));
536− mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(BUTTON_2, 1));
481+ auto deltaRot = rotation * glm::conjugate(prevPose.getRotation());
482+ auto axis = glm::axis(deltaRot);
483+ auto angle = glm::angle(deltaRot);
484+ angularVelocity = glm::angleAxis(angle / deltaTime, axis);
537485
538− mapper.addInputChannel(UserInputMapper::ACTION1, makeInput(BUTTON_4, 0));
539− mapper.addInputChannel(UserInputMapper::ACTION2, makeInput(BUTTON_4, 1));
486+ // Average
487+ auto& samples = _collectedSamples[hand];
488+ samples.first.addSample(velocity);
489+ velocity = samples.first.average;
490+
491+ // FIXME: // Not using quaternion average yet for angular velocity because it s probably wrong but keep the MovingAverage in place
492+ //samples.second.addSample(glm::vec4(angularVelocity.x, angularVelocity.y, angularVelocity.z, angularVelocity.w));
493+ //angularVelocity = glm::quat(samples.second.average.w, samples.second.average.x, samples.second.average.y, samples.second.average.z);
494+ } else if (!prevPose.isValid()) {
495+ _collectedSamples[hand].first.clear();
496+ _collectedSamples[hand].second.clear();
497+ }
540498
541− mapper.addInputChannel(UserInputMapper::LEFT_HAND, makeInput(LEFT_HAND));
542− mapper.addInputChannel(UserInputMapper::RIGHT_HAND, makeInput(RIGHT_HAND));
499+ _poseStateMap[hand] = controller::Pose(position, rotation, velocity, angularVelocity);
500+#endif // HAVE_SIXENSE
501+}
543502
544− mapper.addInputChannel(UserInputMapper::LEFT_HAND_CLICK, makeInput(BACK_TRIGGER, 0));
545− mapper.addInputChannel(UserInputMapper::RIGHT_HAND_CLICK, makeInput(BACK_TRIGGER, 1));
503+static const auto L0 = controller::BACK;
504+static const auto L1 = controller::DL;
505+static const auto L2 = controller::DD;
506+static const auto L3 = controller::DR;
507+static const auto L4 = controller::DU;
508+static const auto R0 = controller::START;
509+static const auto R1 = controller::X;
510+static const auto R2 = controller::A;
511+static const auto R3 = controller::B;
512+static const auto R4 = controller::Y;
513+
514+using namespace controller;
515+
516+controller::Input::NamedVector SixenseManager::getAvailableInputs() const {
517+ using namespace controller;
518+ static const Input::NamedVector availableInputs {
519+ makePair(L0, "L0"),
520+ makePair(L1, "L1"),
521+ makePair(L2, "L2"),
522+ makePair(L3, "L3"),
523+ makePair(L4, "L4"),
524+ makePair(LB, "LB"),
525+ makePair(LS, "LS"),
526+ makePair(LX, "LX"),
527+ makePair(LY, "LY"),
528+ makePair(LT, "LT"),
529+ makePair(R0, "R0"),
530+ makePair(R1, "R1"),
531+ makePair(R2, "R2"),
532+ makePair(R3, "R3"),
533+ makePair(R4, "R4"),
534+ makePair(RB, "RB"),
535+ makePair(RS, "RS"),
536+ makePair(RX, "RX"),
537+ makePair(RY, "RY"),
538+ makePair(RT, "RT"),
539+ makePair(LEFT_HAND, "LeftHand"),
540+ makePair(RIGHT_HAND, "RightHand"),
541+ };
542+ return availableInputs;
543+};
546544
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));
550545
546+QString SixenseManager::getDefaultMappingConfig() const {
547+ static const QString MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/hydra.json";
548+ return MAPPING_JSON;
551549 }
552550
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+
553594 // virtual
554595 void SixenseManager::saveSettings() const {
555596 Settings settings;
@@ -559,7 +600,6 @@ void SixenseManager::saveSettings() const { · 3 unmodified lines
559600 settings.setVec3Value(QString("avatarPosition"), _avatarPosition);
560601 settings.setQuatValue(QString("avatarRotation"), _avatarRotation);
561602 settings.setValue(QString("reachLength"), QVariant(_reachLength));
562− settings.setValue(QString("allowedHydraFailures"), 120);
563603 }
564604 settings.endGroup();
565605 }
@@ -572,19 +612,6 @@ void SixenseManager::loadSettings() { · 6 unmodified lines
572612 settings.getVec3ValueIfValid(QString("avatarPosition"), _avatarPosition);
573613 settings.getQuatValueIfValid(QString("avatarRotation"), _avatarRotation);
574614 settings.getFloatValueIfValid(QString("reachLength"), _reachLength);
575− _allowedBadDataCount = settings.value(QString("allowedHydraFailures"), 120).toInt();
576615 }
577616 settings.endGroup();
578617 }
579−
580−UserInputMapper::Input SixenseManager::makeInput(unsigned int button, int index) {
581− return UserInputMapper::Input(_deviceID, button | (index == 0 ? LEFT_MASK : RIGHT_MASK), UserInputMapper::ChannelType::BUTTON);
582−}
583−
584−UserInputMapper::Input SixenseManager::makeInput(SixenseManager::JoystickAxisChannel axis, int index) {
585− return UserInputMapper::Input(_deviceID, axis | (index == 0 ? LEFT_MASK : RIGHT_MASK), UserInputMapper::ChannelType::AXIS);
586−}
587−
588−UserInputMapper::Input SixenseManager::makeInput(JointChannel joint) {
589− return UserInputMapper::Input(_deviceID, joint, UserInputMapper::ChannelType::POSE);
590−}
libraries/input-plugins/src/input-plugins/SixenseManager.h+20−29
@@ -24,8 +24,12 @@ · 23 unmodified lines
2424
2525 #endif
2626
27+#include <SimpleMovingAverage.h>
28+
29+#include <controllers/InputDevice.h>
30+#include <controllers/StandardControls.h>
31+
2732 #include "InputPlugin.h"
28−#include "InputDevice.h"
2933
3034 class QLibrary;
3135
@@ -40,22 +44,9 @@ const unsigned int BUTTON_TRIGGER = 1U << 8; · 8 unmodified lines
4044 const bool DEFAULT_INVERT_SIXENSE_MOUSE_BUTTONS = false;
4145
4246 // Handles interaction with the Sixense SDK (e.g., Razer Hydra).
43−class SixenseManager : public InputPlugin, public InputDevice {
47+class SixenseManager : public InputPlugin, public controller::InputDevice {
4448 Q_OBJECT
4549 public:
46− enum JoystickAxisChannel {
47− AXIS_Y_POS = 1U << 0,
48− AXIS_Y_NEG = 1U << 3,
49− AXIS_X_POS = 1U << 4,
50− AXIS_X_NEG = 1U << 5,
51− BACK_TRIGGER = 1U << 6,
52− };
53−
54− enum JointChannel {
55− LEFT_HAND = 0,
56− RIGHT_HAND,
57− };
58−
5950 SixenseManager();
6051
6152 // Plugin functions
@@ -71,15 +62,12 @@ public: · 9 unmodified lines
7162 virtual void pluginUpdate(float deltaTime, bool jointsCaptured) override { update(deltaTime, jointsCaptured); }
7263
7364 // Device functions
74− virtual void registerToUserInputMapper(UserInputMapper& mapper) override;
75− virtual void assignDefaultInputMapping(UserInputMapper& mapper) override;
65+ virtual controller::Input::NamedVector getAvailableInputs() const override;
66+ virtual QString getDefaultMappingConfig() const override;
67+
7668 virtual void update(float deltaTime, bool jointsCaptured) override;
7769 virtual void focusOutEvent() override;
7870
79− UserInputMapper::Input makeInput(unsigned int button, int index);
80− UserInputMapper::Input makeInput(JoystickAxisChannel axis, int index);
81− UserInputMapper::Input makeInput(JointChannel joint);
82−
8371 virtual void saveSettings() const override;
8472 virtual void loadSettings() override;
8573
@@ -87,9 +75,9 @@ public slots: · 1 unmodified line
8775 void setSixenseFilter(bool filter);
8876
8977 private:
90− void handleButtonEvent(unsigned int buttons, int index);
91− void handleAxisEvent(float x, float y, float trigger, int index);
92− void handlePoseEvent(glm::vec3 position, glm::quat rotation, int index);
78+ void handleButtonEvent(unsigned int buttons, bool left);
79+ void handleAxisEvent(float x, float y, float trigger, bool left);
80+ void handlePoseEvent(float deltaTime, glm::vec3 position, glm::quat rotation, bool left);
9381
9482 void updateCalibration(void* controllers);
9583
@@ -108,17 +96,20 @@ private: · 12 unmodified lines
10896 glm::vec3 _reachRight;
10997 float _lastDistance;
11098 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;
111105
112106 #ifdef __APPLE__
113− QLibrary* _sixenseLibrary;
107+ QLibrary* _sixenseLibrary { nullptr };
114108 #endif
115109
116− bool _hydrasConnected;
117− int _badDataCount;
118− int _allowedBadDataCount;
119−
120110 static const QString NAME;
121111 static const QString HYDRA_ID_STRING;
122112 };
123113
124114 #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−
18−const float CONTROLLER_THRESHOLD = 0.3f;
19−
20−StandardController::~StandardController() {
21−}
22−
23−void StandardController::update(float deltaTime, bool jointsCaptured) {
24− for (auto axisState : _axisStateMap) {
25− if (fabsf(axisState.second) < CONTROLLER_THRESHOLD) {
26− _axisStateMap[axisState.first] = 0.0f;
27− }
28− }
29−}
30−
31−void StandardController::focusOutEvent() {
32− _axisStateMap.clear();
33− _buttonPressedMap.clear();
34−};
35−
36−void StandardController::registerToUserInputMapper(UserInputMapper& mapper) {
37− // Grab the current free device ID
38− _deviceID = mapper.getStandardDeviceID();
39−
40− auto proxy = std::make_shared<UserInputMapper::DeviceProxy>(_name);
41− proxy->getButton = [this] (const UserInputMapper::Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); };
42− proxy->getAxis = [this] (const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); };
43− proxy->getAvailabeInputs = [this] () -> QVector<UserInputMapper::InputPair> {
44− QVector<UserInputMapper::InputPair> availableInputs;
45− availableInputs.append(UserInputMapper::InputPair(makeInput(STANDARD_CONTROLLER_BUTTON_A), "Bottom Button"));
46− availableInputs.append(UserInputMapper::InputPair(makeInput(STANDARD_CONTROLLER_BUTTON_B), "Right Button"));
47− availableInputs.append(UserInputMapper::InputPair(makeInput(STANDARD_CONTROLLER_BUTTON_X), "Left Button"));
48− availableInputs.append(UserInputMapper::InputPair(makeInput(STANDARD_CONTROLLER_BUTTON_Y), "Top Button"));
49−
50− availableInputs.append(UserInputMapper::InputPair(makeInput(STANDARD_CONTROLLER_BUTTON_DPAD_UP), "DPad Up"));
51− availableInputs.append(UserInputMapper::InputPair(makeInput(STANDARD_CONTROLLER_BUTTON_DPAD_DOWN), "DPad Down"));
52− availableInputs.append(UserInputMapper::InputPair(makeInput(STANDARD_CONTROLLER_BUTTON_DPAD_LEFT), "DPad Left"));
53− availableInputs.append(UserInputMapper::InputPair(makeInput(STANDARD_CONTROLLER_BUTTON_DPAD_RIGHT), "DPad Right"));
54−
55− availableInputs.append(UserInputMapper::InputPair(makeInput(STANDARD_CONTROLLER_BUTTON_LEFTSHOULDER), "L1"));
56− availableInputs.append(UserInputMapper::InputPair(makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), "R1"));
57− availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_SHOULDER), "L2"));
58− availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_SHOULDER), "R2"));
59−
60− availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_AXIS_Y_NEG), "Left Stick Up"));
61− availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_AXIS_Y_POS), "Left Stick Down"));
62− availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_AXIS_X_POS), "Left Stick Right"));
63− availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_AXIS_X_NEG), "Left Stick Left"));
64− availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_AXIS_Y_NEG), "Right Stick Up"));
65− availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_AXIS_Y_POS), "Right Stick Down"));
66− availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_AXIS_X_POS), "Right Stick Right"));
67− availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_AXIS_X_NEG), "Right Stick Left"));
68−
69− availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_HAND), "Left Hand"));
70− availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_HAND), "Right Hand"));
71−
72− return availableInputs;
73− };
74− proxy->resetDeviceBindings = [this, &mapper] () -> bool {
75− mapper.removeAllInputChannelsForDevice(_deviceID);
76− this->assignDefaultInputMapping(mapper);
77− return true;
78− };
79− mapper.registerStandardDevice(proxy);
80−}
81−
82−void StandardController::assignDefaultInputMapping(UserInputMapper& mapper) {
83− const float JOYSTICK_MOVE_SPEED = 1.0f;
84− const float DPAD_MOVE_SPEED = 0.5f;
85− const float JOYSTICK_YAW_SPEED = 0.5f;
86− const float JOYSTICK_PITCH_SPEED = 0.25f;
87− const float BOOM_SPEED = 0.1f;
88−
89− // Y axes are flipped (up is negative)
90− // Left StandardController: Movement, strafing
91− mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(LEFT_AXIS_Y_NEG), JOYSTICK_MOVE_SPEED);
92− mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(LEFT_AXIS_Y_POS), JOYSTICK_MOVE_SPEED);
93− mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(LEFT_AXIS_X_POS), JOYSTICK_MOVE_SPEED);
94− mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(LEFT_AXIS_X_NEG), JOYSTICK_MOVE_SPEED);
95−
96− // Right StandardController: Camera orientation
97− mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(RIGHT_AXIS_X_POS), JOYSTICK_YAW_SPEED);
98− mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(RIGHT_AXIS_X_NEG), JOYSTICK_YAW_SPEED);
99− mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(RIGHT_AXIS_Y_NEG), JOYSTICK_PITCH_SPEED);
100− mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(RIGHT_AXIS_Y_POS), JOYSTICK_PITCH_SPEED);
101−
102− // Dpad movement
103− mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(STANDARD_CONTROLLER_BUTTON_DPAD_UP), DPAD_MOVE_SPEED);
104− mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(STANDARD_CONTROLLER_BUTTON_DPAD_DOWN), DPAD_MOVE_SPEED);
105− mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(STANDARD_CONTROLLER_BUTTON_DPAD_RIGHT), DPAD_MOVE_SPEED);
106− mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(STANDARD_CONTROLLER_BUTTON_DPAD_LEFT), DPAD_MOVE_SPEED);
107−
108− // Button controls
109− mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(STANDARD_CONTROLLER_BUTTON_Y), DPAD_MOVE_SPEED);
110− mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(STANDARD_CONTROLLER_BUTTON_X), DPAD_MOVE_SPEED);
111−
112− // Zoom
113− mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(RIGHT_SHOULDER), BOOM_SPEED);
114− mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(LEFT_SHOULDER), BOOM_SPEED);
115−
116−
117− // Hold front right shoulder button for precision controls
118− // Left StandardController: Movement, strafing
119− mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(LEFT_AXIS_Y_NEG), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_MOVE_SPEED/2.0f);
120− mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(LEFT_AXIS_Y_POS), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_MOVE_SPEED/2.0f);
121− mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(LEFT_AXIS_X_POS), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_MOVE_SPEED/2.0f);
122− mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(LEFT_AXIS_X_NEG), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_MOVE_SPEED/2.0f);
123−
124− // Right StandardController: Camera orientation
125− mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(RIGHT_AXIS_X_POS), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_YAW_SPEED/2.0f);
126− mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(RIGHT_AXIS_X_NEG), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_YAW_SPEED/2.0f);
127− mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(RIGHT_AXIS_Y_NEG), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_PITCH_SPEED/2.0f);
128− mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(RIGHT_AXIS_Y_POS), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_PITCH_SPEED/2.0f);
129−
130− // Dpad movement
131− mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(STANDARD_CONTROLLER_BUTTON_DPAD_UP), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f);
132− mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(STANDARD_CONTROLLER_BUTTON_DPAD_DOWN), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f);
133− mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(STANDARD_CONTROLLER_BUTTON_DPAD_RIGHT), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f);
134− mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(STANDARD_CONTROLLER_BUTTON_DPAD_LEFT), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f);
135−
136− // Button controls
137− mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(STANDARD_CONTROLLER_BUTTON_Y), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f);
138− mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(STANDARD_CONTROLLER_BUTTON_X), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f);
139−
140− // Zoom
141− mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(RIGHT_SHOULDER), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), BOOM_SPEED/2.0f);
142− mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(LEFT_SHOULDER), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), BOOM_SPEED/2.0f);
143−
144− mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(STANDARD_CONTROLLER_BUTTON_LEFTSHOULDER));
145−
146− mapper.addInputChannel(UserInputMapper::ACTION1, makeInput(STANDARD_CONTROLLER_BUTTON_B));
147− mapper.addInputChannel(UserInputMapper::ACTION2, makeInput(STANDARD_CONTROLLER_BUTTON_A));
148−}
149−
150−UserInputMapper::Input StandardController::makeInput(StandardController::StandardControllerButtonChannel button) {
151− return UserInputMapper::Input(_deviceID, button, UserInputMapper::ChannelType::BUTTON);
152−}
153−
154−UserInputMapper::Input StandardController::makeInput(StandardController::StandardControllerAxisChannel axis) {
155− return UserInputMapper::Input(_deviceID, axis, UserInputMapper::ChannelType::AXIS);
156−}
157−
158−UserInputMapper::Input StandardController::makeInput(StandardController::StandardControllerPoseChannel pose) {
159− return UserInputMapper::Input(_deviceID, pose, UserInputMapper::ChannelType::POSE);
160−}
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−
20−typedef std::shared_ptr<StandardController> StandardControllerPointer;
21−
22−class StandardController : public QObject, public InputDevice {
23− Q_OBJECT
24− Q_PROPERTY(QString name READ getName)
25−
26−public:
27− enum StandardControllerAxisChannel {
28− LEFT_AXIS_X_POS = 0,
29− LEFT_AXIS_X_NEG,
30− LEFT_AXIS_Y_POS,
31− LEFT_AXIS_Y_NEG,
32− RIGHT_AXIS_X_POS,
33− RIGHT_AXIS_X_NEG,
34− RIGHT_AXIS_Y_POS,
35− RIGHT_AXIS_Y_NEG,
36− RIGHT_SHOULDER,
37− LEFT_SHOULDER,
38− };
39− enum StandardControllerButtonChannel {
40− STANDARD_CONTROLLER_BUTTON_A = 0,
41− STANDARD_CONTROLLER_BUTTON_B,
42− STANDARD_CONTROLLER_BUTTON_X,
43− STANDARD_CONTROLLER_BUTTON_Y,
44−
45− STANDARD_CONTROLLER_BUTTON_DPAD_UP,
46− STANDARD_CONTROLLER_BUTTON_DPAD_DOWN,
47− STANDARD_CONTROLLER_BUTTON_DPAD_LEFT,
48− STANDARD_CONTROLLER_BUTTON_DPAD_RIGHT,
49−
50− STANDARD_CONTROLLER_BUTTON_LEFTSHOULDER,
51− STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER,
52− };
53−
54− enum StandardControllerPoseChannel {
55− LEFT_HAND = 0,
56− RIGHT_HAND,
57− };
58−
59− const QString& getName() const { return _name; }
60−
61− // Device functions
62− virtual void registerToUserInputMapper(UserInputMapper& mapper) override;
63− virtual void assignDefaultInputMapping(UserInputMapper& mapper) override;
64− virtual void update(float deltaTime, bool jointsCaptured) override;
65− virtual void focusOutEvent() override;
66−
67− StandardController() : InputDevice("Standard") {}
68− ~StandardController();
69−
70− UserInputMapper::Input makeInput(StandardController::StandardControllerButtonChannel button);
71− UserInputMapper::Input makeInput(StandardController::StandardControllerAxisChannel axis);
72− UserInputMapper::Input makeInput(StandardController::StandardControllerPoseChannel pose);
73−
74−private:
75−};
76−
77−#endif // hifi_StandardController_h
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
16−UserInputMapper::UserInputMapper() {
17− registerStandardDevice();
18− assignDefaulActionScales();
19− createActionNames();
20−}
21−
22−UserInputMapper::~UserInputMapper() {
23−}
24−
25−
26−bool UserInputMapper::registerDevice(uint16 deviceID, const DeviceProxy::Pointer& proxy){
27− proxy->_name += " (" + QString::number(deviceID) + ")";
28− _registeredDevices[deviceID] = proxy;
29− return true;
30−}
31−
32−UserInputMapper::DeviceProxy::Pointer UserInputMapper::getDeviceProxy(const Input& input) {
33− auto device = _registeredDevices.find(input.getDevice());
34− if (device != _registeredDevices.end()) {
35− return (device->second);
36− } else {
37− return DeviceProxy::Pointer();
38− }
39−}
40−
41−QString UserInputMapper::getDeviceName(uint16 deviceID) {
42− if (_registeredDevices.find(deviceID) != _registeredDevices.end()) {
43− return _registeredDevices[deviceID]->_name;
44− }
45− return QString("unknown");
46−}
47−
48−
49−void UserInputMapper::resetAllDeviceBindings() {
50− for (auto device : _registeredDevices) {
51− device.second->resetDeviceBindings();
52− }
53−}
54−
55−void UserInputMapper::resetDevice(uint16 deviceID) {
56− auto device = _registeredDevices.find(deviceID);
57− if (device != _registeredDevices.end()) {
58− device->second->resetDeviceBindings();
59− }
60−}
61−
62−int UserInputMapper::findDevice(QString name) {
63− for (auto device : _registeredDevices) {
64− if (device.second->_name.split(" (")[0] == name) {
65− return device.first;
66− }
67− }
68− return 0;
69−}
70−
71−QVector<QString> UserInputMapper::getDeviceNames() {
72− QVector<QString> result;
73− for (auto device : _registeredDevices) {
74− QString deviceName = device.second->_name.split(" (")[0];
75− result << deviceName;
76− }
77− return result;
78−}
79−
80−
81−bool UserInputMapper::addInputChannel(Action action, const Input& input, float scale) {
82− return addInputChannel(action, input, Input(), scale);
83−}
84−
85−bool UserInputMapper::addInputChannel(Action action, const Input& input, const Input& modifier, float scale) {
86− // Check that the device is registered
87− if (!getDeviceProxy(input)) {
88− qDebug() << "UserInputMapper::addInputChannel: The input comes from a device #" << input.getDevice() << "is unknown. no inputChannel mapped.";
89− return false;
90− }
91−
92− auto inputChannel = InputChannel(input, modifier, action, scale);
93−
94− // Insert or replace the input to modifiers
95− if (inputChannel.hasModifier()) {
96− auto& modifiers = _inputToModifiersMap[input.getID()];
97− modifiers.push_back(inputChannel._modifier);
98− std::sort(modifiers.begin(), modifiers.end());
99− }
100−
101− // Now update the action To Inputs side of things
102− _actionToInputsMap.insert(ActionToInputsMap::value_type(action, inputChannel));
103−
104− return true;
105−}
106−
107−int UserInputMapper::addInputChannels(const InputChannels& channels) {
108− int nbAdded = 0;
109− for (auto& channel : channels) {
110− nbAdded += addInputChannel(channel._action, channel._input, channel._modifier, channel._scale);
111− }
112− return nbAdded;
113−}
114−
115−bool UserInputMapper::removeInputChannel(InputChannel inputChannel) {
116− // Remove from Input to Modifiers map
117− if (inputChannel.hasModifier()) {
118− _inputToModifiersMap.erase(inputChannel._input.getID());
119− }
120−
121− // Remove from Action to Inputs map
122− std::pair<ActionToInputsMap::iterator, ActionToInputsMap::iterator> ret;
123− ret = _actionToInputsMap.equal_range(inputChannel._action);
124− for (ActionToInputsMap::iterator it=ret.first; it!=ret.second; ++it) {
125− if (it->second == inputChannel) {
126− _actionToInputsMap.erase(it);
127− return true;
128− }
129− }
130−
131− return false;
132−}
133−
134−void UserInputMapper::removeAllInputChannels() {
135− _inputToModifiersMap.clear();
136− _actionToInputsMap.clear();
137−}
138−
139−void UserInputMapper::removeAllInputChannelsForDevice(uint16 device) {
140− QVector<InputChannel> channels = getAllInputsForDevice(device);
141− for (auto& channel : channels) {
142− removeInputChannel(channel);
143− }
144−}
145−
146−void UserInputMapper::removeDevice(int device) {
147− removeAllInputChannelsForDevice((uint16) device);
148− _registeredDevices.erase(device);
149−}
150−
151−int UserInputMapper::getInputChannels(InputChannels& channels) const {
152− for (auto& channel : _actionToInputsMap) {
153− channels.push_back(channel.second);
154− }
155−
156− return _actionToInputsMap.size();
157−}
158−
159−QVector<UserInputMapper::InputChannel> UserInputMapper::getAllInputsForDevice(uint16 device) {
160− InputChannels allChannels;
161− getInputChannels(allChannels);
162−
163− QVector<InputChannel> channels;
164− for (InputChannel inputChannel : allChannels) {
165− if (inputChannel._input._device == device) {
166− channels.push_back(inputChannel);
167− }
168− }
169−
170− return channels;
171−}
172−
173−void UserInputMapper::update(float deltaTime) {
174−
175− // Reset the axis state for next loop
176− for (auto& channel : _actionStates) {
177− channel = 0.0f;
178− }
179−
180− for (auto& channel : _poseStates) {
181− channel = PoseValue();
182− }
183−
184− int currentTimestamp = 0;
185−
186− for (auto& channelInput : _actionToInputsMap) {
187− auto& inputMapping = channelInput.second;
188− auto& inputID = inputMapping._input;
189− bool enabled = true;
190−
191− // Check if this input channel has modifiers and collect the possibilities
192− auto modifiersIt = _inputToModifiersMap.find(inputID.getID());
193− if (modifiersIt != _inputToModifiersMap.end()) {
194− Modifiers validModifiers;
195− bool isActiveModifier = false;
196− for (auto& modifier : modifiersIt->second) {
197− auto deviceProxy = getDeviceProxy(modifier);
198− if (deviceProxy->getButton(modifier, currentTimestamp)) {
199− validModifiers.push_back(modifier);
200− isActiveModifier |= (modifier.getID() == inputMapping._modifier.getID());
201− }
202− }
203− enabled = (validModifiers.empty() && !inputMapping.hasModifier()) || isActiveModifier;
204− }
205−
206− // if enabled: default input or all modifiers on
207− if (enabled) {
208− auto deviceProxy = getDeviceProxy(inputID);
209− switch (inputMapping._input.getType()) {
210− case ChannelType::BUTTON: {
211− _actionStates[channelInput.first] += inputMapping._scale * float(deviceProxy->getButton(inputID, currentTimestamp));// * deltaTime; // weight the impulse by the deltaTime
212− break;
213− }
214− case ChannelType::AXIS: {
215− _actionStates[channelInput.first] += inputMapping._scale * deviceProxy->getAxis(inputID, currentTimestamp);
216− break;
217− }
218− case ChannelType::POSE: {
219− if (!_poseStates[channelInput.first].isValid()) {
220− _poseStates[channelInput.first] = deviceProxy->getPose(inputID, currentTimestamp);
221− }
222− break;
223− }
224− default: {
225− break; //silence please
226− }
227− }
228− } else{
229− // Channel input not enabled
230− enabled = false;
231− }
232− }
233−
234− // Scale all the channel step with the scale
235− static const float EPSILON = 0.01f;
236− for (auto i = 0; i < NUM_ACTIONS; i++) {
237− _actionStates[i] *= _actionScales[i];
238− // Emit only on change, and emit when moving back to 0
239− if (fabsf(_actionStates[i] - _lastActionStates[i]) > EPSILON) {
240− _lastActionStates[i] = _actionStates[i];
241− emit actionEvent(i, _actionStates[i]);
242− }
243− // TODO: emit signal for pose changes
244− }
245−}
246−
247−QVector<UserInputMapper::Action> UserInputMapper::getAllActions() const {
248− QVector<Action> actions;
249− for (auto i = 0; i < NUM_ACTIONS; i++) {
250− actions.append(Action(i));
251− }
252− return actions;
253−}
254−
255−QVector<UserInputMapper::InputChannel> UserInputMapper::getInputChannelsForAction(UserInputMapper::Action action) {
256− QVector<InputChannel> inputChannels;
257− std::pair <ActionToInputsMap::iterator, ActionToInputsMap::iterator> ret;
258− ret = _actionToInputsMap.equal_range(action);
259− for (ActionToInputsMap::iterator it=ret.first; it!=ret.second; ++it) {
260− inputChannels.append(it->second);
261− }
262− return inputChannels;
263−}
264−
265−int UserInputMapper::findAction(const QString& actionName) const {
266− auto actions = getAllActions();
267− for (auto action : actions) {
268− if (getActionName(action) == actionName) {
269− return action;
270− }
271− }
272− // If the action isn't found, return -1
273− return -1;
274−}
275−
276−QVector<QString> UserInputMapper::getActionNames() const {
277− QVector<QString> result;
278− for (auto i = 0; i < NUM_ACTIONS; i++) {
279− result << _actionNames[i];
280− }
281− return result;
282−}
283−
284−void UserInputMapper::assignDefaulActionScales() {
285− _actionScales[LONGITUDINAL_BACKWARD] = 1.0f; // 1m per unit
286− _actionScales[LONGITUDINAL_FORWARD] = 1.0f; // 1m per unit
287− _actionScales[LATERAL_LEFT] = 1.0f; // 1m per unit
288− _actionScales[LATERAL_RIGHT] = 1.0f; // 1m per unit
289− _actionScales[VERTICAL_DOWN] = 1.0f; // 1m per unit
290− _actionScales[VERTICAL_UP] = 1.0f; // 1m per unit
291− _actionScales[YAW_LEFT] = 1.0f; // 1 degree per unit
292− _actionScales[YAW_RIGHT] = 1.0f; // 1 degree per unit
293− _actionScales[PITCH_DOWN] = 1.0f; // 1 degree per unit
294− _actionScales[PITCH_UP] = 1.0f; // 1 degree per unit
295− _actionScales[BOOM_IN] = 0.5f; // .5m per unit
296− _actionScales[BOOM_OUT] = 0.5f; // .5m per unit
297− _actionScales[LEFT_HAND] = 1.0f; // default
298− _actionScales[RIGHT_HAND] = 1.0f; // default
299− _actionScales[LEFT_HAND_CLICK] = 1.0f; // on
300− _actionScales[RIGHT_HAND_CLICK] = 1.0f; // on
301− _actionStates[SHIFT] = 1.0f; // on
302− _actionStates[ACTION1] = 1.0f; // default
303− _actionStates[ACTION2] = 1.0f; // default
304−}
305−
306−// This is only necessary as long as the actions are hardcoded
307−// Eventually you can just add the string when you add the action
308−void UserInputMapper::createActionNames() {
309− _actionNames[LONGITUDINAL_BACKWARD] = "LONGITUDINAL_BACKWARD";
310− _actionNames[LONGITUDINAL_FORWARD] = "LONGITUDINAL_FORWARD";
311− _actionNames[LATERAL_LEFT] = "LATERAL_LEFT";
312− _actionNames[LATERAL_RIGHT] = "LATERAL_RIGHT";
313− _actionNames[VERTICAL_DOWN] = "VERTICAL_DOWN";
314− _actionNames[VERTICAL_UP] = "VERTICAL_UP";
315− _actionNames[YAW_LEFT] = "YAW_LEFT";
316− _actionNames[YAW_RIGHT] = "YAW_RIGHT";
317− _actionNames[PITCH_DOWN] = "PITCH_DOWN";
318− _actionNames[PITCH_UP] = "PITCH_UP";
319− _actionNames[BOOM_IN] = "BOOM_IN";
320− _actionNames[BOOM_OUT] = "BOOM_OUT";
321− _actionNames[LEFT_HAND] = "LEFT_HAND";
322− _actionNames[RIGHT_HAND] = "RIGHT_HAND";
323− _actionNames[LEFT_HAND_CLICK] = "LEFT_HAND_CLICK";
324− _actionNames[RIGHT_HAND_CLICK] = "RIGHT_HAND_CLICK";
325− _actionNames[SHIFT] = "SHIFT";
326− _actionNames[ACTION1] = "ACTION1";
327− _actionNames[ACTION2] = "ACTION2";
328− _actionNames[CONTEXT_MENU] = "CONTEXT_MENU";
329− _actionNames[TOGGLE_MUTE] = "TOGGLE_MUTE";
330−}
331−
332−void UserInputMapper::registerStandardDevice() {
333− _standardController = std::make_shared<StandardController>();
334− _standardController->registerToUserInputMapper(*this);
335−}
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−
23−class StandardController;
24−typedef std::shared_ptr<StandardController> StandardControllerPointer;
25−
26−class UserInputMapper : public QObject, public Dependency {
27− Q_OBJECT
28− SINGLETON_DEPENDENCY
29− Q_ENUMS(Action)
30−public:
31− ~UserInputMapper();
32−
33− typedef unsigned short uint16;
34− typedef unsigned int uint32;
35−
36− enum class ChannelType {
37− UNKNOWN = 0,
38− BUTTON = 1,
39− AXIS,
40− POSE,
41− };
42−
43− // Input is the unique identifier to find a n input channel of a particular device
44− // Devices are responsible for registering to the UseInputMapper so their input channels can be sued and mapped
45− // to the Action channels
46− class Input {
47− public:
48− union {
49− struct {
50− uint16 _device; // Up to 64K possible devices
51− uint16 _channel : 14; // 2^14 possible channel per Device
52− uint16 _type : 2; // 2 bits to store the Type directly in the ID
53− };
54− uint32 _id = 0; // by default Input is 0 meaning invalid
55− };
56−
57− bool isValid() const { return (_id != 0); }
58−
59− uint16 getDevice() const { return _device; }
60− uint16 getChannel() const { return _channel; }
61− uint32 getID() const { return _id; }
62− ChannelType getType() const { return (ChannelType) _type; }
63−
64− void setDevice(uint16 device) { _device = device; }
65− void setChannel(uint16 channel) { _channel = channel; }
66− void setType(uint16 type) { _type = type; }
67− void setID(uint32 ID) { _id = ID; }
68−
69− bool isButton() const { return getType() == ChannelType::BUTTON; }
70− bool isAxis() const { return getType() == ChannelType::AXIS; }
71− bool isPose() const { return getType() == ChannelType::POSE; }
72−
73− // WORKAROUND: the explicit initializer here avoids a bug in GCC-4.8.2 (but not found in 4.9.2)
74− // where the default initializer (a C++-11ism) for the union data above is not applied.
75− explicit Input() : _id(0) {}
76− explicit Input(uint32 id) : _id(id) {}
77− explicit Input(uint16 device, uint16 channel, ChannelType type) : _device(device), _channel(channel), _type(uint16(type)) {}
78− Input(const Input& src) : _id(src._id) {}
79− Input& operator = (const Input& src) { _id = src._id; return (*this); }
80− bool operator ==(const Input& right) const { return _id == right._id; }
81− bool operator < (const Input& src) const { return _id < src._id; }
82− };
83−
84− // Modifiers are just button inputID
85− typedef std::vector< Input > Modifiers;
86−
87− class PoseValue {
88− public:
89− glm::vec3 _translation{ 0.0f };
90− glm::quat _rotation;
91− bool _valid;
92−
93− PoseValue() : _valid(false) {};
94− PoseValue(glm::vec3 translation, glm::quat rotation) : _translation(translation), _rotation(rotation), _valid(true) {}
95− PoseValue(const PoseValue&) = default;
96− PoseValue& operator = (const PoseValue&) = default;
97− bool operator ==(const PoseValue& right) const { return _translation == right.getTranslation() && _rotation == right.getRotation() && _valid == right.isValid(); }
98−
99− bool isValid() const { return _valid; }
100− glm::vec3 getTranslation() const { return _translation; }
101− glm::quat getRotation() const { return _rotation; }
102− };
103−
104− typedef std::function<bool (const Input& input, int timestamp)> ButtonGetter;
105− typedef std::function<float (const Input& input, int timestamp)> AxisGetter;
106− typedef std::function<PoseValue (const Input& input, int timestamp)> PoseGetter;
107− typedef QPair<Input, QString> InputPair;
108− typedef std::function<QVector<InputPair> ()> AvailableInputGetter;
109− typedef std::function<bool ()> ResetBindings;
110−
111− typedef QVector<InputPair> AvailableInput;
112−
113− class DeviceProxy {
114− public:
115− DeviceProxy(QString name) { _name = name; }
116− const QString& getName() const { return _name; }
117−
118− QString _name;
119− ButtonGetter getButton = [] (const Input& input, int timestamp) -> bool { return false; };
120− AxisGetter getAxis = [] (const Input& input, int timestamp) -> float { return 0.0f; };
121− PoseGetter getPose = [] (const Input& input, int timestamp) -> PoseValue { return PoseValue(); };
122− AvailableInputGetter getAvailabeInputs = [] () -> AvailableInput { return QVector<InputPair>(); };
123− ResetBindings resetDeviceBindings = [] () -> bool { return true; };
124−
125− typedef std::shared_ptr<DeviceProxy> Pointer;
126− };
127− // GetFreeDeviceID should be called before registering a device to use an ID not used by a different device.
128− uint16 getFreeDeviceID() { return _nextFreeDeviceID++; }
129− bool registerDevice(uint16 deviceID, const DeviceProxy::Pointer& device);
130− bool registerStandardDevice(const DeviceProxy::Pointer& device) { _standardDevice = device; return true; }
131− DeviceProxy::Pointer getDeviceProxy(const Input& input);
132− QString getDeviceName(uint16 deviceID);
133− QVector<InputPair> getAvailableInputs(uint16 deviceID) { return _registeredDevices[deviceID]->getAvailabeInputs(); }
134− void resetAllDeviceBindings();
135− void resetDevice(uint16 deviceID);
136− int findDevice(QString name);
137− QVector<QString> getDeviceNames();
138−
139− // Actions are the output channels of the Mapper, that's what the InputChannel map to
140− // For now the Actions are hardcoded, this is bad, but we will fix that in the near future
141− enum Action {
142− LONGITUDINAL_BACKWARD = 0,
143− LONGITUDINAL_FORWARD,
144−
145− LATERAL_LEFT,
146− LATERAL_RIGHT,
147−
148− VERTICAL_DOWN,
149− VERTICAL_UP,
150−
151− YAW_LEFT,
152− YAW_RIGHT,
153−
154− PITCH_DOWN,
155− PITCH_UP,
156−
157− BOOM_IN,
158− BOOM_OUT,
159−
160− LEFT_HAND,
161− RIGHT_HAND,
162−
163− LEFT_HAND_CLICK,
164− RIGHT_HAND_CLICK,
165−
166− SHIFT,
167−
168− ACTION1,
169− ACTION2,
170−
171− CONTEXT_MENU,
172− TOGGLE_MUTE,
173−
174− NUM_ACTIONS,
175− };
176−
177− std::vector<QString> _actionNames = std::vector<QString>(NUM_ACTIONS);
178− void createActionNames();
179−
180− QVector<Action> getAllActions() const;
181− QString getActionName(Action action) const { return UserInputMapper::_actionNames[(int) action]; }
182− float getActionState(Action action) const { return _actionStates[action]; }
183− PoseValue getPoseState(Action action) const { return _poseStates[action]; }
184− int findAction(const QString& actionName) const;
185− QVector<QString> getActionNames() const;
186− void assignDefaulActionScales();
187−
188− // Add input channel to the mapper and check that all the used channels are registered.
189− // Return true if theinput channel is created correctly, false either
190− bool addInputChannel(Action action, const Input& input, float scale = 1.0f);
191− bool addInputChannel(Action action, const Input& input, const Input& modifer, float scale = 1.0f);
192−
193− // Under the hood, the input channels are organized in map sorted on the _output
194− // The InputChannel class is just the full values describing the input channel in one object
195− class InputChannel {
196− public:
197− Input _input;
198− Input _modifier = Input(); // make it invalid by default, meaning no modifier
199− Action _action = LONGITUDINAL_BACKWARD;
200− float _scale = 0.0f;
201−
202− Input getInput() const { return _input; }
203− Input getModifier() const { return _modifier; }
204− Action getAction() const { return _action; }
205− float getScale() const { return _scale; }
206−
207− void setInput(Input input) { _input = input; }
208− void setModifier(Input modifier) { _modifier = modifier; }
209− void setAction(Action action) { _action = action; }
210− void setScale(float scale) { _scale = scale; }
211−
212− InputChannel() {}
213− InputChannel(const Input& input, const Input& modifier, Action action, float scale = 1.0f) :
214− _input(input), _modifier(modifier), _action(action), _scale(scale) {}
215− InputChannel(const InputChannel& src) : InputChannel(src._input, src._modifier, src._action, src._scale) {}
216− InputChannel& operator = (const InputChannel& src) { _input = src._input; _modifier = src._modifier; _action = src._action; _scale = src._scale; return (*this); }
217− bool operator ==(const InputChannel& right) const { return _input == right._input && _modifier == right._modifier && _action == right._action && _scale == right._scale; }
218− bool hasModifier() { return _modifier.isValid(); }
219− };
220− typedef std::vector< InputChannel > InputChannels;
221−
222− // Add a bunch of input channels, return the true number of channels that successfully were added
223− int addInputChannels(const InputChannels& channels);
224− // Remove the first found instance of the input channel from the input mapper, true if found
225− bool removeInputChannel(InputChannel channel);
226− void removeAllInputChannels();
227− void removeAllInputChannelsForDevice(uint16 device);
228− void removeDevice(int device);
229− //Grab all the input channels currently in use, return the number
230− int getInputChannels(InputChannels& channels) const;
231− QVector<InputChannel> getAllInputsForDevice(uint16 device);
232− QVector<InputChannel> getInputChannelsForAction(UserInputMapper::Action action);
233− std::multimap<Action, InputChannel> getActionToInputsMap() { return _actionToInputsMap; }
234−
235− // Update means go grab all the device input channels and update the output channel values
236− void update(float deltaTime);
237−
238− void setSensorToWorldMat(glm::mat4 sensorToWorldMat) { _sensorToWorldMat = sensorToWorldMat; }
239− glm::mat4 getSensorToWorldMat() { return _sensorToWorldMat; }
240−
241− UserInputMapper();
242−
243− typedef std::map<int, DeviceProxy::Pointer> DevicesMap;
244− DevicesMap getDevices() { return _registeredDevices; }
245−
246− uint16 getStandardDeviceID() const { return _standardDeviceID; }
247− DeviceProxy::Pointer getStandardDevice() { return _standardDevice; }
248−
249−signals:
250− void actionEvent(int action, float state);
251−
252−
253−protected:
254− void registerStandardDevice();
255− uint16 _standardDeviceID = 0;
256− DeviceProxy::Pointer _standardDevice;
257− StandardControllerPointer _standardController;
258−
259− DevicesMap _registeredDevices;
260− uint16 _nextFreeDeviceID = 1;
261−
262− typedef std::map<int, Modifiers> InputToMoModifiersMap;
263− InputToMoModifiersMap _inputToModifiersMap;
264−
265− typedef std::multimap<Action, InputChannel> ActionToInputsMap;
266− ActionToInputsMap _actionToInputsMap;
267−
268− std::vector<float> _actionStates = std::vector<float>(NUM_ACTIONS, 0.0f);
269− std::vector<float> _actionScales = std::vector<float>(NUM_ACTIONS, 1.0f);
270− std::vector<float> _lastActionStates = std::vector<float>(NUM_ACTIONS, 0.0f);
271− std::vector<PoseValue> _poseStates = std::vector<PoseValue>(NUM_ACTIONS);
272−
273− glm::mat4 _sensorToWorldMat;
274−};
275−
276−Q_DECLARE_METATYPE(UserInputMapper::InputPair)
277−Q_DECLARE_METATYPE(QVector<UserInputMapper::InputPair>)
278−Q_DECLARE_METATYPE(UserInputMapper::Input)
279−Q_DECLARE_METATYPE(UserInputMapper::InputChannel)
280−Q_DECLARE_METATYPE(QVector<UserInputMapper::InputChannel>)
281−Q_DECLARE_METATYPE(UserInputMapper::Action)
282−Q_DECLARE_METATYPE(QVector<UserInputMapper::Action>)
283−
284−#endif // hifi_UserInputMapper_h
libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp+145−135
@@ -12,15 +12,19 @@ · 11 unmodified lines
1212 #include "ViveControllerManager.h"
1313
1414 #include <PerfStat.h>
15−
16−#include "GeometryCache.h"
15+#include <PathUtils.h>
16+#include <GeometryCache.h>
1717 #include <gpu/Batch.h>
1818 #include <gpu/Context.h>
1919 #include <DeferredLightingEffect.h>
2020 #include <display-plugins/openvr/OpenVrHelpers.h>
21−#include "NumericalConstants.h"
21+#include <NumericalConstants.h>
2222 #include <plugins/PluginContainer.h>
23−#include "UserActivityLogger.h"
23+#include <UserActivityLogger.h>
24+
25+#include <controllers/UserInputMapper.h>
26+
27+#include <controllers/StandardControls.h>
2428
2529 #ifdef Q_OS_WIN
2630 extern vr::IVRSystem* _hmd;
@@ -29,18 +33,6 @@ extern vr::TrackedDevicePose_t _trackedDevicePose[vr::k_unMaxTrackedDeviceCount] · 2 unmodified lines
2933 extern mat4 _trackedDevicePoseMat4[vr::k_unMaxTrackedDeviceCount];
3034 #endif
3135
32−const unsigned int LEFT_MASK = 0U;
33−const unsigned int RIGHT_MASK = 1U;
34−
35−const uint64_t VR_BUTTON_A = 1U << 1;
36−const uint64_t VR_GRIP_BUTTON = 1U << 2;
37−const uint64_t VR_TRACKPAD_BUTTON = 1ULL << 32;
38−const uint64_t VR_TRIGGER_BUTTON = 1ULL << 33;
39−
40−const unsigned int BUTTON_A = 1U << 1;
41−const unsigned int GRIP_BUTTON = 1U << 2;
42−const unsigned int TRACKPAD_BUTTON = 1U << 3;
43−const unsigned int TRIGGER_BUTTON = 1U << 4;
4436
4537 const float CONTROLLER_LENGTH_OFFSET = 0.0762f; // three inches
4638 const QString CONTROLLER_MODEL_STRING = "vr_controller_05_wireless_b";
@@ -52,15 +44,17 @@ const QString MENU_NAME = "Vive Controllers"; · 5 unmodified lines
5244 const QString MENU_PATH = MENU_PARENT + ">" + MENU_NAME;
5345 const QString RENDER_CONTROLLERS = "Render Hand Controllers";
5446
47+static std::shared_ptr<ViveControllerManager> instance;
48+
5549 ViveControllerManager::ViveControllerManager() :
56− InputDevice("SteamVR Controller"),
50+ InputDevice("Vive"),
5751 _trackedControllers(0),
5852 _modelLoaded(false),
5953 _leftHandRenderID(0),
6054 _rightHandRenderID(0),
6155 _renderControllers(false)
6256 {
63−
57+ instance = std::shared_ptr<ViveControllerManager>(this);
6458 }
6559
6660 bool ViveControllerManager::isSupported() const {
@@ -79,6 +73,7 @@ bool ViveControllerManager::isSupported() const { · 12 unmodified lines
7973 }
8074
8175 void ViveControllerManager::activate() {
76+ InputPlugin::activate();
8277 #ifdef Q_OS_WIN
8378 CONTAINER->addMenu(MENU_PATH);
8479 CONTAINER->addMenuItem(MENU_PATH, RENDER_CONTROLLERS,
@@ -140,9 +135,16 @@ void ViveControllerManager::activate() { · 55 unmodified lines
140135 _renderControllers = true;
141136 }
142137 #endif
138+
139+ // unregister with UserInputMapper
140+ auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
141+ userInputMapper->registerDevice(instance);
142+ _registeredWithInputMapper = true;
143143 }
144144
145145 void ViveControllerManager::deactivate() {
146+ InputPlugin::deactivate();
147+
146148 #ifdef Q_OS_WIN
147149 CONTAINER->removeMenuItem(MENU_NAME, RENDER_CONTROLLERS);
148150 CONTAINER->removeMenu(MENU_PATH);
@@ -155,6 +157,11 @@ void ViveControllerManager::deactivate() { · 6 unmodified lines
155157 }
156158 _poseStateMap.clear();
157159 #endif
160+
161+ // unregister with UserInputMapper
162+ auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
163+ userInputMapper->removeDevice(_deviceID);
164+ _registeredWithInputMapper = false;
158165 }
159166
160167 void ViveControllerManager::updateRendering(RenderArgs* args, render::ScenePointer scene, render::PendingChanges pendingChanges) {
@@ -170,8 +177,8 @@ void ViveControllerManager::updateRendering(RenderArgs* args, render::ScenePoint · 9 unmodified lines
170177 //pendingChanges.updateItem(_leftHandRenderID, );
171178
172179
173− UserInputMapper::PoseValue leftHand = _poseStateMap[makeInput(JointChannel::LEFT_HAND).getChannel()];
174− UserInputMapper::PoseValue rightHand = _poseStateMap[makeInput(JointChannel::RIGHT_HAND).getChannel()];
180+ controller::Pose leftHand = _poseStateMap[controller::StandardPoseChannel::LEFT_HAND];
181+ controller::Pose rightHand = _poseStateMap[controller::StandardPoseChannel::RIGHT_HAND];
175182
176183 gpu::doInBatch(args->_context, [=](gpu::Batch& batch) {
177184 auto geometryCache = DependencyManager::get<GeometryCache>();
@@ -183,21 +190,20 @@ void ViveControllerManager::updateRendering(RenderArgs* args, render::ScenePoint · 5 unmodified lines
183190 //batch._glBindTexture(GL_TEXTURE_2D, _uexture);
184191
185192 if (leftHand.isValid()) {
186− renderHand(leftHand, batch, LEFT_HAND);
193+ renderHand(leftHand, batch, 1);
187194 }
188195 if (rightHand.isValid()) {
189− renderHand(rightHand, batch, RIGHT_HAND);
196+ renderHand(rightHand, batch, -1);
190197 }
191198 });
192199 }
193200 }
194201
195−void ViveControllerManager::renderHand(UserInputMapper::PoseValue pose, gpu::Batch& batch, int index) {
196− auto userInputMapper = DependencyManager::get<UserInputMapper>();
202+void ViveControllerManager::renderHand(const controller::Pose& pose, gpu::Batch& batch, int sign) {
203+ auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
197204 Transform transform(userInputMapper->getSensorToWorldMat());
198205 transform.postTranslate(pose.getTranslation() + pose.getRotation() * glm::vec3(0, 0, CONTROLLER_LENGTH_OFFSET));
199206
200− int sign = index == LEFT_HAND ? 1 : -1;
201207 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));
202208 transform.postRotate(rotation);
203209
@@ -248,6 +254,7 @@ void ViveControllerManager::update(float deltaTime, bool jointsCaptured) { · 44 unmodified lines
248254 }
249255
250256 numTrackedControllers++;
257+ bool left = numTrackedControllers == 1;
251258
252259 const mat4& mat = _trackedDevicePoseMat4[device];
253260
@@ -261,26 +268,30 @@ void ViveControllerManager::update(float deltaTime, bool jointsCaptured) { · 7 unmodified lines
261268 //qDebug() << (numTrackedControllers == 1 ? "Left: " : "Right: ");
262269 //qDebug() << "Trackpad: " << controllerState.rAxis[0].x << " " << controllerState.rAxis[0].y;
263270 //qDebug() << "Trigger: " << controllerState.rAxis[1].x << " " << controllerState.rAxis[1].y;
264− handleButtonEvent(controllerState.ulButtonPressed, numTrackedControllers - 1);
265− for (int i = 0; i < vr::k_unControllerStateAxisCount; i++) {
266− handleAxisEvent(Axis(i), controllerState.rAxis[i].x, controllerState.rAxis[i].y, numTrackedControllers - 1);
271+ for (uint32_t i = 0; i < vr::k_EButton_Max; ++i) {
272+ auto mask = vr::ButtonMaskFromId((vr::EVRButtonId)i);
273+ bool pressed = 0 != (controllerState.ulButtonPressed & mask);
274+ handleButtonEvent(i, pressed, left);
275+ }
276+ for (uint32_t i = 0; i < vr::k_unControllerStateAxisCount; i++) {
277+ handleAxisEvent(i, controllerState.rAxis[i].x, controllerState.rAxis[i].y, left);
267278 }
268279 }
269280 }
270281
271− auto userInputMapper = DependencyManager::get<UserInputMapper>();
282+ auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
272283
273284 if (numTrackedControllers == 0) {
274− if (_deviceID != 0) {
285+ if (_registeredWithInputMapper) {
275286 userInputMapper->removeDevice(_deviceID);
276− _deviceID = 0;
287+ _registeredWithInputMapper = false;
277288 _poseStateMap.clear();
278289 }
279290 }
280291
281292 if (_trackedControllers == 0 && numTrackedControllers > 0) {
282− registerToUserInputMapper(*userInputMapper);
283− assignDefaultInputMapping(*userInputMapper);
293+ userInputMapper->registerDevice(instance);
294+ _registeredWithInputMapper = true;
284295 UserActivityLogger::getInstance().connectedDevice("spatial_controller", "steamVR");
285296 }
286297
@@ -293,33 +304,45 @@ void ViveControllerManager::focusOutEvent() { · 6 unmodified lines
293304 _buttonPressedMap.clear();
294305 };
295306
296−void ViveControllerManager::handleAxisEvent(Axis axis, float x, float y, int index) {
297− if (axis == TRACKPAD_AXIS) {
298− _axisStateMap[makeInput(AXIS_Y_POS, index).getChannel()] = (y > 0.0f) ? y : 0.0f;
299− _axisStateMap[makeInput(AXIS_Y_NEG, index).getChannel()] = (y < 0.0f) ? -y : 0.0f;
300− _axisStateMap[makeInput(AXIS_X_POS, index).getChannel()] = (x > 0.0f) ? x : 0.0f;
301− _axisStateMap[makeInput(AXIS_X_NEG, index).getChannel()] = (x < 0.0f) ? -x : 0.0f;
302− } else if (axis == TRIGGER_AXIS) {
303− _axisStateMap[makeInput(BACK_TRIGGER, index).getChannel()] = x;
307+// These functions do translation from the Steam IDs to the standard controller IDs
308+void ViveControllerManager::handleAxisEvent(uint32_t axis, float x, float y, bool left) {
309+#ifdef Q_OS_WIN
310+ axis += vr::k_EButton_Axis0;
311+ using namespace controller;
312+ if (axis == vr::k_EButton_SteamVR_Touchpad) {
313+ _axisStateMap[left ? LX : RX] = x;
314+ _axisStateMap[left ? LY : RY] = y;
315+ } else if (axis == vr::k_EButton_SteamVR_Trigger) {
316+ //FIX ME: Seems that enters here everytime
317+ _axisStateMap[left ? LT : RT] = x;
304318 }
319+#endif
305320 }
306321
307−void ViveControllerManager::handleButtonEvent(uint64_t buttons, int index) {
308− if (buttons & VR_BUTTON_A) {
309− _buttonPressedMap.insert(makeInput(BUTTON_A, index).getChannel());
310− }
311− if (buttons & VR_GRIP_BUTTON) {
312− _buttonPressedMap.insert(makeInput(GRIP_BUTTON, index).getChannel());
313− }
314− if (buttons & VR_TRACKPAD_BUTTON) {
315− _buttonPressedMap.insert(makeInput(TRACKPAD_BUTTON, index).getChannel());
322+// These functions do translation from the Steam IDs to the standard controller IDs
323+void ViveControllerManager::handleButtonEvent(uint32_t button, bool pressed, bool left) {
324+#ifdef Q_OS_WIN
325+ if (!pressed) {
326+ return;
316327 }
317− if (buttons & VR_TRIGGER_BUTTON) {
318− _buttonPressedMap.insert(makeInput(TRIGGER_BUTTON, index).getChannel());
328+
329+ if (button == vr::k_EButton_ApplicationMenu) {
330+ _buttonPressedMap.insert(left ? controller::LEFT_PRIMARY_THUMB : controller::RIGHT_PRIMARY_THUMB);
331+ } else if (button == vr::k_EButton_Grip) {
332+ // Tony says these are harder to reach, so make them the meta buttons
333+ _buttonPressedMap.insert(left ? controller::LB : controller::RB);
334+ } else if (button == vr::k_EButton_SteamVR_Trigger) {
335+ _buttonPressedMap.insert(left ? controller::LT : controller::RT);
336+ } else if (button == vr::k_EButton_SteamVR_Touchpad) {
337+ _buttonPressedMap.insert(left ? controller::LS : controller::RS);
338+ } else if (button == vr::k_EButton_System) {
339+ //FIX ME: not able to ovrewrite the behaviour of this button
340+ _buttonPressedMap.insert(left ? controller::LEFT_SECONDARY_THUMB : controller::RIGHT_SECONDARY_THUMB);
319341 }
342+#endif
320343 }
321344
322−void ViveControllerManager::handlePoseEvent(const mat4& mat, int index) {
345+void ViveControllerManager::handlePoseEvent(const mat4& mat, bool left) {
323346 glm::vec3 position = extractTranslation(mat);
324347 glm::quat rotation = glm::quat_cast(mat);
325348
@@ -375,7 +398,7 @@ void ViveControllerManager::handlePoseEvent(const mat4& mat, int index) { · 49 unmodified lines
375398
376399 const glm::quat quarterX = glm::angleAxis(PI / 2.0f, glm::vec3(1.0f, 0.0f, 0.0f));
377400 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;
379402 const glm::quat signedQuaterZ = glm::angleAxis(sign * PI / 2.0f, glm::vec3(0.0f, 0.0f, 1.0f));
380403 const glm::quat eighthX = glm::angleAxis(PI / 4.0f, glm::vec3(1.0f, 0.0f, 0.0f));
381404 const glm::quat offset = glm::inverse(signedQuaterZ * eighthX);
@@ -383,92 +406,79 @@ void ViveControllerManager::handlePoseEvent(const mat4& mat, int index) { · 1 unmodified line
383406
384407 position += rotation * glm::vec3(0, 0, -CONTROLLER_LENGTH_OFFSET);
385408
386− _poseStateMap[makeInput(JointChannel(index)).getChannel()] = UserInputMapper::PoseValue(position, rotation);
409+ _poseStateMap[left ? controller::LEFT_HAND : controller::RIGHT_HAND] = controller::Pose(position, rotation);
387410 }
388411
389−void ViveControllerManager::registerToUserInputMapper(UserInputMapper& mapper) {
390− // Grab the current free device ID
391− _deviceID = mapper.getFreeDeviceID();
392−
393− auto proxy = UserInputMapper::DeviceProxy::Pointer(new UserInputMapper::DeviceProxy(_name));
394− proxy->getButton = [this] (const UserInputMapper::Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); };
395− proxy->getAxis = [this] (const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); };
396− proxy->getPose = [this](const UserInputMapper::Input& input, int timestamp) -> UserInputMapper::PoseValue { return this->getPose(input.getChannel()); };
397− proxy->getAvailabeInputs = [this] () -> QVector<UserInputMapper::InputPair> {
398− QVector<UserInputMapper::InputPair> availableInputs;
399− availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_HAND), "Left Hand"));
400−
401− availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_A, 0), "Left Button A"));
402− availableInputs.append(UserInputMapper::InputPair(makeInput(GRIP_BUTTON, 0), "Left Grip Button"));
403− availableInputs.append(UserInputMapper::InputPair(makeInput(TRACKPAD_BUTTON, 0), "Left Trackpad Button"));
404− availableInputs.append(UserInputMapper::InputPair(makeInput(TRIGGER_BUTTON, 0), "Left Trigger Button"));
405−
406− availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_Y_POS, 0), "Left Trackpad Up"));
407− availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_Y_NEG, 0), "Left Trackpad Down"));
408− availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_X_POS, 0), "Left Trackpad Right"));
409− availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_X_NEG, 0), "Left Trackpad Left"));
410− availableInputs.append(UserInputMapper::InputPair(makeInput(BACK_TRIGGER, 0), "Left Back Trigger"));
411−
412−
413− availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_HAND), "Right Hand"));
414−
415− availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_A, 1), "Right Button A"));
416− availableInputs.append(UserInputMapper::InputPair(makeInput(GRIP_BUTTON, 1), "Right Grip Button"));
417− availableInputs.append(UserInputMapper::InputPair(makeInput(TRACKPAD_BUTTON, 1), "Right Trackpad Button"));
418− availableInputs.append(UserInputMapper::InputPair(makeInput(TRIGGER_BUTTON, 1), "Right Trigger Button"));
419−
420− availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_Y_POS, 1), "Right Trackpad Up"));
421− availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_Y_NEG, 1), "Right Trackpad Down"));
422− availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_X_POS, 1), "Right Trackpad Right"));
423− availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_X_NEG, 1), "Right Trackpad Left"));
424− availableInputs.append(UserInputMapper::InputPair(makeInput(BACK_TRIGGER, 1), "Right Back Trigger"));
425−
426− return availableInputs;
412+controller::Input::NamedVector ViveControllerManager::getAvailableInputs() const {
413+ using namespace controller;
414+ QVector<Input::NamedPair> availableInputs{
415+ // Trackpad analogs
416+ makePair(LX, "LX"),
417+ makePair(LY, "LY"),
418+ makePair(RX, "RX"),
419+ makePair(RY, "RY"),
420+ // trigger analogs
421+ makePair(LT, "LT"),
422+ makePair(RT, "RT"),
423+
424+ makePair(LB, "LB"),
425+ makePair(RB, "RB"),
426+
427+ makePair(LS, "LS"),
428+ makePair(RS, "RS"),
429+ makePair(LEFT_HAND, "LeftHand"),
430+ makePair(RIGHT_HAND, "RightHand"),
431+
432+ makePair(LEFT_PRIMARY_THUMB, "LeftPrimaryThumb"),
433+ makePair(LEFT_SECONDARY_THUMB, "LeftSecondaryThumb"),
434+ makePair(RIGHT_PRIMARY_THUMB, "RightPrimaryThumb"),
435+ makePair(RIGHT_SECONDARY_THUMB, "RightSecondaryThumb"),
427436 };
428− proxy->resetDeviceBindings = [this, &mapper] () -> bool {
429− mapper.removeAllInputChannelsForDevice(_deviceID);
430− this->assignDefaultInputMapping(mapper);
431− return true;
432− };
433− mapper.registerDevice(_deviceID, proxy);
434−}
435−
436−void ViveControllerManager::assignDefaultInputMapping(UserInputMapper& mapper) {
437− const float JOYSTICK_MOVE_SPEED = 1.0f;
438−
439− // Left Trackpad: Movement, strafing
440− mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(AXIS_Y_POS, 0), makeInput(TRACKPAD_BUTTON, 0), JOYSTICK_MOVE_SPEED);
441− mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(AXIS_Y_NEG, 0), makeInput(TRACKPAD_BUTTON, 0), JOYSTICK_MOVE_SPEED);
442− mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(AXIS_X_POS, 0), makeInput(TRACKPAD_BUTTON, 0), JOYSTICK_MOVE_SPEED);
443− mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(AXIS_X_NEG, 0), makeInput(TRACKPAD_BUTTON, 0), JOYSTICK_MOVE_SPEED);
444−
445− // Right Trackpad: Vertical movement, zooming
446− mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(AXIS_Y_POS, 1), makeInput(TRACKPAD_BUTTON, 1), JOYSTICK_MOVE_SPEED);
447− mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(AXIS_Y_NEG, 1), makeInput(TRACKPAD_BUTTON, 1), JOYSTICK_MOVE_SPEED);
448−
449− // Buttons
450− mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(BUTTON_A, 0));
451− mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(BUTTON_A, 1));
452−
453− mapper.addInputChannel(UserInputMapper::ACTION1, makeInput(GRIP_BUTTON, 0));
454− mapper.addInputChannel(UserInputMapper::ACTION2, makeInput(GRIP_BUTTON, 1));
455−
456− mapper.addInputChannel(UserInputMapper::LEFT_HAND_CLICK, makeInput(BACK_TRIGGER, 0));
457− mapper.addInputChannel(UserInputMapper::RIGHT_HAND_CLICK, makeInput(BACK_TRIGGER, 1));
458−
459− // Hands
460− mapper.addInputChannel(UserInputMapper::LEFT_HAND, makeInput(LEFT_HAND));
461− mapper.addInputChannel(UserInputMapper::RIGHT_HAND, makeInput(RIGHT_HAND));
462−}
463437
464−UserInputMapper::Input ViveControllerManager::makeInput(unsigned int button, int index) {
465− return UserInputMapper::Input(_deviceID, button | (index == 0 ? LEFT_MASK : RIGHT_MASK), UserInputMapper::ChannelType::BUTTON);
438+ //availableInputs.append(Input::NamedPair(makeInput(BUTTON_A, 0), "Left Button A"));
439+ //availableInputs.append(Input::NamedPair(makeInput(GRIP_BUTTON, 0), "Left Grip Button"));
440+ //availableInputs.append(Input::NamedPair(makeInput(TRACKPAD_BUTTON, 0), "Left Trackpad Button"));
441+ //availableInputs.append(Input::NamedPair(makeInput(TRIGGER_BUTTON, 0), "Left Trigger Button"));
442+ //availableInputs.append(Input::NamedPair(makeInput(BACK_TRIGGER, 0), "Left Back Trigger"));
443+ //availableInputs.append(Input::NamedPair(makeInput(RIGHT_HAND), "Right Hand"));
444+ //availableInputs.append(Input::NamedPair(makeInput(BUTTON_A, 1), "Right Button A"));
445+ //availableInputs.append(Input::NamedPair(makeInput(GRIP_BUTTON, 1), "Right Grip Button"));
446+ //availableInputs.append(Input::NamedPair(makeInput(TRACKPAD_BUTTON, 1), "Right Trackpad Button"));
447+ //availableInputs.append(Input::NamedPair(makeInput(TRIGGER_BUTTON, 1), "Right Trigger Button"));
448+ //availableInputs.append(Input::NamedPair(makeInput(BACK_TRIGGER, 1), "Right Back Trigger"));
449+
450+ return availableInputs;
466451 }
467452
468−UserInputMapper::Input ViveControllerManager::makeInput(ViveControllerManager::JoystickAxisChannel axis, int index) {
469− return UserInputMapper::Input(_deviceID, axis | (index == 0 ? LEFT_MASK : RIGHT_MASK), UserInputMapper::ChannelType::AXIS);
453+QString ViveControllerManager::getDefaultMappingConfig() const {
454+ static const QString MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/vive.json";
455+ return MAPPING_JSON;
470456 }
471457
472−UserInputMapper::Input ViveControllerManager::makeInput(JointChannel joint) {
473− return UserInputMapper::Input(_deviceID, joint, UserInputMapper::ChannelType::POSE);
474−}
458+//void ViveControllerManager::assignDefaultInputMapping(UserInputMapper& mapper) {
459+// const float JOYSTICK_MOVE_SPEED = 1.0f;
460+//
461+// // Left Trackpad: Movement, strafing
462+// mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(AXIS_Y_POS, 0), makeInput(TRACKPAD_BUTTON, 0), JOYSTICK_MOVE_SPEED);
463+// mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(AXIS_Y_NEG, 0), makeInput(TRACKPAD_BUTTON, 0), JOYSTICK_MOVE_SPEED);
464+// mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(AXIS_X_POS, 0), makeInput(TRACKPAD_BUTTON, 0), JOYSTICK_MOVE_SPEED);
465+// mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(AXIS_X_NEG, 0), makeInput(TRACKPAD_BUTTON, 0), JOYSTICK_MOVE_SPEED);
466+//
467+// // Right Trackpad: Vertical movement, zooming
468+// mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(AXIS_Y_POS, 1), makeInput(TRACKPAD_BUTTON, 1), JOYSTICK_MOVE_SPEED);
469+// mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(AXIS_Y_NEG, 1), makeInput(TRACKPAD_BUTTON, 1), JOYSTICK_MOVE_SPEED);
470+//
471+// // Buttons
472+// mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(BUTTON_A, 0));
473+// mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(BUTTON_A, 1));
474+//
475+// mapper.addInputChannel(UserInputMapper::ACTION1, makeInput(GRIP_BUTTON, 0));
476+// mapper.addInputChannel(UserInputMapper::ACTION2, makeInput(GRIP_BUTTON, 1));
477+//
478+// mapper.addInputChannel(UserInputMapper::LEFT_HAND_CLICK, makeInput(BACK_TRIGGER, 0));
479+// mapper.addInputChannel(UserInputMapper::RIGHT_HAND_CLICK, makeInput(BACK_TRIGGER, 1));
480+//
481+// // Hands
482+// mapper.addInputChannel(UserInputMapper::LEFT_HAND, makeInput(LEFT_HAND));
483+// mapper.addInputChannel(UserInputMapper::RIGHT_HAND, makeInput(RIGHT_HAND));
484+//}
libraries/input-plugins/src/input-plugins/ViveControllerManager.h+11−33
@@ -19,35 +19,14 @@ · 18 unmodified lines
1919
2020 #include <model/Geometry.h>
2121 #include <gpu/Texture.h>
22−#include "InputDevice.h"
22+#include <controllers/InputDevice.h>
2323 #include "InputPlugin.h"
2424 #include <RenderArgs.h>
2525 #include <render/Scene.h>
2626
27−class ViveControllerManager : public InputPlugin, public InputDevice {
27+class ViveControllerManager : public InputPlugin, public controller::InputDevice {
2828 Q_OBJECT
2929 public:
30− enum JoystickAxisChannel {
31− AXIS_Y_POS = 1U << 1,
32− AXIS_Y_NEG = 1U << 2,
33− AXIS_X_POS = 1U << 3,
34− AXIS_X_NEG = 1U << 4,
35− BACK_TRIGGER = 1U << 5,
36− };
37−
38− enum Axis {
39− TRACKPAD_AXIS = 0,
40− TRIGGER_AXIS,
41− AXIS_2,
42− AXIS_3,
43− AXIS_4,
44− };
45−
46− enum JointChannel {
47− LEFT_HAND = 0,
48− RIGHT_HAND,
49− };
50−
5130 ViveControllerManager();
5231
5332 // Plugin functions
@@ -62,8 +41,8 @@ public: · 8 unmodified lines
6241 virtual void pluginUpdate(float deltaTime, bool jointsCaptured) override { update(deltaTime, jointsCaptured); }
6342
6443 // Device functions
65− virtual void registerToUserInputMapper(UserInputMapper& mapper) override;
66− virtual void assignDefaultInputMapping(UserInputMapper& mapper) override;
44+ virtual controller::Input::NamedVector getAvailableInputs() const override;
45+ virtual QString getDefaultMappingConfig() const override;
6746 virtual void update(float deltaTime, bool jointsCaptured) override;
6847 virtual void focusOutEvent() override;
6948
@@ -71,16 +50,12 @@ public: · 1 unmodified line
7150
7251 void setRenderControllers(bool renderControllers) { _renderControllers = renderControllers; }
7352
74− UserInputMapper::Input makeInput(unsigned int button, int index);
75− UserInputMapper::Input makeInput(JoystickAxisChannel axis, int index);
76− UserInputMapper::Input makeInput(JointChannel joint);
77−
7853 private:
79− void renderHand(UserInputMapper::PoseValue pose, gpu::Batch& batch, int index);
54+ void renderHand(const controller::Pose& pose, gpu::Batch& batch, int sign);
8055
81− void handleButtonEvent(uint64_t buttons, int index);
82− void handleAxisEvent(Axis axis, float x, float y, int index);
83− void handlePoseEvent(const mat4& mat, int index);
56+ void handleButtonEvent(uint32_t button, bool pressed, bool left);
57+ void handleAxisEvent(uint32_t axis, float x, float y, bool left);
58+ void handlePoseEvent(const mat4& mat, bool left);
8459
8560 int _trackedControllers;
8661
@@ -94,6 +69,9 @@ private: · 7 unmodified lines
9469 bool _renderControllers;
9570
9671 static const QString NAME;
72+
73+ bool _registeredWithInputMapper { false };
74+
9775 };
9876
9977 #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
8989
9090 // Move to the AssetClient thread in case we are not currently on that thread (which will usually be the case)
9191 request->moveToThread(thread());
92− request->setParent(this);
9392
9493 return request;
9594 } else {
@@ -105,7 +104,6 @@ AssetUpload* AssetClient::createUpload(const QString& filename) { · 9 unmodified lines
105104 auto upload = new AssetUpload(filename);
106105
107106 upload->moveToThread(thread());
108− upload->setParent(this);
109107
110108 return upload;
111109 } else {
@@ -118,7 +116,6 @@ AssetUpload* AssetClient::createUpload(const QByteArray& data, const QString& ex · 6 unmodified lines
118116 auto upload = new AssetUpload(data, extension);
119117
120118 upload->moveToThread(thread());
121− upload->setParent(this);
122119
123120 return upload;
124121 } else {
libraries/networking/src/udt/PacketHeaders.cpp+1−1
@@ -38,7 +38,7 @@ PacketVersion versionForPacketType(PacketType packetType) { · 37 unmodified lines
3838 case PacketType::EntityAdd:
3939 case PacketType::EntityEdit:
4040 case PacketType::EntityData:
41− return VERSION_ENTITIES_KEYLIGHT_PROPERTIES_GROUP;
41+ return VERSION_ENTITIES_KEYLIGHT_PROPERTIES_GROUP_BIS;
4242 case PacketType::AvatarData:
4343 case PacketType::BulkAvatarData:
4444 default:
libraries/networking/src/udt/PacketHeaders.h+1−0
@@ -144,5 +144,6 @@ const PacketVersion VERSION_ENTITIES_PARTICLE_ELLIPSOID_EMITTER = 44; · 143 unmodified lines
144144 const PacketVersion VERSION_ENTITIES_PROTOCOL_CHANNELS = 45;
145145 const PacketVersion VERSION_ENTITIES_ANIMATION_PROPERTIES_GROUP = 46;
146146 const PacketVersion VERSION_ENTITIES_KEYLIGHT_PROPERTIES_GROUP = 47;
147+const PacketVersion VERSION_ENTITIES_KEYLIGHT_PROPERTIES_GROUP_BIS = 48;
147148
148149 #endif // hifi_PacketHeaders_h
libraries/physics/src/CharacterController.cpp+2−2
@@ -14,11 +14,11 @@ · 13 unmodified lines
1414 #include "PhysicsCollisionGroups.h"
1515
1616 bool CharacterController::needsRemoval() const {
17− return (bool)(_pendingFlags & PENDING_FLAG_REMOVE_FROM_SIMULATION);
17+ return ((_pendingFlags & PENDING_FLAG_REMOVE_FROM_SIMULATION) == PENDING_FLAG_REMOVE_FROM_SIMULATION);
1818 }
1919
2020 bool CharacterController::needsAddition() const {
21− return (bool)(_pendingFlags & PENDING_FLAG_ADD_TO_SIMULATION);
21+ return ((_pendingFlags & PENDING_FLAG_ADD_TO_SIMULATION) == PENDING_FLAG_ADD_TO_SIMULATION);
2222 }
2323
2424 void CharacterController::setDynamicsWorld(btDynamicsWorld* world) {
libraries/physics/src/PhysicsEngine.cpp+3−2
@@ -241,7 +241,7 @@ void PhysicsEngine::stepSimulation() { · 240 unmodified lines
241241 float timeStep = btMin(dt, MAX_TIMESTEP);
242242
243243 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
245245 // the updateShapeIfNecessary() call needs info from MyAvatar and should
246246 // be done on the main thread during the pre-simulation stuff
247247 if (_myAvatarController->needsRemoval()) {
@@ -263,7 +263,8 @@ void PhysicsEngine::stepSimulation() { · 15 unmodified lines
263263 updateContactMap();
264264 };
265265
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);
267268 if (numSubsteps > 0) {
268269 BT_PROFILE("postSimulation");
269270 _numSubsteps += (uint32_t)numSubsteps;
libraries/physics/src/ThreadSafeDynamicsWorld.cpp+3−2
@@ -27,8 +27,9 @@ ThreadSafeDynamicsWorld::ThreadSafeDynamicsWorld( · 26 unmodified lines
2727 : btDiscreteDynamicsWorld(dispatcher, pairCache, constraintSolver, collisionConfiguration) {
2828 }
2929
30−int ThreadSafeDynamicsWorld::stepSimulation( btScalar timeStep, int maxSubSteps, btScalar fixedTimeStep, SubStepCallback onSubStep) {
31− BT_PROFILE("stepSimulation");
30+int ThreadSafeDynamicsWorld::stepSimulationWithSubstepCallback(btScalar timeStep, int maxSubSteps,
31+ btScalar fixedTimeStep, SubStepCallback onSubStep) {
32+ BT_PROFILE("stepSimulationWithSubstepCallback");
3233 int subSteps = 0;
3334 if (maxSubSteps) {
3435 //fixed timestep with interpolation
libraries/physics/src/ThreadSafeDynamicsWorld.h+3−2
@@ -37,8 +37,9 @@ public: · 36 unmodified lines
3737 btConstraintSolver* constraintSolver,
3838 btCollisionConfiguration* collisionConfiguration);
3939
40− // virtual overrides from btDiscreteDynamicsWorld
41− int stepSimulation( btScalar timeStep, int maxSubSteps=1, btScalar fixedTimeStep=btScalar(1.)/btScalar(60.), SubStepCallback onSubStep = []() { });
40+ int stepSimulationWithSubstepCallback(btScalar timeStep, int maxSubSteps = 1,
41+ btScalar fixedTimeStep = btScalar(1.)/btScalar(60.),
42+ SubStepCallback onSubStep = []() { });
4243 void synchronizeMotionStates();
4344
4445 // 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
3333 virtual void deinit();
3434
3535 /// 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+
3740 /// 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+ }
3948
4049 /**
4150 * Called by the application during it's idle phase. If the plugin needs to do
@@ -48,6 +57,7 @@ public: · 6 unmodified lines
4857 virtual void loadSettings() {}
4958
5059 protected:
60+ bool _active{ false };
5161 static PluginContainer* CONTAINER;
5262 static QString UNKNOWN_PLUGIN_ID;
5363
libraries/script-engine/CMakeLists.txt+1−1
@@ -1,3 +1,3 @@
11 set(TARGET_NAME script-engine)
22 setup_hifi_library(Gui Network Script WebSockets Widgets)
3−link_hifi_libraries(shared networking octree gpu procedural model model-networking fbx entities animation audio physics)
3+link_hifi_libraries(shared networking octree gpu procedural model model-networking fbx entities controllers animation audio physics)
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−
27−class ScriptEngine;
28−
29−class AbstractInputController : public QObject {
30− Q_OBJECT
31−
32−public:
33− typedef unsigned int Key;
34−
35− virtual void update() = 0;
36−
37− virtual Key getKey() const = 0;
38−
39−public slots:
40−
41− virtual bool isActive() const = 0;
42− virtual glm::vec3 getAbsTranslation() const = 0;
43− virtual glm::quat getAbsRotation() const = 0;
44− virtual glm::vec3 getLocTranslation() const = 0;
45− virtual glm::quat getLocRotation() const = 0;
46−
47−signals:
48− void spatialEvent(const SpatialEvent& event);
49−
50−};
51−
52−/// handles scripting of input controller commands from JS
53−class AbstractControllerScriptingInterface : public QObject {
54− Q_OBJECT
55−
56−public slots:
57− virtual void registerControllerTypes(ScriptEngine* engine) = 0;
58−
59− virtual bool isPrimaryButtonPressed() const = 0;
60− virtual glm::vec2 getPrimaryJoystickPosition() const = 0;
61−
62− virtual int getNumberOfButtons() const = 0;
63− virtual bool isButtonPressed(int buttonIndex) const = 0;
64−
65− virtual int getNumberOfTriggers() const = 0;
66− virtual float getTriggerValue(int triggerIndex) const = 0;
67−
68− virtual int getNumberOfJoysticks() const = 0;
69− virtual glm::vec2 getJoystickPosition(int joystickIndex) const = 0;
70−
71− virtual int getNumberOfSpatialControls() const = 0;
72− virtual glm::vec3 getSpatialControlPosition(int controlIndex) const = 0;
73− virtual glm::vec3 getSpatialControlVelocity(int controlIndex) const = 0;
74− virtual glm::vec3 getSpatialControlNormal(int controlIndex) const = 0;
75− virtual glm::quat getSpatialControlRawRotation(int controlIndex) const = 0;
76−
77− virtual void captureKeyEvents(const KeyEvent& event) = 0;
78− virtual void releaseKeyEvents(const KeyEvent& event) = 0;
79−
80− virtual void captureMouseEvents() = 0;
81− virtual void releaseMouseEvents() = 0;
82−
83− virtual void captureTouchEvents() = 0;
84− virtual void releaseTouchEvents() = 0;
85−
86− virtual void captureWheelEvents() = 0;
87− virtual void releaseWheelEvents() = 0;
88−
89− virtual void captureActionEvents() = 0;
90− virtual void releaseActionEvents() = 0;
91−
92− virtual void captureJoystick(int joystickIndex) = 0;
93− virtual void releaseJoystick(int joystickIndex) = 0;
94−
95− virtual glm::vec2 getViewportDimensions() const = 0;
96−
97−
98− virtual AbstractInputController* createInputController( const QString& category, const QString& tracker ) = 0;
99−
100−signals:
101− void keyPressEvent(const KeyEvent& event);
102− void keyReleaseEvent(const KeyEvent& event);
103−
104− void actionStartEvent(const HFActionEvent& event);
105− void actionEndEvent(const HFActionEvent& event);
106−
107− void backStartEvent();
108− void backEndEvent();
109−
110− void mouseMoveEvent(const MouseEvent& event, unsigned int deviceID = 0);
111− void mousePressEvent(const MouseEvent& event, unsigned int deviceID = 0);
112− void mouseDoublePressEvent(const MouseEvent& event, unsigned int deviceID = 0);
113− void mouseReleaseEvent(const MouseEvent& event, unsigned int deviceID = 0);
114−
115− void touchBeginEvent(const TouchEvent& event);
116− void touchEndEvent(const TouchEvent& event);
117− void touchUpdateEvent(const TouchEvent& event);
118−
119− void wheelEvent(const WheelEvent& event);
120−
121− void actionEvent(int action, float state);
122−
123−};
124−
125−#endif // hifi_AbstractControllerScriptingInterface_h
libraries/script-engine/src/AbstractScriptingServicesInterface.h+0−8
@@ -12,21 +12,13 @@ · 11 unmodified lines
1212 #ifndef hifi_AbstractScriptingServicesInterface_h
1313 #define hifi_AbstractScriptingServicesInterface_h
1414
15−class AbstractControllerScriptingInterface;
16−class Transform;
1715 class ScriptEngine;
18−class QThread;
1916
2017 /// Interface provided by Application to other objects that need access to scripting services of the application
2118 class AbstractScriptingServicesInterface {
2219 public:
23−
24− /// Returns the controller interface for the application
25− virtual AbstractControllerScriptingInterface* getControllerScriptingInterface() = 0;
26−
2720 /// Registers application specific services with a script engine.
2821 virtual void registerScriptEngineWithApplicationServices(ScriptEngine* scriptEngine) = 0;
29−
3022 };
3123
3224
libraries/script-engine/src/ScriptEngine.cpp+59−17
@@ -28,7 +28,9 @@ · 27 unmodified lines
2828 #include <udt/PacketHeaders.h>
2929 #include <UUID.h>
3030
31−#include "AnimationObject.h"
31+#include <controllers/ScriptingInterface.h>
32+#include <AnimationObject.h>
33+
3234 #include "ArrayBufferViewClass.h"
3335 #include "BatchLoader.h"
3436 #include "DataViewClass.h"
@@ -76,13 +78,16 @@ void avatarDataFromScriptValue(const QScriptValue &object, AvatarData* &out) { · 41 unmodified lines
7678 out = qobject_cast<AvatarData*>(object.toQObject());
7779 }
7880
79−QScriptValue inputControllerToScriptValue(QScriptEngine *engine, AbstractInputController* const &in) {
81+QScriptValue inputControllerToScriptValue(QScriptEngine *engine, controller::InputController* const &in) {
8082 return engine->newQObject(in);
8183 }
82−void inputControllerFromScriptValue(const QScriptValue &object, AbstractInputController* &out) {
83− out = qobject_cast<AbstractInputController*>(object.toQObject());
84+
85+void inputControllerFromScriptValue(const QScriptValue &object, controller::InputController* &out) {
86+ out = qobject_cast<controller::InputController*>(object.toQObject());
8487 }
8588
89+
90+
8691 static bool hasCorrectSyntax(const QScriptProgram& program) {
8792 const auto syntaxCheck = QScriptEngine::checkSyntax(program.sourceCode());
8893 if (syntaxCheck.state() != QScriptSyntaxCheckResult::Valid) {
@@ -114,16 +119,13 @@ static bool hadUncaughtExceptions(QScriptEngine& engine, const QString& fileName · 25 unmodified lines
114119 return false;
115120 }
116121
117−ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNameString,
118− AbstractControllerScriptingInterface* controllerScriptingInterface, bool wantSignals) :
119−
122+ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNameString, bool wantSignals) :
120123 _scriptContents(scriptContents),
121124 _isFinished(false),
122125 _isRunning(false),
123126 _isInitialized(false),
124127 _timerFunctionMap(),
125128 _wantSignals(wantSignals),
126− _controllerScriptingInterface(controllerScriptingInterface),
127129 _fileNameString(fileNameString),
128130 _isUserLoaded(false),
129131 _isReloading(false),
@@ -285,6 +287,27 @@ void ScriptEngine::errorInLoadingScript(const QUrl& url) { · 155 unmodified lines
285287 }
286288 }
287289
290+// Even though we never pass AnimVariantMap directly to and from javascript, the queued invokeMethod of
291+// callAnimationStateHandler requires that the type be registered.
292+// These two are meaningful, if we ever do want to use them...
293+static QScriptValue animVarMapToScriptValue(QScriptEngine* engine, const AnimVariantMap& parameters) {
294+ QStringList unused;
295+ return parameters.animVariantMapToScriptValue(engine, unused, false);
296+}
297+static void animVarMapFromScriptValue(const QScriptValue& value, AnimVariantMap& parameters) {
298+ parameters.animVariantMapFromScriptValue(value);
299+}
300+// ... while these two are not. But none of the four are ever used.
301+static QScriptValue resultHandlerToScriptValue(QScriptEngine* engine, const AnimVariantResultHandler& resultHandler) {
302+ qCCritical(scriptengine) << "Attempt to marshall result handler to javascript";
303+ assert(false);
304+ return QScriptValue();
305+}
306+static void resultHandlerFromScriptValue(const QScriptValue& value, AnimVariantResultHandler& resultHandler) {
307+ qCCritical(scriptengine) << "Attempt to marshall result handler from javascript";
308+ assert(false);
309+}
310+
288311 void ScriptEngine::init() {
289312 if (_isInitialized) {
290313 return; // only initialize once
@@ -337,27 +360,26 @@ void ScriptEngine::init() { · 46 unmodified lines
337360
338361 registerGlobalObject("Script", this);
339362 registerGlobalObject("Audio", &AudioScriptingInterface::getInstance());
340− registerGlobalObject("Controller", _controllerScriptingInterface);
341363 registerGlobalObject("Entities", entityScriptingInterface.data());
342364 registerGlobalObject("Quat", &_quatLibrary);
343365 registerGlobalObject("Vec3", &_vec3Library);
344366 registerGlobalObject("Uuid", &_uuidLibrary);
345367 registerGlobalObject("AnimationCache", DependencyManager::get<AnimationCache>().data());
368+ qScriptRegisterMetaType(this, animVarMapToScriptValue, animVarMapFromScriptValue);
369+ qScriptRegisterMetaType(this, resultHandlerToScriptValue, resultHandlerFromScriptValue);
346370
347371 // constants
348372 globalObject().setProperty("TREE_SCALE", newVariant(QVariant(TREE_SCALE)));
349373
350− if (_controllerScriptingInterface) {
351− _controllerScriptingInterface->registerControllerTypes(this);
352− }
353−
354−
374+ auto scriptingInterface = DependencyManager::get<controller::ScriptingInterface>();
375+ registerGlobalObject("Controller", scriptingInterface.data());
376+ UserInputMapper::registerControllerTypes(this);
355377 }
356378
357379 void ScriptEngine::registerValue(const QString& valueName, QScriptValue value) {
358380 if (QThread::currentThread() != thread()) {
359381 #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() << "]";
361383 #endif
362384 QMetaObject::invokeMethod(this, "registerValue",
363385 Q_ARG(const QString&, valueName),
@@ -743,6 +765,27 @@ void ScriptEngine::stop() { · 379 unmodified lines
743765 }
744766 }
745767
768+// Other threads can invoke this through invokeMethod, which causes the callback to be asynchronously executed in this script's thread.
769+void ScriptEngine::callAnimationStateHandler(QScriptValue callback, AnimVariantMap parameters, QStringList names, bool useNames, AnimVariantResultHandler resultHandler) {
770+ if (QThread::currentThread() != thread()) {
771+#ifdef THREAD_DEBUGGING
772+ qDebug() << "*** WARNING *** ScriptEngine::callAnimationStateHandler() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] name:" << name;
773+#endif
774+ QMetaObject::invokeMethod(this, "callAnimationStateHandler",
775+ Q_ARG(QScriptValue, callback),
776+ Q_ARG(AnimVariantMap, parameters),
777+ Q_ARG(QStringList, names),
778+ Q_ARG(bool, useNames),
779+ Q_ARG(AnimVariantResultHandler, resultHandler));
780+ return;
781+ }
782+ QScriptValue javascriptParameters = parameters.animVariantMapToScriptValue(this, names, useNames);
783+ QScriptValueList callingArguments;
784+ callingArguments << javascriptParameters;
785+ QScriptValue result = callback.call(QScriptValue(), callingArguments);
786+ resultHandler(result);
787+}
788+
746789 void ScriptEngine::timerFired() {
747790 QTimer* callingTimer = reinterpret_cast<QTimer*>(sender());
748791 QScriptValue timerFunction = _timerFunctionMap.value(callingTimer);
@@ -928,9 +971,8 @@ void ScriptEngine::forwardHandlerCall(const EntityItemID& entityID, const QStrin · 179 unmodified lines
928971 if (QThread::currentThread() != thread()) {
929972 qDebug() << "*** ERROR *** ScriptEngine::forwardHandlerCall() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "]";
930973 assert(false);
931− return;
974+ return ;
932975 }
933−
934976 if (!_registeredHandlers.contains(entityID)) {
935977 return;
936978 }
libraries/script-engine/src/ScriptEngine.h+6−4
@@ -21,13 +21,14 @@ · 20 unmodified lines
2121 #include <QtScript/QScriptEngine>
2222
2323 #include <AnimationCache.h>
24+#include <AnimVariant.h>
2425 #include <AvatarData.h>
2526 #include <AvatarHashMap.h>
2627 #include <LimitedNodeList.h>
2728 #include <EntityItemID.h>
2829 #include <EntitiesScriptEngineProvider.h>
2930
30−#include "AbstractControllerScriptingInterface.h"
31+#include "MouseEvent.h"
3132 #include "ArrayBufferClass.h"
3233 #include "AudioScriptingInterface.h"
3334 #include "Quat.h"
@@ -53,7 +54,6 @@ class ScriptEngine : public QScriptEngine, public ScriptUser, public EntitiesScr · 19 unmodified lines
5354 public:
5455 ScriptEngine(const QString& scriptContents = NO_SCRIPT,
5556 const QString& fileNameString = QString(""),
56− AbstractControllerScriptingInterface* controllerScriptingInterface = NULL,
5757 bool wantSignals = true);
5858
5959 ~ScriptEngine();
@@ -142,6 +142,9 @@ public: · 82 unmodified lines
142142 // NOTE - this is used by the TypedArray implemetation. we need to review this for thread safety
143143 ArrayBufferClass* getArrayBufferClass() { return _arrayBufferClass; }
144144
145+public slots:
146+ void callAnimationStateHandler(QScriptValue callback, AnimVariantMap parameters, QStringList names, bool useNames, AnimVariantResultHandler resultHandler);
147+
145148 signals:
146149 void scriptLoaded(const QString& scriptFilename);
147150 void errorLoadingScript(const QString& scriptFilename);
@@ -181,8 +184,7 @@ private: · 33 unmodified lines
181184
182185 QObject* setupTimerWithInterval(const QScriptValue& function, int intervalMS, bool isSingleShot);
183186 void stopTimer(QTimer* timer);
184−
185− AbstractControllerScriptingInterface* _controllerScriptingInterface;
187+
186188 QString _fileNameString;
187189 Quat _quatLibrary;
188190 Vec3 _vec3Library;
libraries/script-engine/src/Vec3.h+22−0
@@ -23,6 +23,25 @@ · 22 unmodified lines
2323 /// Scriptable interface a Vec3ernion helper class object. Used exclusively in the JavaScript API
2424 class Vec3 : public QObject {
2525 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)
2645
2746 public slots:
2847 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
3049 float dot(const glm::vec3& v1, const glm::vec3& v2) { return glm::dot(v1, v2); }
3150 glm::vec3 multiply(const glm::vec3& v1, float f) { return v1 * f; }
3251 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; }
3353 glm::vec3 multiplyQbyV(const glm::quat& q, const glm::vec3& v) { return q * v; }
3454 glm::vec3 sum(const glm::vec3& v1, const glm::vec3& v2) { return v1 + v2; }
3555 glm::vec3 subtract(const glm::vec3& v1, const glm::vec3& v2) { return v1 - v2; }
@@ -45,6 +65,8 @@ public slots: · 9 unmodified lines
4565 glm::vec3 toPolar(const glm::vec3& v);
4666 glm::vec3 fromPolar(const glm::vec3& polar);
4767 glm::vec3 fromPolar(float elevation, float azimuth);
68+
69+private:
4870 const glm::vec3& UNIT_X() { return Vectors::UNIT_X; }
4971 const glm::vec3& UNIT_Y() { return Vectors::UNIT_Y; }
5072 const glm::vec3& UNIT_Z() { return Vectors::UNIT_Z; }
libraries/shared/src/DependencyManager.h+19−3
@@ -51,7 +51,10 @@ public: · 50 unmodified lines
5151
5252 template<typename T, typename ...Args>
5353 static QSharedPointer<T> set(Args&&... args);
54−
54+
55+ template<typename T, typename I, typename ...Args>
56+ static QSharedPointer<T> set(Args&&... args);
57+
5558 template<typename T>
5659 static void destroy();
5760
@@ -89,13 +92,26 @@ QSharedPointer<T> DependencyManager::get() { · 31 unmodified lines
8992 template <typename T, typename ...Args>
9093 QSharedPointer<T> DependencyManager::set(Args&&... args) {
9194 static size_t hashCode = _manager.getHashCode<T>();
92−
95+
9396 QSharedPointer<Dependency>& instance = _manager.safeGet(hashCode);
9497 instance.clear(); // Clear instance before creation of new one to avoid edge cases
9598 QSharedPointer<T> newInstance(new T(args...), &T::customDeleter);
9699 QSharedPointer<Dependency> storedInstance = qSharedPointerCast<Dependency>(newInstance);
97100 instance.swap(storedInstance);
98−
101+
102+ return newInstance;
103+}
104+
105+template <typename T, typename I, typename ...Args>
106+QSharedPointer<T> DependencyManager::set(Args&&... args) {
107+ static size_t hashCode = _manager.getHashCode<T>();
108+
109+ QSharedPointer<Dependency>& instance = _manager.safeGet(hashCode);
110+ instance.clear(); // Clear instance before creation of new one to avoid edge cases
111+ QSharedPointer<T> newInstance(new I(args...), &I::customDeleter);
112+ QSharedPointer<Dependency> storedInstance = qSharedPointerCast<Dependency>(newInstance);
113+ instance.swap(storedInstance);
114+
99115 return newInstance;
100116 }
101117
libraries/shared/src/SharedUtil.cpp+19−12
@@ -15,6 +15,7 @@ · 14 unmodified lines
1515 #include <cstring>
1616 #include <cctype>
1717 #include <time.h>
18+#include <mutex>
1819
1920 #ifdef _WIN32
2021 #include <windows.h>
@@ -39,30 +40,29 @@ void usecTimestampNowForceClockSkew(int clockSkew) { · 18 unmodified lines
3940 ::usecTimestampNowAdjust = clockSkew;
4041 }
4142
43+static qint64 TIME_REFERENCE = 0; // in usec
44+static std::once_flag usecTimestampNowIsInitialized;
45+static QElapsedTimer timestampTimer;
46+
4247 quint64 usecTimestampNow(bool wantDebug) {
43− static bool usecTimestampNowIsInitialized = false;
44− static qint64 TIME_REFERENCE = 0; // in usec
45− static QElapsedTimer timestampTimer;
46−
47− if (!usecTimestampNowIsInitialized) {
48− TIME_REFERENCE = QDateTime::currentMSecsSinceEpoch() * 1000; // ms to usec
48+ std::call_once(usecTimestampNowIsInitialized, [&] {
49+ TIME_REFERENCE = QDateTime::currentMSecsSinceEpoch() * USECS_PER_MSEC; // ms to usec
4950 timestampTimer.start();
50− usecTimestampNowIsInitialized = true;
51− }
51+ });
5252
5353 quint64 now;
5454 quint64 nsecsElapsed = timestampTimer.nsecsElapsed();
55− quint64 usecsElapsed = nsecsElapsed / 1000; // nsec to usec
55+ quint64 usecsElapsed = nsecsElapsed / NSECS_PER_USEC; // nsec to usec
5656
5757 // QElapsedTimer may not advance if the CPU has gone to sleep. In which case it
5858 // will begin to deviate from real time. We detect that here, and reset if necessary
5959 quint64 msecsCurrentTime = QDateTime::currentMSecsSinceEpoch();
60− quint64 msecsEstimate = (TIME_REFERENCE + usecsElapsed) / 1000; // usecs to msecs
60+ quint64 msecsEstimate = (TIME_REFERENCE + usecsElapsed) / USECS_PER_MSEC; // usecs to msecs
6161 int possibleSkew = msecsEstimate - msecsCurrentTime;
62− const int TOLERANCE = 10000; // up to 10 seconds of skew is tolerated
62+ const int TOLERANCE = 10 * MSECS_PER_SECOND; // up to 10 seconds of skew is tolerated
6363 if (abs(possibleSkew) > TOLERANCE) {
6464 // reset our TIME_REFERENCE and timer
65− TIME_REFERENCE = QDateTime::currentMSecsSinceEpoch() * 1000; // ms to usec
65+ TIME_REFERENCE = QDateTime::currentMSecsSinceEpoch() * USECS_PER_MSEC; // ms to usec
6666 timestampTimer.restart();
6767 now = TIME_REFERENCE + ::usecTimestampNowAdjust;
6868
@@ -118,6 +118,13 @@ quint64 usecTimestampNow(bool wantDebug) { · 49 unmodified lines
118118 return now;
119119 }
120120
121+float secTimestampNow() {
122+ static const auto START_TIME = usecTimestampNow();
123+ const auto nowUsecs = usecTimestampNow() - START_TIME;
124+ const auto nowMsecs = nowUsecs / USECS_PER_MSEC;
125+ return (float)nowMsecs / MSECS_PER_SECOND;
126+}
127+
121128 float randFloat() {
122129 return (rand() % 10000)/10000.0f;
123130 }
libraries/shared/src/SharedUtil.h+5−0
@@ -65,9 +65,14 @@ inline bool operator!=(const xColor& lhs, const xColor& rhs) · 64 unmodified lines
6565 // Use a custom User-Agent to avoid ModSecurity filtering, e.g. by hosting providers.
6666 const QByteArray HIGH_FIDELITY_USER_AGENT = "Mozilla/5.0 (HighFidelityInterface)";
6767
68+// Equivalent to time_t but in usecs instead of secs
6869 quint64 usecTimestampNow(bool wantDebug = false);
6970 void usecTimestampNowForceClockSkew(int clockSkew);
7071
72+// Number of seconds expressed since the first call to this function, expressed as a float
73+// Maximum accuracy in msecs
74+float secTimestampNow();
75+
7176 float randFloat();
7277 int randIntInRange (int min, int max);
7378 float randFloatInRange (float min,float max);
libraries/shared/src/SimpleMovingAverage.h+24−0
@@ -40,4 +40,28 @@ private: · 39 unmodified lines
4040 float ONE_MINUS_WEIGHTING;
4141 };
4242
43+
44+template <class T, int MAX_NUM_SAMPLES> class MovingAverage {
45+public:
46+ const float WEIGHTING = 1.0f / (float)MAX_NUM_SAMPLES;
47+ const float ONE_MINUS_WEIGHTING = 1.0f - WEIGHTING;
48+ int numSamples{ 0 };
49+ T average;
50+
51+ void clear() {
52+ numSamples = 0;
53+ }
54+
55+ bool isAverageValid() const { return (numSamples > 0); }
56+
57+ void addSample(T sample) {
58+ if (numSamples > 0) {
59+ average = (sample * WEIGHTING) + (average * ONE_MINUS_WEIGHTING);
60+ } else {
61+ average = sample;
62+ }
63+ numSamples++;
64+ }
65+};
66+
4367 #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+
17+namespace hifi {
18+
19+ template <typename T, typename Key>
20+ class SimpleFactory {
21+ public:
22+ using Pointer = std::shared_ptr<T>;
23+ using Builder = std::function<Pointer()>;
24+ using BuilderMap = std::map<Key, Builder>;
25+
26+ void registerBuilder(const Key& name, Builder builder) {
27+ // FIXME don't allow name collisions
28+ _builders[name] = builder;
29+ }
30+
31+ Pointer create(const Key& name) const {
32+ const auto& entryIt = _builders.find(name);
33+ if (entryIt != _builders.end()) {
34+ return (*entryIt).second();
35+ }
36+ return Pointer();
37+ }
38+
39+ template <typename Impl>
40+ class Registrar {
41+ public:
42+ Registrar(const Key& name, SimpleFactory& factory) {
43+ factory.registerBuilder(name, [] { return std::make_shared<Impl>(); });
44+ }
45+ };
46+ protected:
47+ BuilderMap _builders;
48+ };
49+}
50+
51+#endif
libraries/ui/src/VrMenu.cpp+4−0
@@ -113,6 +113,7 @@ void VrMenu::addMenu(QMenu* menu) { · 112 unmodified lines
113113 Q_ARG(QVariant, QVariant::fromValue(qmlParent)),
114114 Q_ARG(QVariant, QVariant::fromValue(menu->title())));
115115 Q_ASSERT(invokeResult);
116+ Q_UNUSED(invokeResult); // FIXME - apparently we haven't upgraded the Qt on our unix Jenkins environments to 5.5.x
116117 QObject* result = returnedValue.value<QObject*>();
117118 Q_ASSERT(result);
118119
@@ -153,6 +154,7 @@ void VrMenu::addAction(QMenu* menu, QAction* action) { · 34 unmodified lines
153154 Q_ARG(QVariant, QVariant::fromValue(menuQml)),
154155 Q_ARG(QVariant, QVariant::fromValue(action->text())));
155156 Q_ASSERT(invokeResult);
157+ Q_UNUSED(invokeResult); // FIXME - apparently we haven't upgraded the Qt on our unix Jenkins environments to 5.5.x
156158 QObject* result = returnedValue.value<QObject*>();
157159 Q_ASSERT(result);
158160 // Bind the QML and Widget together
@@ -174,6 +176,7 @@ void VrMenu::insertAction(QAction* before, QAction* action) { · 15 unmodified lines
174176 Q_ARG(QVariant, QVariant::fromValue(beforeQml)),
175177 Q_ARG(QVariant, QVariant::fromValue(action->text())));
176178 Q_ASSERT(invokeResult);
179+ Q_UNUSED(invokeResult); // FIXME - apparently we haven't upgraded the Qt on our unix Jenkins environments to 5.5.x
177180 QObject* result = returnedValue.value<QObject*>();
178181 Q_ASSERT(result);
179182 bindActionToQmlAction(result, action);
@@ -192,4 +195,5 @@ void VrMenu::removeAction(QAction* action) { · 12 unmodified lines
192195 Q_ARG(QVariant, QVariant::fromValue(menu)),
193196 Q_ARG(QVariant, QVariant::fromValue(item)));
194197 Q_ASSERT(invokeResult);
198+ Q_UNUSED(invokeResult); // FIXME - apparently we haven't upgraded the Qt on our unix Jenkins environments to 5.5.x
195199 }
tests/controllers/CMakeLists.txt+19−0
@@ -0,0 +1,19 @@
1+
2+set(TARGET_NAME controllers-test)
3+
4+# This is not a testcase -- just set it up as a regular hifi project
5+setup_hifi_project(Script Qml)
6+set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/")
7+
8+# link in the shared libraries
9+link_hifi_libraries(shared gl script-engine plugins render-utils input-plugins display-plugins controllers)
10+
11+
12+if (WIN32)
13+ add_dependency_external_projects(OpenVR)
14+ find_package(OpenVR REQUIRED)
15+ target_include_directories(${TARGET_NAME} PRIVATE ${OPENVR_INCLUDE_DIRS})
16+ target_link_libraries(${TARGET_NAME} ${OPENVR_LIBRARIES})
17+endif()
18+
19+copy_dlls_beside_windows_executable()
tests/controllers/qml/main.qml+22−0
@@ -0,0 +1,22 @@
1+import QtQuick 2.1
2+import QtQuick.Controls 1.0
3+import QtQuick.Layouts 1.0
4+import QtQuick.Dialogs 1.0
5+
6+ApplicationWindow {
7+ id: window
8+ visible: true
9+
10+ Timer {
11+ interval: 50; running: true; repeat: true
12+ onTriggered: {
13+ Controller.update();
14+ }
15+ }
16+
17+
18+ Loader {
19+ id: pageLoader
20+ source: ResourcePath + "TestControllers.qml"
21+ }
22+}
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+
46+const QString& getResourcesDir() {
47+ static QString dir;
48+ if (dir.isEmpty()) {
49+ QDir path(__FILE__);
50+ path.cdUp();
51+ dir = path.cleanPath(path.absoluteFilePath("../../../interface/resources/")) + "/";
52+ qDebug() << "Resources Path: " << dir;
53+ }
54+ return dir;
55+}
56+
57+const QString& getQmlDir() {
58+ static QString dir;
59+ if (dir.isEmpty()) {
60+ dir = getResourcesDir() + "qml/";
61+ qDebug() << "Qml Path: " << dir;
62+ }
63+ return dir;
64+}
65+
66+const QString& getTestQmlDir() {
67+ static QString dir;
68+ if (dir.isEmpty()) {
69+ QDir path(__FILE__);
70+ path.cdUp();
71+ dir = path.cleanPath(path.absoluteFilePath("../")) + "/qml/";
72+ qDebug() << "Qml Test Path: " << dir;
73+ }
74+ return dir;
75+}
76+
77+using namespace controller;
78+
79+
80+class PluginContainerProxy : public QObject, PluginContainer {
81+ Q_OBJECT
82+public:
83+ PluginContainerProxy() {
84+ Plugin::setContainer(this);
85+ }
86+ virtual ~PluginContainerProxy() {}
87+ virtual void addMenu(const QString& menuName) override {}
88+ virtual void removeMenu(const QString& menuName) override {}
89+ virtual QAction* addMenuItem(const QString& path, const QString& name, std::function<void(bool)> onClicked, bool checkable = false, bool checked = false, const QString& groupName = "") override { return nullptr; }
90+ virtual void removeMenuItem(const QString& menuName, const QString& menuItem) override {}
91+ virtual bool isOptionChecked(const QString& name) override { return false; }
92+ virtual void setIsOptionChecked(const QString& path, bool checked) override {}
93+ virtual void setFullscreen(const QScreen* targetScreen, bool hideMenu = true) override {}
94+ virtual void unsetFullscreen(const QScreen* avoidScreen = nullptr) override {}
95+ virtual void showDisplayPluginsTools() override {}
96+ virtual void requestReset() override {}
97+ virtual QGLWidget* getPrimarySurface() override { return nullptr; }
98+ virtual bool isForeground() override { return true; }
99+ virtual const DisplayPlugin* getActiveDisplayPlugin() const override { return nullptr; }
100+};
101+
102+class MyControllerScriptingInterface : public controller::ScriptingInterface {
103+public:
104+ virtual void registerControllerTypes(QScriptEngine* engine) {};
105+};
106+
107+
108+int main(int argc, char** argv) {
109+ QGuiApplication app(argc, argv);
110+ QQmlApplicationEngine engine;
111+ auto rootContext = engine.rootContext();
112+ new PluginContainerProxy();
113+
114+ // Simulate our application idle loop
115+ QTimer timer;
116+ QObject::connect(&timer, &QTimer::timeout, [] {
117+ static float last = secTimestampNow();
118+ float now = secTimestampNow();
119+ float delta = now - last;
120+ last = now;
121+
122+ foreach(auto inputPlugin, PluginManager::getInstance()->getInputPlugins()) {
123+ inputPlugin->pluginUpdate(delta, false);
124+ }
125+
126+ auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
127+ userInputMapper->update(delta);
128+ });
129+ timer.start(50);
130+
131+ {
132+ DependencyManager::set<controller::UserInputMapper>();
133+ foreach(auto inputPlugin, PluginManager::getInstance()->getInputPlugins()) {
134+ QString name = inputPlugin->getName();
135+ inputPlugin->activate();
136+ auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
137+ if (name == KeyboardMouseDevice::NAME) {
138+ auto keyboardMouseDevice = static_cast<KeyboardMouseDevice*>(inputPlugin.data()); // TODO: this seems super hacky
139+ userInputMapper->registerDevice(std::shared_ptr<InputDevice>(keyboardMouseDevice));
140+ }
141+ inputPlugin->pluginUpdate(0, false);
142+ }
143+ rootContext->setContextProperty("Controllers", new MyControllerScriptingInterface());
144+ }
145+ qDebug() << getQmlDir();
146+ rootContext->setContextProperty("ResourcePath", getQmlDir());
147+ engine.setBaseUrl(QUrl::fromLocalFile(getQmlDir()));
148+ engine.addImportPath(getQmlDir());
149+ engine.load(getTestQmlDir() + "main.qml");
150+ for (auto pathItem : engine.importPathList()) {
151+ qDebug() << pathItem;
152+ }
153+ app.exec();
154+ return 0;
155+}
156+
157+#include "main.moc"
158+