@needle-tools/engine 4.4.0-beta → 4.4.0-ci.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1046) hide show
  1. package/CHANGELOG.md +3604 -3604
  2. package/LICENSE.md +10 -10
  3. package/README.md +64 -64
  4. package/components.needle.json +1 -1
  5. package/dist/needle-engine.bundle.js +8 -8
  6. package/dist/needle-engine.bundle.light.js +8 -8
  7. package/dist/needle-engine.bundle.light.min.js +2 -2
  8. package/dist/needle-engine.bundle.light.umd.cjs +2 -2
  9. package/dist/needle-engine.bundle.min.js +2 -2
  10. package/dist/needle-engine.bundle.umd.cjs +2 -2
  11. package/dist/needle-engine.d.ts +130 -138
  12. package/dist/needle-engine.light.d.ts +130 -138
  13. package/dist/postprocessing.js +1673 -1527
  14. package/dist/postprocessing.light.js +1673 -1527
  15. package/dist/postprocessing.light.min.js +80 -66
  16. package/dist/postprocessing.light.umd.cjs +83 -69
  17. package/dist/postprocessing.min.js +80 -66
  18. package/dist/postprocessing.umd.cjs +83 -69
  19. package/dist/vendor.js +1 -1
  20. package/dist/vendor.light.js +1 -1
  21. package/lib/asap/needle-asap.d.ts +1 -1
  22. package/lib/asap/needle-asap.js +95 -95
  23. package/lib/asap/sessiongranted.d.ts +3 -3
  24. package/lib/asap/sessiongranted.js +65 -65
  25. package/lib/asap/utils.d.ts +1 -1
  26. package/lib/asap/utils.js +3 -3
  27. package/lib/engine/analytics/index.d.ts +6 -6
  28. package/lib/engine/analytics/index.js +12 -12
  29. package/lib/engine/analytics/lcp.d.ts +3 -3
  30. package/lib/engine/analytics/lcp.js +34 -34
  31. package/lib/engine/api.d.ts +81 -81
  32. package/lib/engine/api.js +80 -80
  33. package/lib/engine/assets/index.d.ts +11 -11
  34. package/lib/engine/assets/index.js +47 -47
  35. package/lib/engine/assets/static.d.ts +1 -1
  36. package/lib/engine/assets/static.js +4 -4
  37. package/lib/engine/codegen/register_types.d.ts +1 -1
  38. package/lib/engine/codegen/register_types.js +298 -298
  39. package/lib/engine/debug/debug.d.ts +15 -15
  40. package/lib/engine/debug/debug.js +44 -44
  41. package/lib/engine/debug/debug_console.d.ts +2 -2
  42. package/lib/engine/debug/debug_console.js +307 -307
  43. package/lib/engine/debug/debug_overlay.d.ts +22 -22
  44. package/lib/engine/debug/debug_overlay.js +316 -316
  45. package/lib/engine/debug/debug_spatial_console.d.ts +2 -2
  46. package/lib/engine/debug/debug_spatial_console.js +390 -390
  47. package/lib/engine/debug/index.d.ts +2 -2
  48. package/lib/engine/debug/index.js +2 -2
  49. package/lib/engine/engine_addressables.d.ts +166 -166
  50. package/lib/engine/engine_addressables.js +608 -608
  51. package/lib/engine/engine_animation.d.ts +43 -43
  52. package/lib/engine/engine_animation.js +133 -133
  53. package/lib/engine/engine_application.d.ts +40 -40
  54. package/lib/engine/engine_application.js +104 -104
  55. package/lib/engine/engine_assetdatabase.d.ts +25 -25
  56. package/lib/engine/engine_assetdatabase.js +344 -344
  57. package/lib/engine/engine_audio.d.ts +4 -4
  58. package/lib/engine/engine_audio.js +23 -23
  59. package/lib/engine/engine_camera.d.ts +13 -13
  60. package/lib/engine/engine_camera.js +30 -30
  61. package/lib/engine/engine_components.d.ts +110 -110
  62. package/lib/engine/engine_components.js +380 -380
  63. package/lib/engine/engine_components_internal.d.ts +9 -9
  64. package/lib/engine/engine_components_internal.js +36 -36
  65. package/lib/engine/engine_constants.d.ts +10 -10
  66. package/lib/engine/engine_constants.js +41 -41
  67. package/lib/engine/engine_context.d.ts +345 -345
  68. package/lib/engine/engine_context.js +1515 -1515
  69. package/lib/engine/engine_context_registry.d.ts +71 -71
  70. package/lib/engine/engine_context_registry.js +117 -117
  71. package/lib/engine/engine_coroutine.d.ts +35 -35
  72. package/lib/engine/engine_coroutine.js +52 -52
  73. package/lib/engine/engine_create_objects.d.ts +118 -118
  74. package/lib/engine/engine_create_objects.js +308 -308
  75. package/lib/engine/engine_default_parameters.d.ts +2 -2
  76. package/lib/engine/engine_default_parameters.js +3 -3
  77. package/lib/engine/engine_editor-sync.d.ts +21 -21
  78. package/lib/engine/engine_editor-sync.js +4 -4
  79. package/lib/engine/engine_element.d.ts +113 -113
  80. package/lib/engine/engine_element.js +830 -830
  81. package/lib/engine/engine_element_attributes.d.ts +72 -72
  82. package/lib/engine/engine_element_attributes.js +1 -1
  83. package/lib/engine/engine_element_extras.d.ts +6 -6
  84. package/lib/engine/engine_element_extras.js +13 -13
  85. package/lib/engine/engine_element_loading.d.ts +44 -44
  86. package/lib/engine/engine_element_loading.js +349 -349
  87. package/lib/engine/engine_element_overlay.d.ts +21 -21
  88. package/lib/engine/engine_element_overlay.js +166 -166
  89. package/lib/engine/engine_fileloader.d.ts +2 -2
  90. package/lib/engine/engine_fileloader.js +8 -8
  91. package/lib/engine/engine_gameobject.d.ts +68 -68
  92. package/lib/engine/engine_gameobject.js +591 -591
  93. package/lib/engine/engine_generic_utils.d.ts +1 -1
  94. package/lib/engine/engine_generic_utils.js +13 -13
  95. package/lib/engine/engine_gizmos.d.ts +149 -149
  96. package/lib/engine/engine_gizmos.js +530 -530
  97. package/lib/engine/engine_gltf.d.ts +12 -12
  98. package/lib/engine/engine_gltf.js +15 -15
  99. package/lib/engine/engine_gltf_builtin_components.d.ts +7 -7
  100. package/lib/engine/engine_gltf_builtin_components.js +341 -341
  101. package/lib/engine/engine_hot_reload.d.ts +7 -7
  102. package/lib/engine/engine_hot_reload.js +184 -184
  103. package/lib/engine/engine_input.d.ts +352 -352
  104. package/lib/engine/engine_input.js +1265 -1265
  105. package/lib/engine/engine_input_utils.d.ts +2 -2
  106. package/lib/engine/engine_input_utils.js +22 -22
  107. package/lib/engine/engine_instancing.d.ts +19 -19
  108. package/lib/engine/engine_instancing.js +39 -39
  109. package/lib/engine/engine_license.d.ts +9 -9
  110. package/lib/engine/engine_license.js +320 -320
  111. package/lib/engine/engine_lifecycle_api.d.ts +76 -76
  112. package/lib/engine/engine_lifecycle_api.js +99 -99
  113. package/lib/engine/engine_lifecycle_functions_internal.d.ts +32 -32
  114. package/lib/engine/engine_lifecycle_functions_internal.js +146 -146
  115. package/lib/engine/engine_lightdata.d.ts +23 -23
  116. package/lib/engine/engine_lightdata.js +91 -91
  117. package/lib/engine/engine_loaders.d.ts +13 -13
  118. package/lib/engine/engine_loaders.js +62 -62
  119. package/lib/engine/engine_lods.d.ts +31 -31
  120. package/lib/engine/engine_lods.js +146 -146
  121. package/lib/engine/engine_mainloop_utils.d.ts +32 -32
  122. package/lib/engine/engine_mainloop_utils.js +466 -466
  123. package/lib/engine/engine_math.d.ts +114 -114
  124. package/lib/engine/engine_math.js +247 -247
  125. package/lib/engine/engine_modules.d.ts +36 -36
  126. package/lib/engine/engine_modules.js +85 -85
  127. package/lib/engine/engine_networking.d.ts +252 -252
  128. package/lib/engine/engine_networking.js +743 -743
  129. package/lib/engine/engine_networking_auto.d.ts +24 -24
  130. package/lib/engine/engine_networking_auto.js +310 -310
  131. package/lib/engine/engine_networking_blob.d.ts +48 -48
  132. package/lib/engine/engine_networking_blob.js +212 -212
  133. package/lib/engine/engine_networking_files.d.ts +35 -35
  134. package/lib/engine/engine_networking_files.js +172 -172
  135. package/lib/engine/engine_networking_files_default_components.d.ts +6 -6
  136. package/lib/engine/engine_networking_files_default_components.js +42 -42
  137. package/lib/engine/engine_networking_instantiate.d.ts +100 -100
  138. package/lib/engine/engine_networking_instantiate.js +345 -345
  139. package/lib/engine/engine_networking_peer.d.ts +15 -15
  140. package/lib/engine/engine_networking_peer.js +132 -132
  141. package/lib/engine/engine_networking_streams.d.ts +123 -123
  142. package/lib/engine/engine_networking_streams.js +645 -645
  143. package/lib/engine/engine_networking_types.d.ts +22 -22
  144. package/lib/engine/engine_networking_types.js +7 -7
  145. package/lib/engine/engine_networking_utils.d.ts +2 -2
  146. package/lib/engine/engine_networking_utils.js +20 -20
  147. package/lib/engine/engine_networking_websocket.d.ts +1 -1
  148. package/lib/engine/engine_networking_websocket.js +2 -2
  149. package/lib/engine/engine_patcher.d.ts +10 -10
  150. package/lib/engine/engine_patcher.js +142 -142
  151. package/lib/engine/engine_physics.d.ts +152 -152
  152. package/lib/engine/engine_physics.js +645 -645
  153. package/lib/engine/engine_physics.types.d.ts +40 -40
  154. package/lib/engine/engine_physics.types.js +33 -33
  155. package/lib/engine/engine_physics_rapier.d.ts +147 -147
  156. package/lib/engine/engine_physics_rapier.js +1432 -1432
  157. package/lib/engine/engine_playerview.d.ts +26 -26
  158. package/lib/engine/engine_playerview.js +64 -64
  159. package/lib/engine/engine_scenelighting.d.ts +71 -71
  160. package/lib/engine/engine_scenelighting.js +226 -226
  161. package/lib/engine/engine_scenetools.d.ts +50 -50
  162. package/lib/engine/engine_scenetools.js +321 -321
  163. package/lib/engine/engine_serialization.d.ts +3 -3
  164. package/lib/engine/engine_serialization.js +3 -3
  165. package/lib/engine/engine_serialization_builtin_serializer.d.ts +72 -72
  166. package/lib/engine/engine_serialization_builtin_serializer.js +403 -403
  167. package/lib/engine/engine_serialization_core.d.ts +85 -85
  168. package/lib/engine/engine_serialization_core.js +602 -602
  169. package/lib/engine/engine_serialization_decorator.d.ts +23 -23
  170. package/lib/engine/engine_serialization_decorator.js +66 -66
  171. package/lib/engine/engine_setup.d.ts +1 -1
  172. package/lib/engine/engine_setup.js +2 -2
  173. package/lib/engine/engine_shaders.d.ts +53 -53
  174. package/lib/engine/engine_shaders.js +252 -252
  175. package/lib/engine/engine_shims.d.ts +4 -4
  176. package/lib/engine/engine_shims.js +24 -24
  177. package/lib/engine/engine_test_utils.d.ts +39 -39
  178. package/lib/engine/engine_test_utils.js +83 -83
  179. package/lib/engine/engine_texture.d.ts +28 -28
  180. package/lib/engine/engine_texture.js +64 -64
  181. package/lib/engine/engine_three_utils.d.ts +201 -201
  182. package/lib/engine/engine_three_utils.js +731 -731
  183. package/lib/engine/engine_time.d.ts +51 -51
  184. package/lib/engine/engine_time.js +82 -82
  185. package/lib/engine/engine_time_utils.d.ts +88 -88
  186. package/lib/engine/engine_time_utils.js +215 -215
  187. package/lib/engine/engine_tonemapping.d.ts +2 -2
  188. package/lib/engine/engine_tonemapping.js +194 -194
  189. package/lib/engine/engine_types.d.ts +572 -572
  190. package/lib/engine/engine_types.js +88 -88
  191. package/lib/engine/engine_typestore.d.ts +28 -28
  192. package/lib/engine/engine_typestore.js +55 -55
  193. package/lib/engine/engine_util_decorator.d.ts +13 -13
  194. package/lib/engine/engine_util_decorator.js +116 -116
  195. package/lib/engine/engine_utils.d.ts +266 -266
  196. package/lib/engine/engine_utils.js +878 -878
  197. package/lib/engine/engine_utils_format.d.ts +21 -21
  198. package/lib/engine/engine_utils_format.js +193 -193
  199. package/lib/engine/engine_utils_screenshot.d.ts +159 -159
  200. package/lib/engine/engine_utils_screenshot.js +513 -513
  201. package/lib/engine/engine_utils_screenshot.xr.d.ts +5 -5
  202. package/lib/engine/engine_utils_screenshot.xr.js +90 -90
  203. package/lib/engine/engine_xr.d.ts +1 -1
  204. package/lib/engine/engine_xr.js +1 -1
  205. package/lib/engine/export/gltf/Writers.d.ts +19 -19
  206. package/lib/engine/export/gltf/Writers.js +24 -24
  207. package/lib/engine/export/gltf/index.d.ts +11 -11
  208. package/lib/engine/export/gltf/index.js +123 -123
  209. package/lib/engine/export/index.d.ts +2 -2
  210. package/lib/engine/export/index.js +2 -2
  211. package/lib/engine/export/state.d.ts +7 -7
  212. package/lib/engine/export/state.js +17 -17
  213. package/lib/engine/export/utils.d.ts +2 -2
  214. package/lib/engine/export/utils.js +7 -7
  215. package/lib/engine/extensions/EXT_texture_exr.d.ts +8 -8
  216. package/lib/engine/extensions/EXT_texture_exr.js +32 -32
  217. package/lib/engine/extensions/NEEDLE_animator_controller_model.d.ts +122 -122
  218. package/lib/engine/extensions/NEEDLE_animator_controller_model.js +95 -95
  219. package/lib/engine/extensions/NEEDLE_components.d.ts +35 -35
  220. package/lib/engine/extensions/NEEDLE_components.js +220 -220
  221. package/lib/engine/extensions/NEEDLE_gameobject_data.d.ts +10 -10
  222. package/lib/engine/extensions/NEEDLE_gameobject_data.js +57 -57
  223. package/lib/engine/extensions/NEEDLE_lighting_settings.d.ts +37 -37
  224. package/lib/engine/extensions/NEEDLE_lighting_settings.js +157 -157
  225. package/lib/engine/extensions/NEEDLE_lightmaps.d.ts +18 -18
  226. package/lib/engine/extensions/NEEDLE_lightmaps.js +99 -99
  227. package/lib/engine/extensions/NEEDLE_persistent_assets.d.ts +11 -11
  228. package/lib/engine/extensions/NEEDLE_persistent_assets.js +63 -63
  229. package/lib/engine/extensions/NEEDLE_progressive.d.ts +1 -1
  230. package/lib/engine/extensions/NEEDLE_progressive.js +1 -1
  231. package/lib/engine/extensions/NEEDLE_render_objects.d.ts +13 -13
  232. package/lib/engine/extensions/NEEDLE_render_objects.js +159 -159
  233. package/lib/engine/extensions/NEEDLE_techniques_webgl.d.ts +38 -38
  234. package/lib/engine/extensions/NEEDLE_techniques_webgl.js +564 -564
  235. package/lib/engine/extensions/extension_resolver.d.ts +4 -4
  236. package/lib/engine/extensions/extension_resolver.js +1 -1
  237. package/lib/engine/extensions/extension_utils.d.ts +12 -12
  238. package/lib/engine/extensions/extension_utils.js +152 -152
  239. package/lib/engine/extensions/extensions.d.ts +31 -31
  240. package/lib/engine/extensions/extensions.js +103 -103
  241. package/lib/engine/extensions/index.d.ts +6 -6
  242. package/lib/engine/extensions/index.js +6 -6
  243. package/lib/engine/extensions/usage_tracker.d.ts +13 -13
  244. package/lib/engine/extensions/usage_tracker.js +65 -65
  245. package/lib/engine/js-extensions/Camera.d.ts +1 -1
  246. package/lib/engine/js-extensions/Camera.js +39 -39
  247. package/lib/engine/js-extensions/ExtensionUtils.d.ts +9 -9
  248. package/lib/engine/js-extensions/ExtensionUtils.js +67 -67
  249. package/lib/engine/js-extensions/Layers.d.ts +6 -6
  250. package/lib/engine/js-extensions/Layers.js +22 -22
  251. package/lib/engine/js-extensions/Object3D.d.ts +120 -120
  252. package/lib/engine/js-extensions/Object3D.js +136 -136
  253. package/lib/engine/js-extensions/RGBAColor.d.ts +22 -22
  254. package/lib/engine/js-extensions/RGBAColor.js +62 -62
  255. package/lib/engine/js-extensions/Vector.d.ts +3 -3
  256. package/lib/engine/js-extensions/Vector.js +13 -13
  257. package/lib/engine/js-extensions/index.d.ts +5 -5
  258. package/lib/engine/js-extensions/index.js +5 -5
  259. package/lib/engine/physics/workers/mesh-bvh/GenerateMeshBVHWorker.d.ts +4 -4
  260. package/lib/engine/physics/workers/mesh-bvh/GenerateMeshBVHWorker.js +75 -75
  261. package/lib/engine/shaders/shaderData.d.ts +55 -55
  262. package/lib/engine/shaders/shaderData.js +58 -58
  263. package/lib/engine/tests/test_utils.d.ts +2 -2
  264. package/lib/engine/tests/test_utils.js +53 -53
  265. package/lib/engine/webcomponents/WebXRButtons.d.ts +56 -56
  266. package/lib/engine/webcomponents/WebXRButtons.js +230 -230
  267. package/lib/engine/webcomponents/api.d.ts +5 -5
  268. package/lib/engine/webcomponents/api.js +4 -4
  269. package/lib/engine/webcomponents/buttons.d.ts +39 -39
  270. package/lib/engine/webcomponents/buttons.js +237 -237
  271. package/lib/engine/webcomponents/fonts.d.ts +9 -9
  272. package/lib/engine/webcomponents/fonts.js +32 -32
  273. package/lib/engine/webcomponents/icons.d.ts +9 -9
  274. package/lib/engine/webcomponents/icons.js +52 -52
  275. package/lib/engine/webcomponents/index.d.ts +1 -1
  276. package/lib/engine/webcomponents/index.js +1 -1
  277. package/lib/engine/webcomponents/logo-element.d.ts +10 -10
  278. package/lib/engine/webcomponents/logo-element.js +67 -67
  279. package/lib/engine/webcomponents/needle menu/needle-menu-spatial.d.ts +37 -37
  280. package/lib/engine/webcomponents/needle menu/needle-menu-spatial.js +513 -513
  281. package/lib/engine/webcomponents/needle menu/needle-menu.d.ts +151 -151
  282. package/lib/engine/webcomponents/needle menu/needle-menu.js +1018 -1018
  283. package/lib/engine/webcomponents/needle-button.d.ts +34 -34
  284. package/lib/engine/webcomponents/needle-button.js +161 -161
  285. package/lib/engine/xr/NeedleXRController.d.ts +313 -313
  286. package/lib/engine/xr/NeedleXRController.js +1008 -1008
  287. package/lib/engine/xr/NeedleXRSession.d.ts +339 -339
  288. package/lib/engine/xr/NeedleXRSession.js +1427 -1427
  289. package/lib/engine/xr/NeedleXRSync.d.ts +22 -22
  290. package/lib/engine/xr/NeedleXRSync.js +188 -188
  291. package/lib/engine/xr/SceneTransition.d.ts +18 -18
  292. package/lib/engine/xr/SceneTransition.js +69 -69
  293. package/lib/engine/xr/TempXRContext.d.ts +34 -34
  294. package/lib/engine/xr/TempXRContext.js +187 -187
  295. package/lib/engine/xr/XRRig.d.ts +7 -7
  296. package/lib/engine/xr/XRRig.js +1 -1
  297. package/lib/engine/xr/api.d.ts +6 -6
  298. package/lib/engine/xr/api.js +6 -6
  299. package/lib/engine/xr/events.d.ts +66 -66
  300. package/lib/engine/xr/events.js +93 -93
  301. package/lib/engine/xr/internal.d.ts +12 -12
  302. package/lib/engine/xr/internal.js +25 -25
  303. package/lib/engine/xr/usdz.d.ts +12 -12
  304. package/lib/engine/xr/usdz.js +29 -29
  305. package/lib/engine/xr/utils.d.ts +11 -11
  306. package/lib/engine/xr/utils.js +34 -34
  307. package/lib/engine-components/AlignmentConstraint.d.ts +10 -10
  308. package/lib/engine-components/AlignmentConstraint.js +39 -39
  309. package/lib/engine-components/Animation.d.ts +156 -156
  310. package/lib/engine-components/Animation.js +508 -508
  311. package/lib/engine-components/AnimationCurve.d.ts +40 -40
  312. package/lib/engine-components/AnimationCurve.js +159 -159
  313. package/lib/engine-components/AnimationUtils.d.ts +8 -8
  314. package/lib/engine-components/AnimationUtils.js +27 -27
  315. package/lib/engine-components/AnimationUtilsAutoplay.d.ts +1 -1
  316. package/lib/engine-components/AnimationUtilsAutoplay.js +39 -39
  317. package/lib/engine-components/Animator.d.ts +217 -217
  318. package/lib/engine-components/Animator.js +354 -354
  319. package/lib/engine-components/AnimatorController.d.ts +227 -227
  320. package/lib/engine-components/AnimatorController.js +1152 -1152
  321. package/lib/engine-components/AudioListener.d.ts +33 -33
  322. package/lib/engine-components/AudioListener.js +86 -86
  323. package/lib/engine-components/AudioSource.d.ts +217 -217
  324. package/lib/engine-components/AudioSource.js +627 -627
  325. package/lib/engine-components/AvatarLoader.d.ts +80 -80
  326. package/lib/engine-components/AvatarLoader.js +231 -231
  327. package/lib/engine-components/AxesHelper.d.ts +32 -32
  328. package/lib/engine-components/AxesHelper.js +67 -67
  329. package/lib/engine-components/BasicIKConstraint.d.ts +9 -9
  330. package/lib/engine-components/BasicIKConstraint.js +43 -43
  331. package/lib/engine-components/BoxCollider.d.ts +2 -2
  332. package/lib/engine-components/BoxCollider.js +2 -2
  333. package/lib/engine-components/BoxHelperComponent.d.ts +47 -47
  334. package/lib/engine-components/BoxHelperComponent.js +102 -102
  335. package/lib/engine-components/Camera.d.ts +231 -231
  336. package/lib/engine-components/Camera.js +694 -694
  337. package/lib/engine-components/CameraUtils.d.ts +1 -1
  338. package/lib/engine-components/CameraUtils.js +127 -127
  339. package/lib/engine-components/CharacterController.d.ts +55 -55
  340. package/lib/engine-components/CharacterController.js +236 -236
  341. package/lib/engine-components/Collider.d.ts +188 -188
  342. package/lib/engine-components/Collider.js +369 -369
  343. package/lib/engine-components/Component.d.ts +792 -792
  344. package/lib/engine-components/Component.js +915 -915
  345. package/lib/engine-components/ContactShadows.d.ts +82 -82
  346. package/lib/engine-components/ContactShadows.js +430 -430
  347. package/lib/engine-components/DeleteBox.d.ts +19 -19
  348. package/lib/engine-components/DeleteBox.js +58 -58
  349. package/lib/engine-components/DeviceFlag.d.ts +16 -16
  350. package/lib/engine-components/DeviceFlag.js +47 -47
  351. package/lib/engine-components/DragControls.d.ts +170 -170
  352. package/lib/engine-components/DragControls.js +1421 -1421
  353. package/lib/engine-components/DropListener.d.ts +215 -215
  354. package/lib/engine-components/DropListener.js +630 -630
  355. package/lib/engine-components/Duplicatable.d.ts +35 -35
  356. package/lib/engine-components/Duplicatable.js +202 -202
  357. package/lib/engine-components/EventList.d.ts +54 -54
  358. package/lib/engine-components/EventList.js +232 -232
  359. package/lib/engine-components/EventTrigger.d.ts +33 -33
  360. package/lib/engine-components/EventTrigger.js +75 -75
  361. package/lib/engine-components/EventType.d.ts +22 -22
  362. package/lib/engine-components/EventType.js +23 -23
  363. package/lib/engine-components/Fog.d.ts +22 -22
  364. package/lib/engine-components/Fog.js +61 -61
  365. package/lib/engine-components/Gizmos.d.ts +17 -17
  366. package/lib/engine-components/Gizmos.js +64 -64
  367. package/lib/engine-components/GridHelper.d.ts +20 -20
  368. package/lib/engine-components/GridHelper.js +54 -54
  369. package/lib/engine-components/GroundProjection.d.ts +67 -67
  370. package/lib/engine-components/GroundProjection.js +343 -343
  371. package/lib/engine-components/Interactable.d.ts +12 -12
  372. package/lib/engine-components/Interactable.js +12 -12
  373. package/lib/engine-components/Joints.d.ts +19 -19
  374. package/lib/engine-components/Joints.js +51 -51
  375. package/lib/engine-components/LODGroup.d.ts +35 -35
  376. package/lib/engine-components/LODGroup.js +152 -152
  377. package/lib/engine-components/Light.d.ts +180 -180
  378. package/lib/engine-components/Light.js +535 -535
  379. package/lib/engine-components/LookAtConstraint.d.ts +19 -19
  380. package/lib/engine-components/LookAtConstraint.js +35 -35
  381. package/lib/engine-components/NeedleMenu.d.ts +50 -50
  382. package/lib/engine-components/NeedleMenu.js +92 -92
  383. package/lib/engine-components/NestedGltf.d.ts +25 -25
  384. package/lib/engine-components/NestedGltf.js +88 -88
  385. package/lib/engine-components/Networking.d.ts +54 -54
  386. package/lib/engine-components/Networking.js +112 -112
  387. package/lib/engine-components/OffsetConstraint.d.ts +14 -14
  388. package/lib/engine-components/OffsetConstraint.js +65 -65
  389. package/lib/engine-components/OrbitControls.d.ts +266 -266
  390. package/lib/engine-components/OrbitControls.js +1009 -1009
  391. package/lib/engine-components/PlayerColor.d.ts +19 -19
  392. package/lib/engine-components/PlayerColor.js +94 -94
  393. package/lib/engine-components/ReflectionProbe.d.ts +26 -26
  394. package/lib/engine-components/ReflectionProbe.js +194 -194
  395. package/lib/engine-components/Renderer.d.ts +153 -153
  396. package/lib/engine-components/Renderer.js +830 -830
  397. package/lib/engine-components/RendererInstancing.d.ts +140 -140
  398. package/lib/engine-components/RendererInstancing.js +744 -744
  399. package/lib/engine-components/RendererLightmap.d.ts +24 -24
  400. package/lib/engine-components/RendererLightmap.js +182 -182
  401. package/lib/engine-components/RigidBody.d.ts +155 -155
  402. package/lib/engine-components/RigidBody.js +517 -517
  403. package/lib/engine-components/SceneSwitcher.d.ts +263 -263
  404. package/lib/engine-components/SceneSwitcher.js +951 -951
  405. package/lib/engine-components/ScreenCapture.d.ts +144 -144
  406. package/lib/engine-components/ScreenCapture.js +547 -547
  407. package/lib/engine-components/ShadowCatcher.d.ts +33 -33
  408. package/lib/engine-components/ShadowCatcher.js +166 -166
  409. package/lib/engine-components/Skybox.d.ts +82 -82
  410. package/lib/engine-components/Skybox.js +448 -448
  411. package/lib/engine-components/SmoothFollow.d.ts +34 -34
  412. package/lib/engine-components/SmoothFollow.js +82 -82
  413. package/lib/engine-components/SpatialTrigger.d.ts +102 -102
  414. package/lib/engine-components/SpatialTrigger.js +225 -225
  415. package/lib/engine-components/SpectatorCamera.d.ts +111 -111
  416. package/lib/engine-components/SpectatorCamera.js +715 -715
  417. package/lib/engine-components/SphereCollider.d.ts +2 -2
  418. package/lib/engine-components/SphereCollider.js +2 -2
  419. package/lib/engine-components/SpriteRenderer.d.ts +132 -132
  420. package/lib/engine-components/SpriteRenderer.js +472 -472
  421. package/lib/engine-components/SyncedCamera.d.ts +41 -41
  422. package/lib/engine-components/SyncedCamera.js +199 -199
  423. package/lib/engine-components/SyncedRoom.d.ts +106 -106
  424. package/lib/engine-components/SyncedRoom.js +371 -371
  425. package/lib/engine-components/SyncedTransform.d.ts +88 -88
  426. package/lib/engine-components/SyncedTransform.js +326 -326
  427. package/lib/engine-components/TestRunner.d.ts +16 -16
  428. package/lib/engine-components/TestRunner.js +102 -102
  429. package/lib/engine-components/TransformGizmo.d.ts +75 -75
  430. package/lib/engine-components/TransformGizmo.js +209 -209
  431. package/lib/engine-components/VideoPlayer.d.ts +184 -184
  432. package/lib/engine-components/VideoPlayer.js +978 -978
  433. package/lib/engine-components/Voip.d.ts +67 -67
  434. package/lib/engine-components/Voip.js +360 -360
  435. package/lib/engine-components/api.d.ts +51 -51
  436. package/lib/engine-components/api.js +50 -50
  437. package/lib/engine-components/avatar/AvatarBlink_Simple.d.ts +11 -11
  438. package/lib/engine-components/avatar/AvatarBlink_Simple.js +76 -76
  439. package/lib/engine-components/avatar/AvatarEyeLook_Rotation.d.ts +14 -14
  440. package/lib/engine-components/avatar/AvatarEyeLook_Rotation.js +68 -68
  441. package/lib/engine-components/avatar/Avatar_Brain_LookAt.d.ts +29 -29
  442. package/lib/engine-components/avatar/Avatar_Brain_LookAt.js +121 -121
  443. package/lib/engine-components/avatar/Avatar_MouthShapes.d.ts +15 -15
  444. package/lib/engine-components/avatar/Avatar_MouthShapes.js +79 -79
  445. package/lib/engine-components/avatar/Avatar_MustacheShake.d.ts +9 -9
  446. package/lib/engine-components/avatar/Avatar_MustacheShake.js +29 -29
  447. package/lib/engine-components/codegen/components.d.ts +215 -215
  448. package/lib/engine-components/codegen/components.js +217 -217
  449. package/lib/engine-components/debug/LogStats.d.ts +5 -5
  450. package/lib/engine-components/debug/LogStats.js +18 -18
  451. package/lib/engine-components/export/gltf/GltfExport.d.ts +30 -30
  452. package/lib/engine-components/export/gltf/GltfExport.js +246 -246
  453. package/lib/engine-components/export/gltf/index.d.ts +1 -1
  454. package/lib/engine-components/export/gltf/index.js +1 -1
  455. package/lib/engine-components/export/index.d.ts +1 -1
  456. package/lib/engine-components/export/index.js +1 -1
  457. package/lib/engine-components/export/usdz/Extension.d.ts +22 -22
  458. package/lib/engine-components/export/usdz/Extension.js +1 -1
  459. package/lib/engine-components/export/usdz/ThreeUSDZExporter.d.ts +160 -160
  460. package/lib/engine-components/export/usdz/ThreeUSDZExporter.js +1782 -1782
  461. package/lib/engine-components/export/usdz/USDZExporter.d.ts +130 -130
  462. package/lib/engine-components/export/usdz/USDZExporter.js +663 -663
  463. package/lib/engine-components/export/usdz/extensions/Animation.d.ts +106 -106
  464. package/lib/engine-components/export/usdz/extensions/Animation.js +1071 -1071
  465. package/lib/engine-components/export/usdz/extensions/DocumentExtension.d.ts +5 -5
  466. package/lib/engine-components/export/usdz/extensions/DocumentExtension.js +6 -6
  467. package/lib/engine-components/export/usdz/extensions/USDZText.d.ts +54 -54
  468. package/lib/engine-components/export/usdz/extensions/USDZText.js +203 -203
  469. package/lib/engine-components/export/usdz/extensions/USDZUI.d.ts +8 -8
  470. package/lib/engine-components/export/usdz/extensions/USDZUI.js +157 -157
  471. package/lib/engine-components/export/usdz/extensions/behavior/Actions.d.ts +30 -30
  472. package/lib/engine-components/export/usdz/extensions/behavior/Actions.js +88 -88
  473. package/lib/engine-components/export/usdz/extensions/behavior/AudioExtension.d.ts +10 -10
  474. package/lib/engine-components/export/usdz/extensions/behavior/AudioExtension.js +86 -86
  475. package/lib/engine-components/export/usdz/extensions/behavior/Behaviour.d.ts +28 -28
  476. package/lib/engine-components/export/usdz/extensions/behavior/Behaviour.js +290 -290
  477. package/lib/engine-components/export/usdz/extensions/behavior/BehaviourComponents.d.ts +190 -190
  478. package/lib/engine-components/export/usdz/extensions/behavior/BehaviourComponents.js +1060 -1060
  479. package/lib/engine-components/export/usdz/extensions/behavior/BehavioursBuilder.d.ts +135 -135
  480. package/lib/engine-components/export/usdz/extensions/behavior/BehavioursBuilder.js +548 -548
  481. package/lib/engine-components/export/usdz/extensions/behavior/PhysicsExtension.d.ts +7 -7
  482. package/lib/engine-components/export/usdz/extensions/behavior/PhysicsExtension.js +115 -115
  483. package/lib/engine-components/export/usdz/index.d.ts +3 -3
  484. package/lib/engine-components/export/usdz/index.js +2 -2
  485. package/lib/engine-components/export/usdz/utils/animationutils.d.ts +7 -7
  486. package/lib/engine-components/export/usdz/utils/animationutils.js +163 -163
  487. package/lib/engine-components/export/usdz/utils/quicklook.d.ts +2 -2
  488. package/lib/engine-components/export/usdz/utils/quicklook.js +43 -43
  489. package/lib/engine-components/particlesystem/ParticleSystem.d.ts +177 -177
  490. package/lib/engine-components/particlesystem/ParticleSystem.js +1176 -1176
  491. package/lib/engine-components/particlesystem/ParticleSystemModules.d.ts +526 -526
  492. package/lib/engine-components/particlesystem/ParticleSystemModules.js +1930 -1930
  493. package/lib/engine-components/particlesystem/ParticleSystemSubEmitter.d.ts +25 -25
  494. package/lib/engine-components/particlesystem/ParticleSystemSubEmitter.js +87 -87
  495. package/lib/engine-components/particlesystem/api.d.ts +2 -2
  496. package/lib/engine-components/particlesystem/api.js +2 -2
  497. package/lib/engine-components/postprocessing/Effects/Antialiasing.d.ts +17 -17
  498. package/lib/engine-components/postprocessing/Effects/Antialiasing.js +50 -50
  499. package/lib/engine-components/postprocessing/Effects/BloomEffect.d.ts +46 -46
  500. package/lib/engine-components/postprocessing/Effects/BloomEffect.js +113 -113
  501. package/lib/engine-components/postprocessing/Effects/ChromaticAberration.d.ts +11 -11
  502. package/lib/engine-components/postprocessing/Effects/ChromaticAberration.js +39 -39
  503. package/lib/engine-components/postprocessing/Effects/ColorAdjustments.d.ts +15 -15
  504. package/lib/engine-components/postprocessing/Effects/ColorAdjustments.js +92 -92
  505. package/lib/engine-components/postprocessing/Effects/DepthOfField.d.ts +25 -25
  506. package/lib/engine-components/postprocessing/Effects/DepthOfField.js +104 -104
  507. package/lib/engine-components/postprocessing/Effects/EffectWrapper.d.ts +12 -12
  508. package/lib/engine-components/postprocessing/Effects/EffectWrapper.js +18 -18
  509. package/lib/engine-components/postprocessing/Effects/Pixelation.d.ts +11 -11
  510. package/lib/engine-components/postprocessing/Effects/Pixelation.js +32 -32
  511. package/lib/engine-components/postprocessing/Effects/ScreenspaceAmbientOcclusion.d.ts +18 -18
  512. package/lib/engine-components/postprocessing/Effects/ScreenspaceAmbientOcclusion.js +91 -91
  513. package/lib/engine-components/postprocessing/Effects/ScreenspaceAmbientOcclusionN8.d.ts +70 -70
  514. package/lib/engine-components/postprocessing/Effects/ScreenspaceAmbientOcclusionN8.js +175 -175
  515. package/lib/engine-components/postprocessing/Effects/Sharpening.d.ts +17 -17
  516. package/lib/engine-components/postprocessing/Effects/Sharpening.js +123 -123
  517. package/lib/engine-components/postprocessing/Effects/TiltShiftEffect.d.ts +17 -17
  518. package/lib/engine-components/postprocessing/Effects/TiltShiftEffect.js +70 -70
  519. package/lib/engine-components/postprocessing/Effects/Tonemapping.d.ts +26 -26
  520. package/lib/engine-components/postprocessing/Effects/Tonemapping.js +138 -138
  521. package/lib/engine-components/postprocessing/Effects/Vignette.d.ts +15 -15
  522. package/lib/engine-components/postprocessing/Effects/Vignette.js +60 -60
  523. package/lib/engine-components/postprocessing/PostProcessingEffect.d.ts +72 -72
  524. package/lib/engine-components/postprocessing/PostProcessingEffect.js +149 -149
  525. package/lib/engine-components/postprocessing/PostProcessingHandler.d.ts +25 -25
  526. package/lib/engine-components/postprocessing/PostProcessingHandler.js +350 -350
  527. package/lib/engine-components/postprocessing/Volume.d.ts +89 -89
  528. package/lib/engine-components/postprocessing/Volume.js +367 -367
  529. package/lib/engine-components/postprocessing/VolumeParameter.d.ts +26 -26
  530. package/lib/engine-components/postprocessing/VolumeParameter.js +136 -136
  531. package/lib/engine-components/postprocessing/VolumeProfile.d.ts +15 -15
  532. package/lib/engine-components/postprocessing/VolumeProfile.js +60 -60
  533. package/lib/engine-components/postprocessing/index.d.ts +5 -5
  534. package/lib/engine-components/postprocessing/index.js +5 -5
  535. package/lib/engine-components/postprocessing/utils.d.ts +11 -11
  536. package/lib/engine-components/postprocessing/utils.js +37 -37
  537. package/lib/engine-components/timeline/PlayableDirector.d.ts +163 -163
  538. package/lib/engine-components/timeline/PlayableDirector.js +686 -686
  539. package/lib/engine-components/timeline/SignalAsset.d.ts +24 -24
  540. package/lib/engine-components/timeline/SignalAsset.js +130 -130
  541. package/lib/engine-components/timeline/TimelineModels.d.ts +89 -89
  542. package/lib/engine-components/timeline/TimelineModels.js +22 -22
  543. package/lib/engine-components/timeline/TimelineTracks.d.ts +110 -110
  544. package/lib/engine-components/timeline/TimelineTracks.js +879 -879
  545. package/lib/engine-components/timeline/index.d.ts +4 -4
  546. package/lib/engine-components/timeline/index.js +3 -3
  547. package/lib/engine-components/ui/BaseUIComponent.d.ts +49 -49
  548. package/lib/engine-components/ui/BaseUIComponent.js +170 -170
  549. package/lib/engine-components/ui/Button.d.ts +64 -64
  550. package/lib/engine-components/ui/Button.js +315 -315
  551. package/lib/engine-components/ui/Canvas.d.ts +74 -74
  552. package/lib/engine-components/ui/Canvas.js +407 -407
  553. package/lib/engine-components/ui/CanvasGroup.d.ts +19 -19
  554. package/lib/engine-components/ui/CanvasGroup.js +58 -58
  555. package/lib/engine-components/ui/EventSystem.d.ts +125 -125
  556. package/lib/engine-components/ui/EventSystem.js +764 -764
  557. package/lib/engine-components/ui/Graphic.d.ts +55 -55
  558. package/lib/engine-components/ui/Graphic.js +254 -254
  559. package/lib/engine-components/ui/Image.d.ts +35 -35
  560. package/lib/engine-components/ui/Image.js +116 -116
  561. package/lib/engine-components/ui/InputField.d.ts +42 -42
  562. package/lib/engine-components/ui/InputField.js +268 -268
  563. package/lib/engine-components/ui/Interfaces.d.ts +38 -38
  564. package/lib/engine-components/ui/Interfaces.js +12 -12
  565. package/lib/engine-components/ui/Layout.d.ts +84 -84
  566. package/lib/engine-components/ui/Layout.js +330 -330
  567. package/lib/engine-components/ui/Outline.d.ts +7 -7
  568. package/lib/engine-components/ui/Outline.js +20 -20
  569. package/lib/engine-components/ui/PointerEvents.d.ts +115 -115
  570. package/lib/engine-components/ui/PointerEvents.js +145 -145
  571. package/lib/engine-components/ui/RaycastUtils.d.ts +11 -11
  572. package/lib/engine-components/ui/RaycastUtils.js +67 -67
  573. package/lib/engine-components/ui/Raycaster.d.ts +30 -30
  574. package/lib/engine-components/ui/Raycaster.js +95 -95
  575. package/lib/engine-components/ui/RectTransform.d.ts +61 -61
  576. package/lib/engine-components/ui/RectTransform.js +353 -353
  577. package/lib/engine-components/ui/SpatialHtml.d.ts +8 -8
  578. package/lib/engine-components/ui/SpatialHtml.js +79 -79
  579. package/lib/engine-components/ui/Text.d.ts +78 -78
  580. package/lib/engine-components/ui/Text.js +539 -539
  581. package/lib/engine-components/ui/Utils.d.ts +24 -24
  582. package/lib/engine-components/ui/Utils.js +90 -90
  583. package/lib/engine-components/ui/index.d.ts +1 -1
  584. package/lib/engine-components/ui/index.js +1 -1
  585. package/lib/engine-components/utils/EnvironmentScene.d.ts +5 -5
  586. package/lib/engine-components/utils/EnvironmentScene.js +205 -205
  587. package/lib/engine-components/utils/LookAt.d.ts +31 -31
  588. package/lib/engine-components/utils/LookAt.js +82 -82
  589. package/lib/engine-components/utils/OpenURL.d.ts +42 -42
  590. package/lib/engine-components/utils/OpenURL.js +119 -119
  591. package/lib/engine-components/webxr/Avatar.d.ts +25 -25
  592. package/lib/engine-components/webxr/Avatar.js +255 -255
  593. package/lib/engine-components/webxr/TeleportTarget.d.ts +7 -7
  594. package/lib/engine-components/webxr/TeleportTarget.js +7 -7
  595. package/lib/engine-components/webxr/WebARCameraBackground.d.ts +30 -30
  596. package/lib/engine-components/webxr/WebARCameraBackground.js +155 -155
  597. package/lib/engine-components/webxr/WebARSessionRoot.d.ts +98 -98
  598. package/lib/engine-components/webxr/WebARSessionRoot.js +770 -770
  599. package/lib/engine-components/webxr/WebXR.d.ts +232 -232
  600. package/lib/engine-components/webxr/WebXR.js +561 -561
  601. package/lib/engine-components/webxr/WebXRAvatar.d.ts +27 -27
  602. package/lib/engine-components/webxr/WebXRAvatar.js +44 -44
  603. package/lib/engine-components/webxr/WebXRImageTracking.d.ts +86 -86
  604. package/lib/engine-components/webxr/WebXRImageTracking.js +470 -470
  605. package/lib/engine-components/webxr/WebXRPlaneTracking.d.ts +92 -92
  606. package/lib/engine-components/webxr/WebXRPlaneTracking.js +500 -500
  607. package/lib/engine-components/webxr/WebXRRig.d.ts +32 -32
  608. package/lib/engine-components/webxr/WebXRRig.js +72 -72
  609. package/lib/engine-components/webxr/XRFlag.d.ts +38 -38
  610. package/lib/engine-components/webxr/XRFlag.js +139 -139
  611. package/lib/engine-components/webxr/controllers/XRControllerFollow.d.ts +47 -47
  612. package/lib/engine-components/webxr/controllers/XRControllerFollow.js +120 -120
  613. package/lib/engine-components/webxr/controllers/XRControllerModel.d.ts +43 -43
  614. package/lib/engine-components/webxr/controllers/XRControllerModel.js +351 -351
  615. package/lib/engine-components/webxr/controllers/XRControllerMovement.d.ts +78 -78
  616. package/lib/engine-components/webxr/controllers/XRControllerMovement.js +506 -506
  617. package/lib/engine-components/webxr/index.d.ts +3 -3
  618. package/lib/engine-components/webxr/index.js +3 -3
  619. package/lib/engine-components/webxr/types.d.ts +3 -3
  620. package/lib/engine-components/webxr/types.js +1 -1
  621. package/lib/engine-components-experimental/Presentation.d.ts +6 -6
  622. package/lib/engine-components-experimental/Presentation.js +9 -9
  623. package/lib/engine-components-experimental/api.d.ts +4 -4
  624. package/lib/engine-components-experimental/api.js +4 -4
  625. package/lib/engine-components-experimental/networking/PlayerSync.d.ts +156 -156
  626. package/lib/engine-components-experimental/networking/PlayerSync.js +377 -377
  627. package/lib/engine-schemes/api.d.ts +12 -12
  628. package/lib/engine-schemes/api.js +12 -12
  629. package/lib/engine-schemes/schemes.d.ts +7 -7
  630. package/lib/engine-schemes/schemes.js +19 -19
  631. package/lib/engine-schemes/synced-camera-model.d.ts +25 -25
  632. package/lib/engine-schemes/synced-camera-model.js +67 -67
  633. package/lib/engine-schemes/synced-transform-model.d.ts +31 -31
  634. package/lib/engine-schemes/synced-transform-model.js +66 -66
  635. package/lib/engine-schemes/transform.d.ts +12 -12
  636. package/lib/engine-schemes/transform.js +39 -39
  637. package/lib/engine-schemes/vec2.d.ts +10 -10
  638. package/lib/engine-schemes/vec2.js +25 -25
  639. package/lib/engine-schemes/vec3.d.ts +11 -11
  640. package/lib/engine-schemes/vec3.js +29 -29
  641. package/lib/engine-schemes/vec4.d.ts +12 -12
  642. package/lib/engine-schemes/vec4.js +33 -33
  643. package/lib/engine-schemes/vr-user-state-buffer.d.ts +37 -37
  644. package/lib/engine-schemes/vr-user-state-buffer.js +110 -110
  645. package/lib/include/three/EXT_mesh_gpu_instancing_exporter.d.ts +6 -6
  646. package/lib/include/three/EXT_mesh_gpu_instancing_exporter.js +45 -45
  647. package/lib/needle-engine.d.ts +7 -7
  648. package/lib/needle-engine.js +64 -64
  649. package/package.json +1 -1
  650. package/plugins/common/buildinfo.js +64 -63
  651. package/plugins/common/cloud.js +2 -0
  652. package/plugins/common/config.cjs +31 -31
  653. package/plugins/common/config.js +35 -35
  654. package/plugins/common/files.js +31 -31
  655. package/plugins/common/generator.js +10 -10
  656. package/plugins/common/license.js +266 -224
  657. package/plugins/common/npm.js +15 -15
  658. package/plugins/common/timers.js +7 -7
  659. package/plugins/common/version.js +37 -37
  660. package/plugins/gltf-packer.mjs +1 -1
  661. package/plugins/next/alias.cjs +39 -39
  662. package/plugins/next/license.cjs +18 -18
  663. package/plugins/next/meshbvhworker.cjs +18 -18
  664. package/plugins/next/next.js +128 -128
  665. package/plugins/types/index.d.ts +2 -2
  666. package/plugins/types/license.d.ts +24 -24
  667. package/plugins/types/needleConfig.d.ts +27 -27
  668. package/plugins/types/userconfig.d.ts +105 -105
  669. package/plugins/types/webmanifest.d.ts +32 -32
  670. package/plugins/vite/alias.js +174 -174
  671. package/plugins/vite/asap.js +245 -245
  672. package/plugins/vite/build-pipeline.js +355 -354
  673. package/plugins/vite/build.js +19 -19
  674. package/plugins/vite/buildinfo.js +41 -41
  675. package/plugins/vite/config.js +106 -106
  676. package/plugins/vite/copyfiles.js +138 -138
  677. package/plugins/vite/defines.js +66 -66
  678. package/plugins/vite/dependencies.js +213 -213
  679. package/plugins/vite/dependency-watcher.js +231 -231
  680. package/plugins/vite/drop-client.js +76 -76
  681. package/plugins/vite/drop.js +87 -87
  682. package/plugins/vite/editor-connection.js +124 -124
  683. package/plugins/vite/facebook-instant-games.js +99 -99
  684. package/plugins/vite/gzip.js +5 -5
  685. package/plugins/vite/imports-logger.js +143 -143
  686. package/plugins/vite/index.js +139 -139
  687. package/plugins/vite/license.js +55 -55
  688. package/plugins/vite/meta.js +163 -163
  689. package/plugins/vite/npm.js +7 -7
  690. package/plugins/vite/peer.js +29 -29
  691. package/plugins/vite/poster-client.js +73 -73
  692. package/plugins/vite/poster.js +79 -79
  693. package/plugins/vite/pwa.js +604 -604
  694. package/plugins/vite/reload-client.js +15 -15
  695. package/plugins/vite/reload.js +363 -363
  696. package/plugins/vite/server.js +66 -66
  697. package/plugins/vite/transform-codegen.js +55 -55
  698. package/plugins/vite/transform.js +31 -31
  699. package/plugins/vite/vite-4.4-hack.js +31 -31
  700. package/src/asap/needle-asap.ts +111 -111
  701. package/src/asap/sessiongranted.ts +75 -75
  702. package/src/asap/utils.ts +4 -4
  703. package/src/engine/analytics/index.ts +10 -10
  704. package/src/engine/analytics/lcp.ts +35 -35
  705. package/src/engine/api.ts +81 -81
  706. package/src/engine/assets/index.ts +59 -59
  707. package/src/engine/assets/static.js +5 -5
  708. package/src/engine/codegen/register_types.ts +298 -298
  709. package/src/engine/debug/debug.ts +51 -51
  710. package/src/engine/debug/debug_console.ts +333 -333
  711. package/src/engine/debug/debug_overlay.ts +332 -332
  712. package/src/engine/debug/debug_spatial_console.ts +429 -429
  713. package/src/engine/debug/index.ts +1 -1
  714. package/src/engine/engine_addressables.ts +679 -679
  715. package/src/engine/engine_animation.ts +145 -145
  716. package/src/engine/engine_application.ts +113 -113
  717. package/src/engine/engine_assetdatabase.ts +387 -387
  718. package/src/engine/engine_audio.ts +24 -24
  719. package/src/engine/engine_camera.ts +39 -39
  720. package/src/engine/engine_components.ts +373 -373
  721. package/src/engine/engine_components_internal.ts +40 -40
  722. package/src/engine/engine_constants.ts +52 -52
  723. package/src/engine/engine_context.ts +1662 -1662
  724. package/src/engine/engine_context_registry.ts +129 -129
  725. package/src/engine/engine_coroutine.ts +54 -54
  726. package/src/engine/engine_create_objects.ts +398 -398
  727. package/src/engine/engine_default_parameters.ts +3 -3
  728. package/src/engine/engine_editor-sync.ts +28 -28
  729. package/src/engine/engine_element.ts +871 -871
  730. package/src/engine/engine_element_attributes.ts +85 -85
  731. package/src/engine/engine_element_extras.ts +16 -16
  732. package/src/engine/engine_element_loading.ts +381 -381
  733. package/src/engine/engine_element_overlay.ts +186 -186
  734. package/src/engine/engine_fileloader.js +9 -9
  735. package/src/engine/engine_gameobject.ts +684 -684
  736. package/src/engine/engine_generic_utils.js +13 -13
  737. package/src/engine/engine_gizmos.ts +577 -577
  738. package/src/engine/engine_gltf.ts +29 -29
  739. package/src/engine/engine_gltf_builtin_components.ts +403 -403
  740. package/src/engine/engine_hot_reload.ts +198 -198
  741. package/src/engine/engine_input.ts +1460 -1460
  742. package/src/engine/engine_input_utils.ts +23 -23
  743. package/src/engine/engine_instancing.ts +45 -45
  744. package/src/engine/engine_license.ts +329 -329
  745. package/src/engine/engine_lifecycle_api.ts +106 -106
  746. package/src/engine/engine_lifecycle_functions_internal.ts +193 -193
  747. package/src/engine/engine_lightdata.ts +117 -117
  748. package/src/engine/engine_loaders.ts +82 -82
  749. package/src/engine/engine_lods.ts +168 -168
  750. package/src/engine/engine_mainloop_utils.ts +472 -472
  751. package/src/engine/engine_math.ts +282 -282
  752. package/src/engine/engine_modules.ts +83 -83
  753. package/src/engine/engine_networking.ts +841 -841
  754. package/src/engine/engine_networking_auto.ts +352 -352
  755. package/src/engine/engine_networking_blob.ts +254 -254
  756. package/src/engine/engine_networking_files.ts +217 -217
  757. package/src/engine/engine_networking_files_default_components.ts +58 -58
  758. package/src/engine/engine_networking_instantiate.ts +419 -419
  759. package/src/engine/engine_networking_peer.ts +159 -159
  760. package/src/engine/engine_networking_streams.ts +713 -713
  761. package/src/engine/engine_networking_types.ts +24 -24
  762. package/src/engine/engine_networking_utils.ts +23 -23
  763. package/src/engine/engine_networking_websocket.ts +2 -2
  764. package/src/engine/engine_patcher.ts +199 -199
  765. package/src/engine/engine_physics.ts +783 -783
  766. package/src/engine/engine_physics.types.ts +46 -46
  767. package/src/engine/engine_physics_rapier.ts +1579 -1579
  768. package/src/engine/engine_playerview.ts +80 -80
  769. package/src/engine/engine_scenelighting.ts +294 -294
  770. package/src/engine/engine_scenetools.ts +365 -365
  771. package/src/engine/engine_serialization.ts +2 -2
  772. package/src/engine/engine_serialization_builtin_serializer.ts +473 -473
  773. package/src/engine/engine_serialization_core.ts +705 -705
  774. package/src/engine/engine_serialization_decorator.ts +80 -80
  775. package/src/engine/engine_setup.ts +1 -1
  776. package/src/engine/engine_shaders.ts +267 -267
  777. package/src/engine/engine_shims.ts +32 -32
  778. package/src/engine/engine_test_utils.ts +109 -109
  779. package/src/engine/engine_texture.ts +82 -82
  780. package/src/engine/engine_three_utils.ts +849 -849
  781. package/src/engine/engine_time.ts +94 -94
  782. package/src/engine/engine_time_utils.ts +237 -237
  783. package/src/engine/engine_tonemapping.ts +210 -210
  784. package/src/engine/engine_types.ts +714 -714
  785. package/src/engine/engine_typestore.ts +63 -63
  786. package/src/engine/engine_util_decorator.ts +136 -136
  787. package/src/engine/engine_utils.ts +972 -972
  788. package/src/engine/engine_utils_format.ts +212 -212
  789. package/src/engine/engine_utils_screenshot.ts +698 -698
  790. package/src/engine/engine_utils_screenshot.xr.ts +103 -103
  791. package/src/engine/export/gltf/Writers.ts +34 -34
  792. package/src/engine/export/gltf/index.ts +158 -158
  793. package/src/engine/export/index.ts +2 -2
  794. package/src/engine/export/state.ts +19 -19
  795. package/src/engine/export/utils.ts +9 -9
  796. package/src/engine/extensions/EXT_texture_exr.ts +50 -50
  797. package/src/engine/extensions/NEEDLE_animator_controller_model.ts +195 -195
  798. package/src/engine/extensions/NEEDLE_components.ts +268 -268
  799. package/src/engine/extensions/NEEDLE_gameobject_data.ts +81 -81
  800. package/src/engine/extensions/NEEDLE_lighting_settings.ts +185 -185
  801. package/src/engine/extensions/NEEDLE_lightmaps.ts +119 -119
  802. package/src/engine/extensions/NEEDLE_persistent_assets.ts +76 -76
  803. package/src/engine/extensions/NEEDLE_render_objects.ts +209 -209
  804. package/src/engine/extensions/NEEDLE_techniques_webgl.ts +640 -640
  805. package/src/engine/extensions/extension_resolver.ts +4 -4
  806. package/src/engine/extensions/extension_utils.ts +166 -166
  807. package/src/engine/extensions/extensions.ts +135 -135
  808. package/src/engine/extensions/index.ts +5 -5
  809. package/src/engine/extensions/usage_tracker.ts +100 -100
  810. package/src/engine/js-extensions/Camera.ts +37 -37
  811. package/src/engine/js-extensions/ExtensionUtils.ts +85 -85
  812. package/src/engine/js-extensions/Layers.ts +23 -23
  813. package/src/engine/js-extensions/Object3D.ts +296 -296
  814. package/src/engine/js-extensions/RGBAColor.ts +75 -75
  815. package/src/engine/js-extensions/Vector.ts +18 -18
  816. package/src/engine/js-extensions/index.ts +4 -4
  817. package/src/engine/physics/workers/mesh-bvh/GenerateMeshBVHWorker.js +122 -122
  818. package/src/engine/shaders/shaderData.ts +67 -67
  819. package/src/engine/tests/test_utils.ts +63 -63
  820. package/src/engine/webcomponents/WebXRButtons.ts +260 -260
  821. package/src/engine/webcomponents/api.ts +6 -6
  822. package/src/engine/webcomponents/buttons.ts +262 -262
  823. package/src/engine/webcomponents/fonts.ts +41 -41
  824. package/src/engine/webcomponents/icons.ts +57 -57
  825. package/src/engine/webcomponents/index.ts +1 -1
  826. package/src/engine/webcomponents/logo-element.ts +78 -78
  827. package/src/engine/webcomponents/needle menu/needle-menu-spatial.ts +573 -573
  828. package/src/engine/webcomponents/needle menu/needle-menu.ts +1107 -1107
  829. package/src/engine/webcomponents/needle-button.ts +181 -181
  830. package/src/engine/xr/NeedleXRController.ts +1126 -1126
  831. package/src/engine/xr/NeedleXRSession.ts +1585 -1585
  832. package/src/engine/xr/NeedleXRSync.ts +220 -220
  833. package/src/engine/xr/SceneTransition.ts +78 -78
  834. package/src/engine/xr/TempXRContext.ts +216 -216
  835. package/src/engine/xr/XRRig.ts +9 -9
  836. package/src/engine/xr/api.ts +5 -5
  837. package/src/engine/xr/events.ts +102 -102
  838. package/src/engine/xr/internal.ts +34 -34
  839. package/src/engine/xr/usdz.ts +30 -30
  840. package/src/engine/xr/utils.ts +39 -39
  841. package/src/engine-components/AlignmentConstraint.ts +36 -36
  842. package/src/engine-components/Animation.ts +557 -557
  843. package/src/engine-components/AnimationCurve.ts +150 -150
  844. package/src/engine-components/AnimationUtils.ts +28 -28
  845. package/src/engine-components/AnimationUtilsAutoplay.ts +43 -43
  846. package/src/engine-components/Animator.ts +397 -397
  847. package/src/engine-components/AnimatorController.ts +1293 -1293
  848. package/src/engine-components/AudioListener.ts +92 -92
  849. package/src/engine-components/AudioSource.ts +639 -639
  850. package/src/engine-components/AvatarLoader.ts +263 -263
  851. package/src/engine-components/AxesHelper.ts +59 -59
  852. package/src/engine-components/BasicIKConstraint.ts +54 -54
  853. package/src/engine-components/BoxCollider.ts +1 -1
  854. package/src/engine-components/BoxHelperComponent.ts +114 -114
  855. package/src/engine-components/Camera.ts +707 -707
  856. package/src/engine-components/CameraUtils.ts +144 -144
  857. package/src/engine-components/CharacterController.ts +253 -253
  858. package/src/engine-components/Collider.ts +374 -374
  859. package/src/engine-components/Component.ts +1294 -1294
  860. package/src/engine-components/ContactShadows.ts +482 -482
  861. package/src/engine-components/DeleteBox.ts +62 -62
  862. package/src/engine-components/DeviceFlag.ts +46 -46
  863. package/src/engine-components/DragControls.ts +1623 -1623
  864. package/src/engine-components/DropListener.ts +698 -698
  865. package/src/engine-components/Duplicatable.ts +198 -198
  866. package/src/engine-components/EventList.ts +266 -266
  867. package/src/engine-components/EventTrigger.ts +74 -74
  868. package/src/engine-components/EventType.ts +22 -22
  869. package/src/engine-components/Fog.ts +60 -60
  870. package/src/engine-components/Gizmos.ts +56 -56
  871. package/src/engine-components/GridHelper.ts +48 -48
  872. package/src/engine-components/GroundProjection.ts +356 -356
  873. package/src/engine-components/Interactable.ts +14 -14
  874. package/src/engine-components/Joints.ts +52 -52
  875. package/src/engine-components/LODGroup.ts +153 -153
  876. package/src/engine-components/Light.ts +558 -558
  877. package/src/engine-components/LookAtConstraint.ts +25 -25
  878. package/src/engine-components/NeedleMenu.ts +84 -84
  879. package/src/engine-components/NestedGltf.ts +86 -86
  880. package/src/engine-components/Networking.ts +114 -114
  881. package/src/engine-components/OffsetConstraint.ts +60 -60
  882. package/src/engine-components/OrbitControls.ts +1068 -1068
  883. package/src/engine-components/PlayerColor.ts +103 -103
  884. package/src/engine-components/ReflectionProbe.ts +209 -209
  885. package/src/engine-components/Renderer.ts +900 -900
  886. package/src/engine-components/RendererInstancing.ts +855 -855
  887. package/src/engine-components/RendererLightmap.ts +198 -198
  888. package/src/engine-components/RigidBody.ts +526 -526
  889. package/src/engine-components/SceneSwitcher.ts +1013 -1013
  890. package/src/engine-components/ScreenCapture.ts +592 -592
  891. package/src/engine-components/ShadowCatcher.ts +172 -172
  892. package/src/engine-components/Skybox.ts +455 -455
  893. package/src/engine-components/SmoothFollow.ts +76 -76
  894. package/src/engine-components/SpatialTrigger.ts +229 -229
  895. package/src/engine-components/SpectatorCamera.ts +787 -787
  896. package/src/engine-components/SphereCollider.ts +1 -1
  897. package/src/engine-components/SpriteRenderer.ts +468 -468
  898. package/src/engine-components/SyncedCamera.ts +220 -220
  899. package/src/engine-components/SyncedRoom.ts +380 -380
  900. package/src/engine-components/SyncedTransform.ts +378 -378
  901. package/src/engine-components/TestRunner.ts +118 -118
  902. package/src/engine-components/TransformGizmo.ts +219 -219
  903. package/src/engine-components/VideoPlayer.ts +1025 -1025
  904. package/src/engine-components/Voip.ts +363 -363
  905. package/src/engine-components/api.ts +60 -60
  906. package/src/engine-components/avatar/AvatarBlink_Simple.ts +69 -69
  907. package/src/engine-components/avatar/AvatarEyeLook_Rotation.ts +63 -63
  908. package/src/engine-components/avatar/Avatar_Brain_LookAt.ts +139 -139
  909. package/src/engine-components/avatar/Avatar_MouthShapes.ts +83 -83
  910. package/src/engine-components/avatar/Avatar_MustacheShake.ts +31 -31
  911. package/src/engine-components/codegen/components.ts +216 -216
  912. package/src/engine-components/debug/LogStats.ts +21 -21
  913. package/src/engine-components/export/gltf/GltfExport.ts +265 -265
  914. package/src/engine-components/export/usdz/Extension.ts +24 -24
  915. package/src/engine-components/export/usdz/ThreeUSDZExporter.ts +2413 -2413
  916. package/src/engine-components/export/usdz/USDZExporter.ts +705 -705
  917. package/src/engine-components/export/usdz/extensions/Animation.ts +1204 -1204
  918. package/src/engine-components/export/usdz/extensions/DocumentExtension.ts +9 -9
  919. package/src/engine-components/export/usdz/extensions/USDZText.ts +240 -240
  920. package/src/engine-components/export/usdz/extensions/USDZUI.ts +189 -189
  921. package/src/engine-components/export/usdz/extensions/behavior/Actions.ts +99 -99
  922. package/src/engine-components/export/usdz/extensions/behavior/AudioExtension.ts +102 -102
  923. package/src/engine-components/export/usdz/extensions/behavior/Behaviour.ts +320 -320
  924. package/src/engine-components/export/usdz/extensions/behavior/BehaviourComponents.ts +1225 -1225
  925. package/src/engine-components/export/usdz/extensions/behavior/BehavioursBuilder.ts +646 -646
  926. package/src/engine-components/export/usdz/extensions/behavior/PhysicsExtension.ts +132 -132
  927. package/src/engine-components/export/usdz/index.ts +2 -2
  928. package/src/engine-components/export/usdz/utils/animationutils.ts +191 -191
  929. package/src/engine-components/export/usdz/utils/quicklook.ts +50 -50
  930. package/src/engine-components/particlesystem/ParticleSystem.ts +1287 -1287
  931. package/src/engine-components/particlesystem/ParticleSystemModules.ts +1765 -1765
  932. package/src/engine-components/particlesystem/ParticleSystemSubEmitter.ts +111 -111
  933. package/src/engine-components/particlesystem/api.ts +1 -1
  934. package/src/engine-components/postprocessing/Effects/Antialiasing.ts +55 -55
  935. package/src/engine-components/postprocessing/Effects/BloomEffect.ts +116 -116
  936. package/src/engine-components/postprocessing/Effects/ChromaticAberration.ts +37 -37
  937. package/src/engine-components/postprocessing/Effects/ColorAdjustments.ts +95 -95
  938. package/src/engine-components/postprocessing/Effects/DepthOfField.ts +103 -103
  939. package/src/engine-components/postprocessing/Effects/EffectWrapper.ts +25 -25
  940. package/src/engine-components/postprocessing/Effects/Pixelation.ts +32 -32
  941. package/src/engine-components/postprocessing/Effects/ScreenspaceAmbientOcclusion.ts +90 -90
  942. package/src/engine-components/postprocessing/Effects/ScreenspaceAmbientOcclusionN8.ts +191 -191
  943. package/src/engine-components/postprocessing/Effects/Sharpening.ts +138 -138
  944. package/src/engine-components/postprocessing/Effects/TiltShiftEffect.ts +61 -61
  945. package/src/engine-components/postprocessing/Effects/Tonemapping.ts +155 -155
  946. package/src/engine-components/postprocessing/Effects/Vignette.ts +59 -59
  947. package/src/engine-components/postprocessing/PostProcessingEffect.ts +172 -172
  948. package/src/engine-components/postprocessing/PostProcessingHandler.ts +398 -398
  949. package/src/engine-components/postprocessing/Volume.ts +410 -410
  950. package/src/engine-components/postprocessing/VolumeParameter.ts +158 -158
  951. package/src/engine-components/postprocessing/VolumeProfile.ts +61 -61
  952. package/src/engine-components/postprocessing/index.ts +5 -5
  953. package/src/engine-components/postprocessing/utils.ts +52 -52
  954. package/src/engine-components/timeline/PlayableDirector.ts +722 -722
  955. package/src/engine-components/timeline/SignalAsset.ts +144 -144
  956. package/src/engine-components/timeline/TimelineModels.ts +92 -92
  957. package/src/engine-components/timeline/TimelineTracks.ts +967 -967
  958. package/src/engine-components/timeline/index.ts +3 -3
  959. package/src/engine-components/ui/BaseUIComponent.ts +203 -203
  960. package/src/engine-components/ui/Button.ts +307 -307
  961. package/src/engine-components/ui/Canvas.ts +419 -419
  962. package/src/engine-components/ui/CanvasGroup.ts +54 -54
  963. package/src/engine-components/ui/EventSystem.ts +853 -853
  964. package/src/engine-components/ui/Graphic.ts +275 -275
  965. package/src/engine-components/ui/Image.ts +112 -112
  966. package/src/engine-components/ui/InputField.ts +321 -321
  967. package/src/engine-components/ui/Interfaces.ts +57 -57
  968. package/src/engine-components/ui/Layout.ts +334 -334
  969. package/src/engine-components/ui/Outline.ts +13 -13
  970. package/src/engine-components/ui/PointerEvents.ts +206 -206
  971. package/src/engine-components/ui/RaycastUtils.ts +69 -69
  972. package/src/engine-components/ui/Raycaster.ts +102 -102
  973. package/src/engine-components/ui/RectTransform.ts +372 -372
  974. package/src/engine-components/ui/SpatialHtml.ts +79 -79
  975. package/src/engine-components/ui/Text.ts +578 -578
  976. package/src/engine-components/ui/Utils.ts +112 -112
  977. package/src/engine-components/utils/EnvironmentScene.ts +245 -245
  978. package/src/engine-components/utils/LookAt.ts +88 -88
  979. package/src/engine-components/utils/OpenURL.ts +114 -114
  980. package/src/engine-components/webxr/Avatar.ts +265 -265
  981. package/src/engine-components/webxr/TeleportTarget.ts +9 -9
  982. package/src/engine-components/webxr/WebARCameraBackground.ts +175 -175
  983. package/src/engine-components/webxr/WebARSessionRoot.ts +880 -880
  984. package/src/engine-components/webxr/WebXR.ts +585 -585
  985. package/src/engine-components/webxr/WebXRAvatar.ts +66 -66
  986. package/src/engine-components/webxr/WebXRImageTracking.ts +518 -518
  987. package/src/engine-components/webxr/WebXRPlaneTracking.ts +570 -570
  988. package/src/engine-components/webxr/WebXRRig.ts +77 -77
  989. package/src/engine-components/webxr/XRFlag.ts +147 -147
  990. package/src/engine-components/webxr/controllers/XRControllerFollow.ts +118 -118
  991. package/src/engine-components/webxr/controllers/XRControllerModel.ts +374 -374
  992. package/src/engine-components/webxr/controllers/XRControllerMovement.ts +550 -550
  993. package/src/engine-components/webxr/index.ts +2 -2
  994. package/src/engine-components/webxr/types.ts +3 -3
  995. package/src/engine-components-experimental/Presentation.ts +12 -12
  996. package/src/engine-components-experimental/api.ts +4 -4
  997. package/src/engine-components-experimental/networking/PlayerSync.ts +401 -401
  998. package/src/engine-schemes/COMPILE_SCHEMES.bat +3 -3
  999. package/src/engine-schemes/COMPILE_TS.bat +11 -11
  1000. package/src/engine-schemes/README.md +1 -1
  1001. package/src/engine-schemes/api.ts +12 -12
  1002. package/src/engine-schemes/schemes.ts +28 -28
  1003. package/src/engine-schemes/synced-camera-model.ts +92 -92
  1004. package/src/engine-schemes/synced-transform-model.ts +90 -90
  1005. package/src/engine-schemes/syncedCamera.fbs +10 -10
  1006. package/src/engine-schemes/transform.ts +50 -50
  1007. package/src/engine-schemes/transforms.fbs +25 -25
  1008. package/src/engine-schemes/vec.fbs +19 -19
  1009. package/src/engine-schemes/vec2.ts +33 -33
  1010. package/src/engine-schemes/vec3.ts +38 -38
  1011. package/src/engine-schemes/vec4.ts +43 -43
  1012. package/src/engine-schemes/vr-user-state-buffer.ts +145 -145
  1013. package/src/engine-schemes/vrUserStateBuffer.fbs +17 -17
  1014. package/src/include/draco/draco_decoder.js +34 -34
  1015. package/src/include/ktx2/basis_transcoder.js +21 -21
  1016. package/src/include/needle/arial-msdf.json +1471 -1471
  1017. package/src/include/three/DragControls.js +231 -231
  1018. package/src/include/three/EXT_mesh_gpu_instancing_exporter.js +66 -66
  1019. package/src/needle-engine.ts +70 -70
  1020. package/lib/engine/engine.d.ts +0 -4
  1021. package/lib/engine/engine.js +0 -12
  1022. package/lib/engine/engine.js.map +0 -1
  1023. package/lib/engine/engine_web_api.d.ts +0 -12
  1024. package/lib/engine/engine_web_api.js +0 -113
  1025. package/lib/engine/engine_web_api.js.map +0 -1
  1026. package/lib/engine-components/FlyControls.d.ts +0 -10
  1027. package/lib/engine-components/FlyControls.js +0 -29
  1028. package/lib/engine-components/FlyControls.js.map +0 -1
  1029. package/src/engine-schemes/dist/api.js +0 -17
  1030. package/src/engine-schemes/dist/api.js.meta +0 -7
  1031. package/src/engine-schemes/dist/schemes.js +0 -25
  1032. package/src/engine-schemes/dist/schemes.js.meta +0 -7
  1033. package/src/engine-schemes/dist/synced-camera-model.js +0 -74
  1034. package/src/engine-schemes/dist/synced-camera-model.js.meta +0 -7
  1035. package/src/engine-schemes/dist/synced-transform-model.js +0 -73
  1036. package/src/engine-schemes/dist/synced-transform-model.js.meta +0 -7
  1037. package/src/engine-schemes/dist/transform.js +0 -46
  1038. package/src/engine-schemes/dist/transform.js.meta +0 -7
  1039. package/src/engine-schemes/dist/vec2.js +0 -32
  1040. package/src/engine-schemes/dist/vec2.js.meta +0 -7
  1041. package/src/engine-schemes/dist/vec3.js +0 -36
  1042. package/src/engine-schemes/dist/vec3.js.meta +0 -7
  1043. package/src/engine-schemes/dist/vec4.js +0 -40
  1044. package/src/engine-schemes/dist/vec4.js.meta +0 -7
  1045. package/src/engine-schemes/dist/vr-user-state-buffer.js +0 -110
  1046. package/src/engine-schemes/dist/vr-user-state-buffer.js.meta +0 -7
@@ -1,1127 +1,1127 @@
1
- import { fetchProfile, MotionController } from "@webxr-input-profiles/motion-controllers";
2
- import { AxesHelper, Euler, MathUtils, Matrix4, Object3D, Quaternion, Ray, Vector3 } from "three";
3
-
4
- import { Context } from "../engine_context.js";
5
- import { Gizmos } from "../engine_gizmos.js";
6
- import { type InputEventNames, InputEvents, IPointerHitEventReceiver, NEPointerEvent, type NEPointerEventInit } from "../engine_input.js";
7
- import { getTempQuaternion, getTempVector, getWorldQuaternion } from "../engine_three_utils.js";
8
- import type { ButtonName, IGameObject, Vec3, XRControllerButtonName, XRGestureName } from "../engine_types.js";
9
- import { getParam } from "../engine_utils.js";
10
- import { flipForwardMatrix, flipForwardQuaternion } from "./internal.js";
11
- import type { NeedleXRHitTestResult, NeedleXRSession } from "./NeedleXRSession.js";
12
-
13
- const debug = getParam("debugwebxr");
14
- /** when enabled we will not use the browser select event but instead
15
- * we will emit the input event based on our own pinch detection
16
- * this is a workaround for visionOS not emitting the select events, see https://linear.app/needle/issue/NE-4212
17
- */
18
- const debugCustomGesture = getParam("debugcustomgesture");
19
-
20
- /** true when selectstart was ever received.
21
- * On VisionOS 1.1 we always have select events (as per the spec), so this is always true
22
- */
23
- // let _didReceiveSelectStartEvent = false;
24
-
25
- // https://github.com/immersive-web/webxr-input-profiles/blob/4484a05e30bcd43fe86bb4e06b7a707861a26796/packages/registry/profiles/meta/meta-quest-touch-plus.json
26
- declare type ControllerAxes = "xr-standard-thumbstick";
27
- declare type StickName = "xr-standard-thumbstick";
28
- declare type Mapping = "xr-standard";
29
- declare type ComponentType = "button" | "thumbstick" | "squeeze";
30
- declare type GamepadKey = "button" | "xAxis" | "yAxis";
31
-
32
- declare type NeedleXRControllerButtonName = ButtonName | "primary-button" | "primary";
33
-
34
- declare type ComponentMap = {
35
- type: ComponentType,
36
- rootNodeName?: string,
37
- gamepadIndices?: { [key in GamepadKey]?: number },
38
- visualResponses?: { [key: string]: { states: Array<string> } }
39
- }
40
-
41
- declare type InputDeviceLayout = {
42
- selectComponentId: string,
43
- components: { [key: string]: ComponentMap }
44
- mapping: Mapping;
45
- gamepad: Array<XRControllerButtonName>,
46
- axes: Array<{
47
- componentId: ControllerAxes,
48
- axis: "x-axis" | "y-axis",
49
- }>,
50
- }
51
- declare type InputDeviceProfile = {
52
- profileId: string,
53
- fallbackProfileIds: string[],
54
- layouts: [
55
- left: InputDeviceLayout,
56
- right: InputDeviceLayout
57
- ]
58
- }
59
-
60
- // https://github.com/immersive-web/webxr-input-profiles/blob/4484a05e30bcd43fe86bb4e06b7a707861a26796/packages/registry/profiles/meta/meta-quest-touch-plus.json
61
- const DEFAULT_PROFILES_PATH = 'https://cdn.jsdelivr.net/npm/@webxr-input-profiles/assets@1.0/dist/profiles';
62
- const DEFAULT_PROFILE = 'generic-trigger';
63
- const metacarpalToGripQuaternion = new Quaternion().setFromEuler(new Euler(MathUtils.degToRad(0), MathUtils.degToRad(-90), MathUtils.degToRad(-90)))
64
- const metacarpalToGripPosition = new Vector3(0.04, -0.04, 0.0);
65
-
66
- /**
67
- * A NeedleXRController wraps a connected XRInputDevice that is either a physical controller or a hand
68
- * You can access specific buttons using `getButton` and `getStick`
69
- * To get spatial data in rig space (position, rotation) use the `gripPosition`, `gripQuaternion`, `rayPosition` and `rayQuaternion` properties
70
- * To get spatial data in world space use the `gripWorldPosition`, `gripWorldQuaternion`, `rayWorldPosition` and `rayWorldQuaternion` properties
71
- * Inputs will also be emitted as pointer events on `this.context.input` - so you can receive controller inputs on objects using the appropriate input events on your components (e.g. `onPointerDown`, `onPointerUp` etc) - use the `pointerType` property to check if the event is from a controller or not
72
- * @link https://developer.mozilla.org/en-US/docs/Web/API/XRInputSource
73
- */
74
- export class NeedleXRController implements IPointerHitEventReceiver {
75
- /** the Needle XR Session */
76
- readonly xr: NeedleXRSession;
77
- get context() { return this.xr.context; }
78
- /**
79
- * https://developer.mozilla.org/en-US/docs/Web/API/XRInputSource
80
- */
81
- readonly inputSource: XRInputSource;
82
- /** the input source index */
83
- readonly index: number = 0;
84
-
85
- /** When enabled the controller will create input events in the Needle Engine input system (e.g. when a button is pressed or the controller is moved)
86
- * You can disable this if you don't want inputs to go through the input system but be aware that this will result in `onPointerDown` component callbacks to not be invoked anymore for this XRController
87
- */
88
- emitEvents = true;
89
-
90
- /** Is the controller still connected? */
91
- get connected() {
92
- return this._connected;
93
- }
94
- private _connected: boolean = true;
95
-
96
- get isTracking() { return this._isTracking; }
97
- private _isTracking: boolean = false;
98
- /** the input source gamepad giving raw access to the gamepad values
99
- * You should usually use the `getButton` and `getStick` methods instead to get access to named buttons and sticks
100
- */
101
- get gamepad() { return this.__gamepad ??= this.inputSource.gamepad; }
102
- private __gamepad?: Gamepad;
103
- /** @returns true if this is a hand (otherwise this is a controller) */
104
- get isHand() { return this.hand != undefined; }
105
- /**
106
- * If this is a hand then this is the hand info (XRHand)
107
- * @link https://developer.mozilla.org/en-US/docs/Web/API/XRHand
108
- */
109
- get hand() { return this.__hand ??= this.inputSource.hand; }
110
- private __hand?: XRHand;
111
- /** threejs XRHandSpace, shorthand for `context.renderer.xr.getHand(controllerIndex)`
112
- * @link https://threejs.org/docs/#api/en/renderers/webxr/WebXRManager.getHand
113
- */
114
- get handObject() { return this.context.renderer.xr.getHand(this.index); }
115
- /** The input source profiles */
116
- get profiles() { return this.inputSource.profiles; }
117
- /** The device input layout */
118
- get layout() { return this._layout; }
119
-
120
- /** shorthand for `inputSource.targetRayMode` */
121
- get targetRayMode(): (XRTargetRayMode | "transient-pointer") { return this.inputSource.targetRayMode; }
122
- /** shorthand for `inputSource.targetRaySpace` */
123
- get targetRaySpace() { return this.inputSource.targetRaySpace; }
124
- /** shorthand for `inputSource.gripSpace` */
125
- get gripSpace() { return this.inputSource.gripSpace; }
126
- /**
127
- * If the controller if held in the left or right hand (or if it's a left or right hand)
128
- **/
129
- get side() { return this.__side ??= this.inputSource.handedness; }
130
- private __side: XRHandedness | undefined = undefined;
131
-
132
- /** is right side. shorthand for `side === 'right'` */
133
- get isRight() { return this.side === 'right'; }
134
- /** is left side. shorthand for `side === 'left'` */
135
- get isLeft() { return this.side === 'left'; }
136
-
137
- /** is XR stylus, e.g. Logitech MX Ink */
138
- get isStylus() { return this._isMxInk; }
139
-
140
- /** The XRTransientInputHitTestSource can be used to perform hit tests with the controller ray against the real world.
141
- * see https://developer.mozilla.org/en-US/docs/Web/API/XRSession/requestHitTestSourceForTransientInput for more information
142
- * Requires the hit-test feature to be enabled in the XRSession
143
- *
144
- * NOTE: The hit test source should be cancelled once it's not needed anymore. Call `cancelHitTestSource` to do this
145
- */
146
- getHitTestSource() {
147
- if (!this._hitTestSource) this._requestHitTestSource();
148
- return this._hitTestSource;
149
- }
150
- get hasHitTestSource() {
151
- return this._hitTestSource;
152
- }
153
- /** Make sure to cancel the hittest source once it's not needed anymore */
154
- cancelHitTestSource() {
155
- if (this._hitTestSource) {
156
- this._hitTestSource.cancel();
157
- this._hitTestSource = undefined;
158
- }
159
- }
160
- private _hitTestSource: XRTransientInputHitTestSource | undefined = undefined;
161
- private _hasSelectEvent = false;
162
- get hasSelectEvent() { return this._hasSelectEvent; }
163
- private _isMxInk = false;
164
- private _isMetaQuestTouchController = false;
165
-
166
- /** Perform a hit test against the XR planes or meshes. shorthand for `xr.getHitTest(controller)`
167
- * @returns the hit test result (with position and rotation in worldspace) or null if no hit was found
168
- */
169
- getHitTest(): NeedleXRHitTestResult | null {
170
- return this.xr.getHitTest(this);
171
- }
172
-
173
- /** This is cleared at the beginning of each frame */
174
- private readonly _handJointPoses: Map<XRJointSpace, XRJointPose> = new Map();
175
- /** Get the hand joint pose from the current XRFrame. Results are cached for a frame to avoid calling getJointPose multiple times */
176
- getHandJointPose(joint: XRJointSpace, frame?: XRFrame) {
177
- frame = frame || this.xr.frame;
178
- if (!this.hand || !frame?.getJointPose || !this.xr.referenceSpace) return null;
179
- let pose = this._handJointPoses?.get(joint);
180
- if (pose) return pose;
181
- pose = frame.getJointPose(joint, this.xr.referenceSpace);
182
- if (pose) this._handJointPoses.set(joint, pose);
183
- return pose;
184
- }
185
-
186
- /** Grip matrix in grip space */
187
- private readonly _gripMatrix = new Matrix4();
188
- /** Grip position in grip space */
189
- private readonly _gripPosition = new Vector3();
190
- /** Grip rotation in grip space */
191
- private readonly _gripQuaternion = new Quaternion();
192
- private readonly _linearVelocity: Vector3 = new Vector3();
193
-
194
- private readonly _rayPositionRaw = new Vector3();
195
- private readonly _rayRotationRaw = new Quaternion();
196
- /** ray matrix in grip space */
197
- private readonly _rayMatrix = new Matrix4();
198
- /** Ray position in rig space */
199
- private readonly _rayPosition = new Vector3();
200
- /** Ray rotation in rig space */
201
- private readonly _rayQuaternion = new Quaternion();
202
-
203
- /** Grip position in rig space */
204
- get gripPosition() { return getTempVector(this._gripPosition) }
205
- /** Grip rotation in rig space */
206
- get gripQuaternion() { return getTempQuaternion(this._gripQuaternion) }
207
- get gripMatrix() { return this._gripMatrix; }
208
- /** Grip linear velocity in rig space
209
- * @link https://developer.mozilla.org/en-US/docs/Web/API/XRPose/linearVelocity
210
- */
211
- get gripLinearVelocity() {
212
- return getTempVector(this._linearVelocity).applyQuaternion(flipForwardQuaternion);
213
- }
214
- /** Ray position in rig space */
215
- get rayPosition() { return getTempVector(this._rayPosition) }
216
- /** Ray rotation in rig space */
217
- get rayQuaternion() { return getTempQuaternion(this._rayQuaternion) }
218
-
219
- /** Controller grip position in worldspace */
220
- get gripWorldPosition() { return getTempVector(this._gripWorldPosition); }
221
- private readonly _gripWorldPosition: Vector3 = new Vector3();
222
-
223
- /** Controller grip rotation in wordspace */
224
- get gripWorldQuaternion() {
225
- return getTempQuaternion(this._gripWorldQuaternion);
226
- }
227
- private readonly _gripWorldQuaternion: Quaternion = new Quaternion();
228
-
229
- /** Controller ray position in worldspace (this value is calculated once per frame by default - call `updateRayWorldPosition` to force an update) */
230
- get rayWorldPosition() {
231
- return getTempVector(this._rayWorldPosition);
232
- }
233
- private readonly _rayWorldPosition: Vector3 = new Vector3();
234
- /** Recalculates the ray world position */
235
- updateRayWorldPosition() {
236
- const parent = this.xr.context.mainCamera?.parent;
237
- this._rayWorldPosition.copy(this._rayPositionRaw);
238
- if (parent) this._rayWorldPosition.applyMatrix4(parent.matrixWorld);
239
- }
240
-
241
- /** Controller ray rotation in wordspace (this value is calculated once per frame by default - call `updateRayWorldQuaternion` to force an update) */
242
- get rayWorldQuaternion() {
243
- return getTempQuaternion(this._rayWorldQuaternion);
244
- }
245
- private readonly _rayWorldQuaternion: Quaternion = new Quaternion();
246
-
247
- get pinchPosition() {
248
- return getTempVector(this._pinchPosition);
249
- }
250
- private readonly _pinchPosition: Vector3 = new Vector3();
251
-
252
- /** Recalculates the ray world quaternion */
253
- updateRayWorldQuaternion() {
254
- const parent = this.xr.context.mainCamera?.parent;
255
- const parentWorldQuaternion = parent ? getWorldQuaternion(parent) : undefined;
256
- this._rayWorldQuaternion.copy(this._rayRotationRaw)
257
- // flip forward because we want +Z to be forward
258
- .multiply(flipForwardQuaternion);
259
- if (parentWorldQuaternion) this._rayWorldQuaternion.premultiply(parentWorldQuaternion)
260
- }
261
-
262
- /** The controller ray in worldspace */
263
- get ray(): Ray {
264
- this._ray.origin.copy(this.rayWorldPosition);
265
- this._ray.direction.copy(getTempVector(0, 0, 1).applyQuaternion(this.rayWorldQuaternion));
266
- return this._ray;
267
- }
268
- private readonly _ray;
269
-
270
- /** Recalculated once per update */
271
- private _hand_wristDotUp: number | undefined = undefined;
272
- /**
273
- * The dot product of the hand palm with the up vector.
274
- * This is a number between -1 and 1, where 1 means the palm is directly up and -1 means the palm is directly down (upside down).
275
- * This value is undefined if there's no hand
276
- */
277
- get handWristDotUp(): number | undefined {
278
- if (this._hand_wristDotUp !== undefined) return this._hand_wristDotUp;
279
- const handPalm = this.handObject?.joints["wrist"];
280
- if (handPalm) {
281
- const up = getTempVector(0, 1, 0).applyQuaternion(handPalm.quaternion);
282
- const dot = getTempVector(0, 1, 0).dot(up);
283
- return this._hand_wristDotUp = dot;
284
- }
285
- return undefined;
286
- }
287
- /**
288
- * @returns true if the hand is upside down
289
- */
290
- get isHandUpsideDown() {
291
- return this.handWristDotUp !== undefined ? this.handWristDotUp < -.7 : false;
292
- }
293
- /**
294
- * @returns true if the hand is upside down and we got a pinch down event this frame.
295
- */
296
- get isTeleportGesture() {
297
- return this.isHandUpsideDown && this.getGesture("pinch")?.isDown;
298
- }
299
-
300
- /** The controller object space.
301
- * You can use it to attach objects to the controller.
302
- * Children will be automatically detached and put into the scene when the controller disconnects
303
- */
304
- get object() { return this._object; }
305
- private readonly _object: IGameObject;
306
- private readonly _gripSpaceObject?: IGameObject;
307
- private readonly _raySpaceObject?: IGameObject;
308
-
309
- /** Assigned the model that you use for rendering. This can be used as a hint for other components */
310
- model: Object3D | null = null;
311
-
312
- private readonly _debugAxesHelper = new AxesHelper(.15);
313
- private readonly _debugGripAxesHelper = new AxesHelper(.07);
314
- private readonly _debugRayAxesHelper = new AxesHelper(.07);
315
-
316
- /** returns the URL of the default controller model */
317
- async getModelUrl(): Promise<string | null> {
318
- return this.getMotionController?.then(res => res?.assetUrl || null);
319
- }
320
-
321
- constructor(session: NeedleXRSession, device: XRInputSource, index: number) {
322
- this.xr = session;
323
- this.inputSource = device;
324
- this.index = index;
325
- this._object = new Object3D() as unknown as IGameObject;
326
- this._object.name = `NeedleXRController_${index}`;
327
-
328
- if (debug) {
329
- this._object.add(this._debugAxesHelper);
330
- this._gripSpaceObject = new Object3D() as unknown as IGameObject;
331
- this._raySpaceObject = new Object3D() as unknown as IGameObject;
332
- this._gripSpaceObject.name = `NeedleXRController_${index}_gripSpace`;
333
- this._raySpaceObject.name = `NeedleXRController_${index}_raySpace`;
334
- this._gripSpaceObject.add(this._debugGripAxesHelper);
335
- this._raySpaceObject.add(this._debugRayAxesHelper);
336
- this.xr.context.scene.add(this._gripSpaceObject);
337
- this.xr.context.scene.add(this._raySpaceObject);
338
- }
339
- this.xr.context.scene.add(this._object);
340
- this._ray = new Ray();
341
- this.pointerInit = {
342
- origin: this,
343
- pointerType: this.hand ? "hand" : "controller",
344
- deviceIndex: this.index,
345
- pointerId: -1, // < this will be updated in the emitPointerEvent method
346
- mode: this.inputSource.targetRayMode,
347
- ray: this._ray,
348
- device: this._object,
349
- buttonName: "none",
350
- }
351
- this.initialize();
352
- this.subscribeEvents();
353
-
354
- }
355
-
356
- private _hitTestSourcePromise: Promise<XRTransientInputHitTestSource | null> | null = null;
357
- private _requestHitTestSource(): Promise<XRTransientInputHitTestSource | null> | null {
358
- if (this._hitTestSourcePromise) return this._hitTestSourcePromise;
359
- // We only request a hit test source when we need it - meaning e.g. when we want to place the scene in AR
360
- // Make sure to cancel the hittest source when we don't need it anymore for performance reasons
361
-
362
- // // TODO: change this to check if we have hit-testing enabled instead of pass through.
363
- if (this.xr.mode === "immersive-ar" && this.inputSource.targetRayMode === "tracked-pointer" && this.xr.session.requestHitTestSourceForTransientInput) {
364
- // request hittest source
365
- return this._hitTestSourcePromise = this.xr.session.requestHitTestSourceForTransientInput({
366
- profile: this.inputSource.profiles[0],
367
- offsetRay: new XRRay(),
368
- })?.then(hitTestSource => {
369
- this._hitTestSourcePromise = null;
370
- if (!this.connected) {
371
- hitTestSource.cancel();
372
- return null;
373
- }
374
- return this._hitTestSource = hitTestSource;
375
- }) ?? null;
376
- }
377
- return null;
378
- }
379
-
380
- onPointerHits = _evt => {
381
- }
382
-
383
- onUpdate(frame: XRFrame) {
384
- performance.mark('NeedleXRController onUpdate start');
385
- this.onUpdateFrame(frame);
386
- this.updateInputEvents();
387
- this.onUpdateMove();
388
- performance.mark('NeedleXRController onUpdate end');
389
- performance.measure('NeedleXRController onUpdate', 'NeedleXRController onUpdate start', 'NeedleXRController onUpdate end');
390
- }
391
-
392
- onRenderDebug() {
393
- Gizmos.DrawSphere(this.rayWorldPosition, .003);
394
- Gizmos.DrawDirection(this.rayWorldPosition, getTempVector(0, 0, 10).applyQuaternion(this.rayWorldQuaternion));
395
- const labelPosition = this.inputSource.gripSpace ? this.gripWorldPosition : this.object.worldPosition;
396
- const debugLabelPosition = labelPosition.sub(this.object.worldForward.multiplyScalar(.1));
397
- const profileStr = this.inputSource.profiles.join("\n");
398
- let debugStr = `Controller[${this.index}] (${this.inputSource.targetRayMode}, ${this.side})
399
- C:${this.connected ? "x" : "-"} T:${this.isTracking ? "x" : "-"} Hand:${this.inputSource.hand ? "x" : "-"} Pen: ${this._isMxInk ? "x" : "-"}`;
400
- if (this.inputSource.hand) debugStr += `\nPinch: ${this.getGesture("pinch")?.value.toFixed(3)}`;
401
- debugStr += "\n" + profileStr;
402
- debugStr += "\n" + (this.inputSource.targetRaySpace ? `Ray: x` : "Ray: -") +
403
- (this.inputSource.gripSpace ? " Grip: x" : " Grip: -") +
404
- (this.inputSource.gamepad ? ` Gamepad: ${this.inputSource.gamepad.mapping}` : " Gamepad: -");
405
- if (this.inputSource.gamepad) {
406
- const gp = this.inputSource.gamepad;
407
- let gamepadStr = "[btns " + gp.buttons.length + "]: " + gp.buttons.map(b => b.value.toPrecision(1)).join(",");
408
- gamepadStr += "\n[axes " + gp.axes.length + "]: " + gp.axes.map(a => a.toPrecision(1)).join(",");
409
- debugStr += "\n" + gamepadStr;
410
- }
411
- Gizmos.DrawLabel(debugLabelPosition, debugStr, .006);
412
- }
413
-
414
- private onUpdateFrame(frame: XRFrame) {
415
- // make sure this is cleared every frame
416
- this._handJointPoses.clear();
417
- this._hand_wristDotUp = undefined;
418
-
419
- if (!this.xr.referenceSpace) {
420
- this._isTracking = false;
421
- return;
422
- }
423
-
424
- const rayPose = frame.getPose(this.inputSource.targetRaySpace, this.xr.referenceSpace);
425
- this._isTracking = rayPose != null;
426
- let gripPositionRaw: Vector3 | null = null;
427
- let gripQuaternionRaw: Quaternion | null = null;
428
- let rayPositionRaw: Vector3 | null = null;
429
- let rayQuaternionRaw: Quaternion | null = null;
430
-
431
- if (rayPose) {
432
- const t = rayPose.transform;
433
- this._rayMatrix
434
- .fromArray(t.matrix)
435
- .premultiply(flipForwardMatrix);
436
- this._rayMatrix.decompose(this._rayPosition, this._rayQuaternion, getTempVector(1, 1, 1));
437
- rayPositionRaw = getTempVector(t.position);
438
- rayQuaternionRaw = getTempQuaternion(t.orientation);
439
-
440
- this._rayPositionRaw.copy(rayPositionRaw);
441
- this._rayRotationRaw.copy(rayQuaternionRaw);
442
- }
443
-
444
- if (this.inputSource.gripSpace) {
445
- const gripPose = frame.getPose(this.inputSource.gripSpace, this.xr.referenceSpace!);
446
- if (gripPose) {
447
- const t = gripPose.transform;
448
- gripPositionRaw = getTempVector(t.position);
449
- gripQuaternionRaw = getTempQuaternion(t.orientation);
450
- this._gripMatrix
451
- .fromArray(t.matrix)
452
- .premultiply(flipForwardMatrix);
453
- this._gripMatrix.decompose(this._gripPosition, this._gripQuaternion, getTempVector(1, 1, 1));
454
-
455
- if ("linearVelocity" in gripPose && gripPose.linearVelocity) {
456
- const p = gripPose.linearVelocity as DOMPointReadOnly;
457
- this._linearVelocity.set(p.x, p.y, p.z);
458
- }
459
- }
460
- }
461
-
462
- // update controller object parent – needs to be parented to the rig, which
463
- // implicitly is the same object as the camera parent.
464
- if (this.xr.context.mainCamera?.parent) {
465
- if (this._object.parent !== this.xr.context.mainCamera?.parent)
466
- this.xr.context.mainCamera.parent.add(this._object);
467
- if (this._gripSpaceObject !== undefined && this._gripSpaceObject?.parent !== this.xr.context.mainCamera?.parent)
468
- this.xr.context.mainCamera.parent.add(this._gripSpaceObject);
469
- if (this._raySpaceObject !== undefined && this._raySpaceObject?.parent !== this.xr.context.mainCamera?.parent)
470
- this.xr.context.mainCamera.parent.add(this._raySpaceObject);
471
- }
472
- // for controllers, we set the position and rotation of the object to the ray position and rotation
473
- // for hands, we take the wrist position and rotation
474
- const hand = this.hand;
475
- if (hand) {
476
- // https://www.w3.org/TR/webxr-hand-input-1/#xrhand-interface
477
- let gotWrist = false;
478
- // TODO check why types are not correct here
479
- const wrist = hand.get("wrist");
480
- const wristPose = wrist && this.getHandJointPose(wrist, frame);
481
- if (wristPose) {
482
- gotWrist = true;
483
- const p = wristPose.transform.position;
484
- const q = wristPose.transform.orientation;
485
- this._object.position.set(p.x, p.y, p.z);
486
- this._object.quaternion.set(q.x, q.y, q.z, q.w).multiply(flipForwardQuaternion);
487
- }
488
- if (!gotWrist) {
489
- this._object.position.copy(this._rayPosition);
490
- this._object.quaternion.copy(this._rayQuaternion).multiply(flipForwardQuaternion);
491
- }
492
-
493
- //@ts-ignore
494
- const middle = hand.get("middle-finger-metacarpal");
495
- const middlePose = middle && this.getHandJointPose(middle, frame);
496
- if (middlePose) {
497
- // for some reason the grip rotation is different from the wrist rotation
498
- // but we want to use the wrist rotation for the grip
499
- this._gripMatrix
500
- .fromArray(middlePose.transform.matrix)
501
- .premultiply(flipForwardMatrix);
502
- this._gripMatrix.decompose(this._gripPosition, this._gripQuaternion, getTempVector(1, 1, 1));
503
-
504
- // If we don't have a grip space, we update the data from the metacarpal bone instead.
505
- // this way, things looking for a grip pose will still find one (e.g. XRControllerFollow).
506
- // For example, hands on VisionOS do not provide a gripSpace.
507
- if (true || !this.inputSource.gripSpace) {
508
- gripPositionRaw = getTempVector().copy(middlePose.transform.position);
509
- gripQuaternionRaw = getTempQuaternion().copy(middlePose.transform.orientation);
510
- gripQuaternionRaw.multiply(metacarpalToGripQuaternion);
511
- gripPositionRaw.add(getTempVector(metacarpalToGripPosition).applyQuaternion(gripQuaternionRaw));
512
- }
513
- }
514
- }
515
- // on VisionOS we get a gripSpace that matches where the controller is for transient input sources
516
- else if (this.inputSource.gripSpace && this.targetRayMode === "transient-pointer" && gripPositionRaw && gripQuaternionRaw) {
517
- this._object.position.copy(gripPositionRaw);
518
- this._object.quaternion.copy(gripQuaternionRaw).multiply(flipForwardQuaternion);
519
- }
520
- else if (rayPositionRaw && rayQuaternionRaw) {
521
- this._object.position.copy(rayPositionRaw);
522
- this._object.quaternion.copy(rayQuaternionRaw).multiply(flipForwardQuaternion);
523
- }
524
-
525
- if (debug) {
526
- if (rayPositionRaw && rayQuaternionRaw) {
527
- this._raySpaceObject?.position.copy(rayPositionRaw);
528
- this._raySpaceObject?.quaternion.copy(rayQuaternionRaw).multiply(flipForwardQuaternion);
529
- }
530
- if (gripPositionRaw && gripQuaternionRaw) {
531
- this._gripSpaceObject?.position.copy(gripPositionRaw);
532
- this._gripSpaceObject?.quaternion.copy(gripQuaternionRaw).multiply(flipForwardQuaternion);
533
- }
534
- }
535
-
536
- // UPDATE WORLD TRANSFORM DATA
537
- const parent = this.xr.context.mainCamera?.parent;
538
- const parentWorldQuaternion = parent ? getWorldQuaternion(parent) : undefined;
539
-
540
- // GRIP
541
- if (gripPositionRaw && gripQuaternionRaw) {
542
- this._gripWorldPosition.copy(gripPositionRaw);
543
- if (parent) this._gripWorldPosition.applyMatrix4(parent.matrixWorld);
544
-
545
- this._gripWorldQuaternion.copy(gripQuaternionRaw);
546
- // flip forward because we want +Z to be forward
547
- this._gripWorldQuaternion.multiply(flipForwardQuaternion);
548
- if (parentWorldQuaternion) this._gripWorldQuaternion.premultiply(parentWorldQuaternion)
549
- }
550
-
551
- // RAY
552
- this.updateRayWorldPosition();
553
- this.updateRayWorldQuaternion();
554
- }
555
-
556
- /** Called when the input source disconnects */
557
- onDisconnected() {
558
- this._connected = false;
559
- if (debug) console.warn("Controller disconnected", this.index);
560
- // move all attached objects into the scene
561
- for (const child of this._object.children) {
562
- this.xr.context.scene.attach(child);
563
- }
564
- this._object?.removeFromParent();
565
- this._debugAxesHelper?.removeFromParent();
566
- this._debugGripAxesHelper?.removeFromParent();
567
- this._debugRayAxesHelper?.removeFromParent();
568
- this._gripSpaceObject?.removeFromParent();
569
- this._raySpaceObject?.removeFromParent();
570
-
571
- this.unsubscribeEvents();
572
- if (this._hitTestSource) {
573
- this._hitTestSource.cancel();
574
- this._hitTestSource = undefined;
575
- }
576
- }
577
-
578
- /**
579
- * Get a gamepad button
580
- * @link https://github.com/immersive-web/webxr-gamepads-module/blob/main/gamepads-module-explainer.md
581
- * @param key the controller button name e.g. x-button
582
- * @returns the gamepad button if it exists on the controller - otherwise undefined
583
- */
584
- getButton(key: NeedleXRControllerButtonName): NeedleGamepadButton | undefined | null {
585
- if (!this._layout) return undefined;
586
-
587
- switch (key) {
588
- case "primary-button":
589
- if (this.isLeft) key = "x-button";
590
- else if (this.isRight) key = "a-button";
591
- else return undefined;
592
- break;
593
- case "primary":
594
- if (this.hand) {
595
- return this.getGesture("pinch");
596
- }
597
- return this.toNeedleGamepadButton(0, key);
598
- }
599
-
600
-
601
- if (this._buttonMap.has(key)) {
602
- return this.toNeedleGamepadButton(this._buttonMap.get(key)!, key);
603
- }
604
- const componentModel = this._layout?.components[key];
605
- if (componentModel?.gamepadIndices) {
606
- switch (componentModel.type) {
607
- case "button":
608
- case "squeeze":
609
- if (this.inputSource.gamepad) {
610
- const index = componentModel.gamepadIndices!.button!;
611
- this._buttonMap.set(key, index);
612
- return this.toNeedleGamepadButton(index, key);
613
- }
614
- break;
615
- default:
616
- console.warn("Unsupported component type", componentModel.type);
617
- break;
618
- }
619
- }
620
- this._buttonMap.set(key, undefined!);
621
- return undefined;
622
- }
623
-
624
- /** Get a gesture state */
625
- getGesture(key: XRGestureName): NeedleGamepadButton | null {
626
- const state = this.states[key];
627
- if (!state) return null;
628
- this.states[key] = state;
629
- const needleButton = this._needleGamepadButtons[key] || new NeedleGamepadButton(undefined, key);
630
- needleButton.pressed = state.pressed;
631
- needleButton.value = state.value;
632
- needleButton.isDown = state.isDown;
633
- needleButton.isUp = state.isUp;
634
- this._needleGamepadButtons[key] = needleButton;
635
- return needleButton;
636
- }
637
-
638
- /**
639
- * Get the pointer id for a specific button of this input device.
640
- * This is useful if you want to check if a button (e.g. trigger) is currently being in use which can be queried on the inputsystem.
641
- * @returns the pointer id for the button or undefined if the button is not supported
642
- * @example
643
- * ```ts
644
- * const pointerId = controller.getPointerId("primary");
645
- * if (pointerId !== undefined) {
646
- * const isUsed = this.context.input.getPointerUsed(pointerId);
647
- * console.log(controller.side, "used?", isUsed);
648
- * }
649
- * ```
650
- */
651
- getPointerId(button: number): number;
652
- getPointerId(button: NeedleXRControllerButtonName | XRGestureName): number | undefined;
653
- getPointerId(button: number | NeedleXRControllerButtonName | XRGestureName): number | undefined {
654
- if (button === "primary") {
655
- button = 0;
656
- }
657
- else if (button === "pinch") {
658
- button = 0;
659
- }
660
- if (typeof button !== "number") {
661
- const needleButton = this._buttonMap.get(button);
662
- if (needleButton === undefined) {
663
- return undefined;
664
- }
665
- button = needleButton;
666
- }
667
- return this.index * 10 + button;
668
- }
669
-
670
- private readonly _needleGamepadButtons: { [key: number | string]: NeedleGamepadButton } = {};
671
- /** combine the InputState information + the GamepadButton information (since GamepadButtons can not be extended) */
672
- private toNeedleGamepadButton(index: number, name: string): NeedleGamepadButton | undefined {
673
- if (!this.inputSource.gamepad?.buttons) return undefined
674
- const button = this.inputSource.gamepad?.buttons[index];
675
- const state = this.states[index];
676
- const needleButton = this._needleGamepadButtons[index] || new NeedleGamepadButton(index, name);
677
- if (button) {
678
- needleButton.pressed = button.pressed;
679
- needleButton.value = button.value;
680
- needleButton.touched = button.touched;
681
- }
682
- if (state) {
683
- needleButton.isDown = state.isDown;
684
- needleButton.isUp = state.isUp;
685
- }
686
- this._needleGamepadButtons[index] = needleButton;
687
- return needleButton;
688
- }
689
-
690
- /**
691
- * Get the values of a controller joystick
692
- * @link https://github.com/immersive-web/webxr-gamepads-module/blob/main/gamepads-module-explainer.md
693
- * @returns the stick values where x is left/right, y is up/down and z is the button value
694
- */
695
- getStick(key: StickName | "primary"): Vec3 {
696
- if (!this._layout) return { x: 0, y: 0, z: 0 };
697
-
698
- if (key === "primary") {
699
- const x = this.inputSource.gamepad?.axes[0] || 0;
700
- const y = this.inputSource.gamepad?.axes[1] || 0;
701
- // the primary thumbstick is button 3 (see gamepads module explainer)
702
- const z = this.inputSource.gamepad?.buttons[3]?.value || 0;
703
- return { x, y, z }
704
- }
705
-
706
- const componentModel = this._layout?.components[key];
707
- if (componentModel?.gamepadIndices) {
708
- switch (componentModel.type) {
709
- case "thumbstick":
710
- if (this.inputSource.gamepad) {
711
- const xIndex = componentModel.gamepadIndices!.xAxis!;
712
- const yIndex = componentModel.gamepadIndices!.yAxis!;
713
- let x = this.inputSource.gamepad?.axes[xIndex];
714
- let y = this.inputSource.gamepad?.axes[yIndex];
715
- x *= -1;
716
- y *= -1;
717
- const buttonIndex = componentModel.gamepadIndices!.button!;
718
- const z = this.inputSource.gamepad?.buttons[buttonIndex]?.value;
719
- return { x, y, z }
720
- }
721
- }
722
- }
723
- return { x: 0, y: 0, z: 0 }
724
- }
725
-
726
- private readonly _buttonMap = new Map<NeedleXRControllerButtonName, number>();
727
-
728
- // the motion controller contains the controller scheme, we use this to simplify button access
729
- private _motioncontroller?: MotionController;
730
- private _layout: InputDeviceLayout | undefined;
731
- private getMotionController!: Promise<MotionController>;
732
- private initialize() {
733
- // WORKAROUND for hand controllers that don't have a select event
734
- this._hasSelectEvent = this.profiles.includes("generic-hand-select") || this.profiles.some(p => p.startsWith("generic-trigger"));
735
-
736
- // Used to determine special layout for Quest controllers, e.g. last button is menu button
737
- this._isMetaQuestTouchController = this.profiles.includes("meta-quest-touch-plus") || this.profiles.includes("oculus-touch-v3");
738
-
739
- // Proper profile starting with v69 and browser 35.1
740
- this._isMxInk = this.profiles.includes("logitech-mx-ink")
741
-
742
- if (!this._layout) {
743
- // Ignore transient-pointer since we likely don't want to spawn a controller visual just for a temporary pointer.
744
- // TODO we should check how this is actually handled on Quest Browser when the transient-pointer flag is on.
745
- if (this.inputSource.targetRayMode as XRTargetRayMode | "transient-pointer" === "transient-pointer") return;
746
-
747
- // TODO: we should fetch the profiles or better yet the profile list once and cache it
748
- const fetchProfileCall = fetchProfile(this.inputSource, DEFAULT_PROFILES_PATH, DEFAULT_PROFILE);
749
- /** @ts-ignore */
750
- this.getMotionController = fetchProfileCall.then(res => {
751
-
752
- if (!this.connected) return null;
753
-
754
- this._motioncontroller = new MotionController(
755
- this.inputSource,
756
- res.profile,
757
- res.assetPath || ""
758
- );
759
-
760
- const profile = res.profile as InputDeviceProfile;
761
- const layout = profile.layouts[this.inputSource.handedness];
762
- this._layout = layout;
763
- if (this._layout) {
764
- if (!this._layout.gamepad?.length) {
765
- this._layout.gamepad = [];
766
- for (const key in this._layout.components) {
767
- const component = this._layout.components[key];
768
- this._layout.gamepad[component.gamepadIndices!.button!] = key as XRControllerButtonName;
769
- }
770
- }
771
- }
772
- // if (debug) console.log(this._layout, this.inputSource);
773
- // debugger;
774
- // this.getButton("a-button")
775
- return this._motioncontroller;
776
- }).catch(err => {
777
- if (this.inputSource)
778
- console.warn("Couldn't initialize motion controller profile for ", this.inputSource, err);
779
- return null;
780
- });
781
- }
782
- }
783
-
784
- /**
785
- * When enabled the controller will automatically emit pointer down events to the Needle Engine Input System.
786
- * @default true
787
- */
788
- emitPointerDownEvent: boolean = true;
789
-
790
- /**
791
- * When enabled the controller will automatically emit pointer up events to the Needle Engine Input System.
792
- * @default true
793
- */
794
- emitPointerUpEvent: boolean = true;
795
-
796
- /**
797
- * When enabled the controller will automatically emit pointer move events to the Needle Engine Input System.
798
- * @default true
799
- */
800
- emitPointerMoveEvent: boolean = true;
801
-
802
- /**
803
- * The distance threshold for pointer move events. This value is in units in rig space
804
- * @default 0.03
805
- */
806
- pointerMoveDistanceThreshold = 0.03;
807
-
808
- /**
809
- * The angle threshold for pointer move events. This value is in radians.
810
- * @default 0.05
811
- */
812
- pointerMoveAngleThreshold = 0.05;
813
-
814
- private subscribeEvents() {
815
- // https://developer.mozilla.org/en-US/docs/Web/API/XRSession/selectstart_event
816
- this.xr.session.addEventListener("selectstart", this.onSelectStart);
817
- this.xr.session.addEventListener("selectend", this.onSelectEnd);
818
- // https://developer.mozilla.org/en-US/docs/Web/API/XRSession/squeeze_event
819
- this.xr.session.addEventListener("squeezestart", this.onSequeezeStart);
820
- this.xr.session.addEventListener("squeezeend", this.onSequeezeEnd);
821
- }
822
- private unsubscribeEvents() {
823
- this.xr.session.removeEventListener("selectstart", this.onSelectStart);
824
- this.xr.session.removeEventListener("selectend", this.onSelectEnd);
825
- this.xr.session.removeEventListener("squeezestart", this.onSequeezeStart);
826
- this.xr.session.removeEventListener("squeezeend", this.onSequeezeEnd);
827
- }
828
-
829
- private _selectButtonIndex: number | undefined = undefined;
830
- private _squeezeButtonIndex: number | undefined = undefined;
831
-
832
- private onSelectStart = (evt: XRInputSourceEvent) => {
833
- if (!this.emitPointerDownEvent) return;
834
- if (this.inputSource !== evt.inputSource) return;
835
- // if a selectstart event happens right after an input source is connected, we may even receive this event before
836
- // requestAnimationFrame callback with the current session. So, we need to update the frame here.
837
- this.onUpdateFrame(evt.frame);
838
- // if we receive a select event we can be true that this device supports select events
839
- this._hasSelectEvent = true;
840
- const selectComponentId = this._layout?.selectComponentId;
841
- const i = this._layout?.components[selectComponentId!]?.gamepadIndices?.button;
842
- if (i !== undefined) this._selectButtonIndex = i;
843
- if (debugCustomGesture) return;
844
- /*
845
- if (!_didReceiveSelectStartEvent) {
846
- _didReceiveSelectStartEvent = true;
847
- // safeguard first pinch event - check if the pinch gesture is already down
848
- const pinch = this.getGesture("pinch");
849
- if (pinch?.pressed) {
850
- console.warn("Select start event was received but the pinch gesture is already down. This might happen the first time you start pinching", this.index, this.side);
851
- return;
852
- }
853
- }
854
- */
855
- if (debug) Gizmos.DrawDirection(this.rayWorldPosition, getTempVector(0, .01, 1).applyQuaternion(this.rayWorldQuaternion), 0xff0000, 10);
856
- this.emitPointerEvent(InputEvents.PointerDown, this._selectButtonIndex || 0, "xr-standard-trigger", true, evt);
857
- }
858
- private onSelectEnd = (evt: XRInputSourceEvent) => {
859
- if (!this.emitPointerUpEvent) return;
860
- if (debugCustomGesture) return;
861
- if (this.inputSource !== evt.inputSource) return;
862
- this.emitPointerEvent(InputEvents.PointerUp, this._selectButtonIndex || 0, "xr-standard-trigger", true, evt);
863
- }
864
- private onSequeezeStart = (evt: XRInputSourceEvent) => {
865
- if (!this.emitPointerDownEvent) return;
866
- if (this.inputSource !== evt.inputSource) return;
867
- this._squeezeButtonIndex = this._layout?.components["xr-standard-squeeze"]?.gamepadIndices?.button;
868
- if (this._squeezeButtonIndex !== undefined) {
869
- if (debug) Gizmos.DrawDirection(this.rayWorldPosition, getTempVector(0, .01, 1).applyQuaternion(this.rayWorldQuaternion), 0x0000ff, 10);
870
- this.emitPointerEvent(InputEvents.PointerDown, this._squeezeButtonIndex || 0, "xr-standard-squeeze", true, evt);
871
- }
872
- };
873
- private onSequeezeEnd = (evt: XRInputSourceEvent) => {
874
- if (!this.emitPointerUpEvent) return;
875
- if (this.inputSource !== evt.inputSource) return;
876
- if (this._squeezeButtonIndex !== undefined)
877
- this.emitPointerEvent(InputEvents.PointerUp, this._squeezeButtonIndex || 0, "xr-standard-squeeze", true, evt);
878
- };
879
-
880
- /** Index = button index */
881
- private readonly states: { [key: number | string]: InputState } = {};
882
- // If we want to invoke button events for ALL buttons we need to keep track of the previous state
883
- // instead of using XR input select start events which is only raised for the primary button
884
- // we should probably do both but then we need to ignore the primary index in the following function (to not raise an event for the same button twice)
885
- // and start with index = 1
886
- private updateInputEvents() {
887
- // https://immersive-web.github.io/webxr-gamepads-module/#xr-standard-heading
888
- if (this.gamepad?.buttons) {
889
- for (let index = 0; index < this.gamepad.buttons.length; index++) {
890
- const button = this.gamepad.buttons[index];
891
- const state = this.states[index] || new InputState();
892
- let eventName: InputEventNames | null = null;
893
-
894
- // Special handling for MX Ink stylus on Quest OS v69+.
895
- // We're never getting a "pressed" state here, so we determine pressed state based on the value.
896
- if (this._isMxInk && (index === 4 || index === 5)) {
897
- if (button.value > 0 && !state.pressed) {
898
- eventName = "pointerdown";
899
- state.isDown = true;
900
- state.isUp = false;
901
- }
902
- else if (button.value === 0 && state.pressed) {
903
- eventName = "pointerup";
904
- state.isDown = false;
905
- state.isUp = true;
906
- }
907
- else if (state.pressed) {
908
- eventName = "pointermove";
909
- state.isDown = false;
910
- state.isUp = false;
911
- }
912
- state.pressed = button.value > 0;
913
- state.value = button.value;
914
- }
915
- // Regular controller handling.
916
- else {
917
- // is down
918
- if (button.pressed && !state.pressed) {
919
- eventName = "pointerdown";
920
- state.isDown = true;
921
- state.isUp = false;
922
- }
923
- // is up
924
- else if (!button.pressed && state.pressed) {
925
- eventName = "pointerup"
926
- state.isDown = false;
927
- state.isUp = true;
928
- }
929
- else {
930
- state.isDown = false;
931
- state.isUp = false;
932
- }
933
- state.pressed = button.pressed;
934
- state.value = button.value;
935
- }
936
- this.states[index] = state;
937
-
938
- // the selection event is handled in the "selectstart" callback
939
- const emitEvent = index !== this._selectButtonIndex && index !== this._squeezeButtonIndex;
940
-
941
- if (eventName != null && emitEvent) {
942
- let name = this._layout?.gamepad[index];
943
- if (this._isMxInk && index === 4) name = "stylus-touch";
944
- if (this._isMxInk && index === 5) name = "stylus-tip";
945
- if (debug || debugCustomGesture) console.log("Emitting pointer event", eventName, index, name, button.value, this.gamepad, this._layout);
946
- this.emitPointerEvent(eventName, index, name ?? "none", false, null, button.value);
947
- }
948
- }
949
-
950
- // For Quest controllers, the last button is the menu button
951
- if (this._isMetaQuestTouchController) {
952
- const menuButtonIndex = this.gamepad.buttons.length - 1;
953
- const menuButtonState = this.states[menuButtonIndex];
954
- if (menuButtonState) {
955
- if (menuButtonState.isDown) {
956
- const menu = this.context.menu;
957
- if (menu.spatialMenuIsVisible)
958
- menu.setSpatialMenuVisible(false);
959
- else
960
- this.context.menu.setSpatialMenuVisible(true);
961
- }
962
- }
963
- }
964
- }
965
-
966
- // update hand gesture states
967
- if (this.hand) {
968
- const handObject = this.handObject;
969
- if (handObject) {
970
- // update pinch state
971
- const indexTip = handObject.joints["index-finger-tip"];
972
- const thumbTip = handObject.joints["thumb-tip"];
973
- if (indexTip && thumbTip) {
974
- const distance = indexTip.position.distanceTo(thumbTip.position);
975
-
976
- // upddate position of the pinch point
977
- this._pinchPosition.lerpVectors(indexTip.position, thumbTip.position, .5);
978
- const parent = this.xr.context.mainCamera?.parent;
979
- if (parent) this._pinchPosition.applyMatrix4(parent.matrixWorld);
980
-
981
- if (distance !== 0) { // ignore exactly 0 which happens when we switch from hands to controllers
982
- const pinchThreshold = .02;
983
- const pinchHysteresis = .01;
984
- const state = this.states["pinch"] || new InputState();
985
- const maxDistance = (pinchThreshold + pinchHysteresis) * 1.5;
986
- state.value = 1 - ((distance - pinchThreshold) / maxDistance);
987
-
988
- const isPressed = distance < (pinchThreshold - pinchHysteresis);
989
- const isReleased = distance > (pinchThreshold + pinchHysteresis);
990
- if (isPressed && !state.pressed) {
991
- if (debugCustomGesture) console.log("pinch start", distance);
992
- state.isDown = true;
993
- state.isUp = false;
994
- state.pressed = true;
995
- }
996
- else if (isReleased && state.pressed) {
997
- state.isDown = false;
998
- state.isUp = true;
999
- state.pressed = false;
1000
- }
1001
- else {
1002
- state.isDown = false;
1003
- state.isUp = false;
1004
- }
1005
- this.states["pinch"] = state;
1006
- }
1007
-
1008
- /** Workaround for visionOS not emitting selectstart. See https://linear.app/needle/issue/NE-4212
1009
- * If a selectstart event was never received we do a manual check here if the user is pinching
1010
- * Update: VisionOS 1.1 now properly emits select events from transient input sources, based on gaze.
1011
- * We're keeping this code commented for now since there may be future changes before VisionOS WebXR ships.
1012
- */
1013
- /*
1014
- if (!_didReceiveSelectStartEvent && (state.isDown || state.isUp)) {
1015
- const eventName = isPressed ? "pointerdown" : "pointerup";
1016
- const pressure = distance / pinchThreshold;
1017
- if (debugCustomGesture) {
1018
- const p = this.rayWorldPosition.add(this.object.worldForward.multiplyScalar(.2));
1019
- p.y += .05;
1020
- p.y += Math.random() * .02;
1021
- Gizmos.DrawLabel(p, "pinch:" + eventName + ", " + this.index + ", " + this.side + "\n" + handObject.uuid, 0.01, 5, 0x000000, new RGBAColor(1, 1, 1, .1));
1022
- }
1023
- this.emitPointerEvent(eventName, 0, "pinch", false, null, pressure);
1024
- }
1025
- */
1026
- }
1027
- }
1028
- }
1029
- }
1030
-
1031
- private _didMoveLastFrame = false;
1032
- private readonly _lastPointerMovePosition = new Vector3();
1033
- private readonly _lastPointerMoveQuaternion = new Quaternion();
1034
-
1035
- private onUpdateMove() {
1036
- if (!this.emitPointerMoveEvent) return;
1037
- let didMove = false;
1038
- const dist = this._lastPointerMovePosition.distanceTo(this.gripWorldPosition);
1039
- if (dist > this.pointerMoveDistanceThreshold * this.xr.rigScale) didMove = true;
1040
- if (!didMove) {
1041
- const angle = this._lastPointerMoveQuaternion.angleTo(this.gripWorldQuaternion);
1042
- if (angle > this.pointerMoveAngleThreshold) didMove = true;
1043
- }
1044
- if (didMove) {
1045
- this._didMoveLastFrame = true;
1046
- this._lastPointerMovePosition.copy(this.gripWorldPosition);
1047
- this._lastPointerMoveQuaternion.copy(this.gripWorldQuaternion);
1048
- if (debug) Gizmos.DrawLabel(this.rayWorldPosition.add(this.object.worldForward.multiplyScalar(.1)), "move", .01);
1049
-
1050
- let button = this.xr.context.input.getFirstPressedButtonForPointer(this.index);
1051
- if (button === undefined) button = 0;
1052
- const pressure = this.gamepad?.buttons[button]?.value;
1053
- this.emitPointerEvent("pointermove", button, "none", false, null, pressure);
1054
- }
1055
- else {
1056
- this._didMoveLastFrame = false;
1057
- }
1058
- }
1059
-
1060
-
1061
- /** cached spatial pointer init object. We re-use it to not have */
1062
- private readonly pointerInit: NEPointerEventInit;
1063
- private emitPointerEvent(type: InputEventNames, button: number, buttonName: ButtonName | "none", primary: boolean, source: Event | null = null, pressure?: number) {
1064
-
1065
- if (!this.emitEvents) {
1066
- if (debug && type !== InputEvents.PointerMove) console.warn("Pointer events are disabled for this controller", this.index, type, button);
1067
- return;
1068
- }
1069
-
1070
- // Currently we do only want to emit pointer events for NON screen based events
1071
- // that means if the input device is spatial (AR touch on a screen should be handled via touchdown etc still)
1072
- // Not sure if *this* is enough to determine if the event is spatial or not
1073
- if (this.xr.mode === "immersive-vr" || this.xr.isPassThrough) {
1074
- this.pointerInit.origin = this;
1075
- this.pointerInit.pointerId = this.getPointerId(button);
1076
- this.pointerInit.pointerType = this.hand ? "hand" : "controller";
1077
- this.pointerInit.button = button;
1078
- this.pointerInit.buttonName = buttonName;
1079
- this.pointerInit.isPrimary = primary;
1080
- this.pointerInit.mode = this.inputSource.targetRayMode;
1081
- this.pointerInit.ray = this.ray;
1082
- this.pointerInit.device = this.object;
1083
- this.pointerInit.pressure = pressure;
1084
- this.pointerInit.clientX = this._rayPosition.x / this.xr.rigScale;
1085
- this.pointerInit.clientY = this._rayPosition.y / this.xr.rigScale;
1086
- this.pointerInit.clientZ = this._rayPosition.z / this.xr.rigScale;
1087
-
1088
- const prevContext = Context.Current;
1089
- Context.Current = this.xr.context;
1090
- if (debug && type !== "pointermove") console.warn("Pointer event", type, button, buttonName, { ...this.pointerInit });
1091
- this.xr.context.input.createInputEvent(new NEPointerEvent(type, source, this.pointerInit));
1092
- Context.Current = prevContext;
1093
- }
1094
- }
1095
-
1096
-
1097
- }
1098
-
1099
- class InputState {
1100
- /** if the button was pressed the last update */
1101
- isDown: boolean = false;
1102
- /** if the button was released the last update */
1103
- isUp: boolean = false;
1104
-
1105
- pressed: boolean = false;
1106
- value: number = 0;
1107
- };
1108
-
1109
- /** Enhanced GamepadButton with `isDown` and `isUp` information */
1110
- class NeedleGamepadButton {
1111
- /** The index of the button in the input gamepad */
1112
- readonly index: number | undefined;
1113
- readonly name: string;
1114
-
1115
- touched: boolean = false;
1116
- pressed: boolean = false;
1117
- value: number = 0;
1118
- /** was the button just pressed down the last update */
1119
- isDown: boolean = false;
1120
- /** was the button just released the last update */
1121
- isUp: boolean = false;
1122
-
1123
- constructor(index: number | undefined, name: string) {
1124
- this.index = index;
1125
- this.name = name;
1126
- }
1
+ import { fetchProfile, MotionController } from "@webxr-input-profiles/motion-controllers";
2
+ import { AxesHelper, Euler, MathUtils, Matrix4, Object3D, Quaternion, Ray, Vector3 } from "three";
3
+
4
+ import { Context } from "../engine_context.js";
5
+ import { Gizmos } from "../engine_gizmos.js";
6
+ import { type InputEventNames, InputEvents, IPointerHitEventReceiver, NEPointerEvent, type NEPointerEventInit } from "../engine_input.js";
7
+ import { getTempQuaternion, getTempVector, getWorldQuaternion } from "../engine_three_utils.js";
8
+ import type { ButtonName, IGameObject, Vec3, XRControllerButtonName, XRGestureName } from "../engine_types.js";
9
+ import { getParam } from "../engine_utils.js";
10
+ import { flipForwardMatrix, flipForwardQuaternion } from "./internal.js";
11
+ import type { NeedleXRHitTestResult, NeedleXRSession } from "./NeedleXRSession.js";
12
+
13
+ const debug = getParam("debugwebxr");
14
+ /** when enabled we will not use the browser select event but instead
15
+ * we will emit the input event based on our own pinch detection
16
+ * this is a workaround for visionOS not emitting the select events, see https://linear.app/needle/issue/NE-4212
17
+ */
18
+ const debugCustomGesture = getParam("debugcustomgesture");
19
+
20
+ /** true when selectstart was ever received.
21
+ * On VisionOS 1.1 we always have select events (as per the spec), so this is always true
22
+ */
23
+ // let _didReceiveSelectStartEvent = false;
24
+
25
+ // https://github.com/immersive-web/webxr-input-profiles/blob/4484a05e30bcd43fe86bb4e06b7a707861a26796/packages/registry/profiles/meta/meta-quest-touch-plus.json
26
+ declare type ControllerAxes = "xr-standard-thumbstick";
27
+ declare type StickName = "xr-standard-thumbstick";
28
+ declare type Mapping = "xr-standard";
29
+ declare type ComponentType = "button" | "thumbstick" | "squeeze";
30
+ declare type GamepadKey = "button" | "xAxis" | "yAxis";
31
+
32
+ declare type NeedleXRControllerButtonName = ButtonName | "primary-button" | "primary";
33
+
34
+ declare type ComponentMap = {
35
+ type: ComponentType,
36
+ rootNodeName?: string,
37
+ gamepadIndices?: { [key in GamepadKey]?: number },
38
+ visualResponses?: { [key: string]: { states: Array<string> } }
39
+ }
40
+
41
+ declare type InputDeviceLayout = {
42
+ selectComponentId: string,
43
+ components: { [key: string]: ComponentMap }
44
+ mapping: Mapping;
45
+ gamepad: Array<XRControllerButtonName>,
46
+ axes: Array<{
47
+ componentId: ControllerAxes,
48
+ axis: "x-axis" | "y-axis",
49
+ }>,
50
+ }
51
+ declare type InputDeviceProfile = {
52
+ profileId: string,
53
+ fallbackProfileIds: string[],
54
+ layouts: [
55
+ left: InputDeviceLayout,
56
+ right: InputDeviceLayout
57
+ ]
58
+ }
59
+
60
+ // https://github.com/immersive-web/webxr-input-profiles/blob/4484a05e30bcd43fe86bb4e06b7a707861a26796/packages/registry/profiles/meta/meta-quest-touch-plus.json
61
+ const DEFAULT_PROFILES_PATH = 'https://cdn.jsdelivr.net/npm/@webxr-input-profiles/assets@1.0/dist/profiles';
62
+ const DEFAULT_PROFILE = 'generic-trigger';
63
+ const metacarpalToGripQuaternion = new Quaternion().setFromEuler(new Euler(MathUtils.degToRad(0), MathUtils.degToRad(-90), MathUtils.degToRad(-90)))
64
+ const metacarpalToGripPosition = new Vector3(0.04, -0.04, 0.0);
65
+
66
+ /**
67
+ * A NeedleXRController wraps a connected XRInputDevice that is either a physical controller or a hand
68
+ * You can access specific buttons using `getButton` and `getStick`
69
+ * To get spatial data in rig space (position, rotation) use the `gripPosition`, `gripQuaternion`, `rayPosition` and `rayQuaternion` properties
70
+ * To get spatial data in world space use the `gripWorldPosition`, `gripWorldQuaternion`, `rayWorldPosition` and `rayWorldQuaternion` properties
71
+ * Inputs will also be emitted as pointer events on `this.context.input` - so you can receive controller inputs on objects using the appropriate input events on your components (e.g. `onPointerDown`, `onPointerUp` etc) - use the `pointerType` property to check if the event is from a controller or not
72
+ * @link https://developer.mozilla.org/en-US/docs/Web/API/XRInputSource
73
+ */
74
+ export class NeedleXRController implements IPointerHitEventReceiver {
75
+ /** the Needle XR Session */
76
+ readonly xr: NeedleXRSession;
77
+ get context() { return this.xr.context; }
78
+ /**
79
+ * https://developer.mozilla.org/en-US/docs/Web/API/XRInputSource
80
+ */
81
+ readonly inputSource: XRInputSource;
82
+ /** the input source index */
83
+ readonly index: number = 0;
84
+
85
+ /** When enabled the controller will create input events in the Needle Engine input system (e.g. when a button is pressed or the controller is moved)
86
+ * You can disable this if you don't want inputs to go through the input system but be aware that this will result in `onPointerDown` component callbacks to not be invoked anymore for this XRController
87
+ */
88
+ emitEvents = true;
89
+
90
+ /** Is the controller still connected? */
91
+ get connected() {
92
+ return this._connected;
93
+ }
94
+ private _connected: boolean = true;
95
+
96
+ get isTracking() { return this._isTracking; }
97
+ private _isTracking: boolean = false;
98
+ /** the input source gamepad giving raw access to the gamepad values
99
+ * You should usually use the `getButton` and `getStick` methods instead to get access to named buttons and sticks
100
+ */
101
+ get gamepad() { return this.__gamepad ??= this.inputSource.gamepad; }
102
+ private __gamepad?: Gamepad;
103
+ /** @returns true if this is a hand (otherwise this is a controller) */
104
+ get isHand() { return this.hand != undefined; }
105
+ /**
106
+ * If this is a hand then this is the hand info (XRHand)
107
+ * @link https://developer.mozilla.org/en-US/docs/Web/API/XRHand
108
+ */
109
+ get hand() { return this.__hand ??= this.inputSource.hand; }
110
+ private __hand?: XRHand;
111
+ /** threejs XRHandSpace, shorthand for `context.renderer.xr.getHand(controllerIndex)`
112
+ * @link https://threejs.org/docs/#api/en/renderers/webxr/WebXRManager.getHand
113
+ */
114
+ get handObject() { return this.context.renderer.xr.getHand(this.index); }
115
+ /** The input source profiles */
116
+ get profiles() { return this.inputSource.profiles; }
117
+ /** The device input layout */
118
+ get layout() { return this._layout; }
119
+
120
+ /** shorthand for `inputSource.targetRayMode` */
121
+ get targetRayMode(): (XRTargetRayMode | "transient-pointer") { return this.inputSource.targetRayMode; }
122
+ /** shorthand for `inputSource.targetRaySpace` */
123
+ get targetRaySpace() { return this.inputSource.targetRaySpace; }
124
+ /** shorthand for `inputSource.gripSpace` */
125
+ get gripSpace() { return this.inputSource.gripSpace; }
126
+ /**
127
+ * If the controller if held in the left or right hand (or if it's a left or right hand)
128
+ **/
129
+ get side() { return this.__side ??= this.inputSource.handedness; }
130
+ private __side: XRHandedness | undefined = undefined;
131
+
132
+ /** is right side. shorthand for `side === 'right'` */
133
+ get isRight() { return this.side === 'right'; }
134
+ /** is left side. shorthand for `side === 'left'` */
135
+ get isLeft() { return this.side === 'left'; }
136
+
137
+ /** is XR stylus, e.g. Logitech MX Ink */
138
+ get isStylus() { return this._isMxInk; }
139
+
140
+ /** The XRTransientInputHitTestSource can be used to perform hit tests with the controller ray against the real world.
141
+ * see https://developer.mozilla.org/en-US/docs/Web/API/XRSession/requestHitTestSourceForTransientInput for more information
142
+ * Requires the hit-test feature to be enabled in the XRSession
143
+ *
144
+ * NOTE: The hit test source should be cancelled once it's not needed anymore. Call `cancelHitTestSource` to do this
145
+ */
146
+ getHitTestSource() {
147
+ if (!this._hitTestSource) this._requestHitTestSource();
148
+ return this._hitTestSource;
149
+ }
150
+ get hasHitTestSource() {
151
+ return this._hitTestSource;
152
+ }
153
+ /** Make sure to cancel the hittest source once it's not needed anymore */
154
+ cancelHitTestSource() {
155
+ if (this._hitTestSource) {
156
+ this._hitTestSource.cancel();
157
+ this._hitTestSource = undefined;
158
+ }
159
+ }
160
+ private _hitTestSource: XRTransientInputHitTestSource | undefined = undefined;
161
+ private _hasSelectEvent = false;
162
+ get hasSelectEvent() { return this._hasSelectEvent; }
163
+ private _isMxInk = false;
164
+ private _isMetaQuestTouchController = false;
165
+
166
+ /** Perform a hit test against the XR planes or meshes. shorthand for `xr.getHitTest(controller)`
167
+ * @returns the hit test result (with position and rotation in worldspace) or null if no hit was found
168
+ */
169
+ getHitTest(): NeedleXRHitTestResult | null {
170
+ return this.xr.getHitTest(this);
171
+ }
172
+
173
+ /** This is cleared at the beginning of each frame */
174
+ private readonly _handJointPoses: Map<XRJointSpace, XRJointPose> = new Map();
175
+ /** Get the hand joint pose from the current XRFrame. Results are cached for a frame to avoid calling getJointPose multiple times */
176
+ getHandJointPose(joint: XRJointSpace, frame?: XRFrame) {
177
+ frame = frame || this.xr.frame;
178
+ if (!this.hand || !frame?.getJointPose || !this.xr.referenceSpace) return null;
179
+ let pose = this._handJointPoses?.get(joint);
180
+ if (pose) return pose;
181
+ pose = frame.getJointPose(joint, this.xr.referenceSpace);
182
+ if (pose) this._handJointPoses.set(joint, pose);
183
+ return pose;
184
+ }
185
+
186
+ /** Grip matrix in grip space */
187
+ private readonly _gripMatrix = new Matrix4();
188
+ /** Grip position in grip space */
189
+ private readonly _gripPosition = new Vector3();
190
+ /** Grip rotation in grip space */
191
+ private readonly _gripQuaternion = new Quaternion();
192
+ private readonly _linearVelocity: Vector3 = new Vector3();
193
+
194
+ private readonly _rayPositionRaw = new Vector3();
195
+ private readonly _rayRotationRaw = new Quaternion();
196
+ /** ray matrix in grip space */
197
+ private readonly _rayMatrix = new Matrix4();
198
+ /** Ray position in rig space */
199
+ private readonly _rayPosition = new Vector3();
200
+ /** Ray rotation in rig space */
201
+ private readonly _rayQuaternion = new Quaternion();
202
+
203
+ /** Grip position in rig space */
204
+ get gripPosition() { return getTempVector(this._gripPosition) }
205
+ /** Grip rotation in rig space */
206
+ get gripQuaternion() { return getTempQuaternion(this._gripQuaternion) }
207
+ get gripMatrix() { return this._gripMatrix; }
208
+ /** Grip linear velocity in rig space
209
+ * @link https://developer.mozilla.org/en-US/docs/Web/API/XRPose/linearVelocity
210
+ */
211
+ get gripLinearVelocity() {
212
+ return getTempVector(this._linearVelocity).applyQuaternion(flipForwardQuaternion);
213
+ }
214
+ /** Ray position in rig space */
215
+ get rayPosition() { return getTempVector(this._rayPosition) }
216
+ /** Ray rotation in rig space */
217
+ get rayQuaternion() { return getTempQuaternion(this._rayQuaternion) }
218
+
219
+ /** Controller grip position in worldspace */
220
+ get gripWorldPosition() { return getTempVector(this._gripWorldPosition); }
221
+ private readonly _gripWorldPosition: Vector3 = new Vector3();
222
+
223
+ /** Controller grip rotation in wordspace */
224
+ get gripWorldQuaternion() {
225
+ return getTempQuaternion(this._gripWorldQuaternion);
226
+ }
227
+ private readonly _gripWorldQuaternion: Quaternion = new Quaternion();
228
+
229
+ /** Controller ray position in worldspace (this value is calculated once per frame by default - call `updateRayWorldPosition` to force an update) */
230
+ get rayWorldPosition() {
231
+ return getTempVector(this._rayWorldPosition);
232
+ }
233
+ private readonly _rayWorldPosition: Vector3 = new Vector3();
234
+ /** Recalculates the ray world position */
235
+ updateRayWorldPosition() {
236
+ const parent = this.xr.context.mainCamera?.parent;
237
+ this._rayWorldPosition.copy(this._rayPositionRaw);
238
+ if (parent) this._rayWorldPosition.applyMatrix4(parent.matrixWorld);
239
+ }
240
+
241
+ /** Controller ray rotation in wordspace (this value is calculated once per frame by default - call `updateRayWorldQuaternion` to force an update) */
242
+ get rayWorldQuaternion() {
243
+ return getTempQuaternion(this._rayWorldQuaternion);
244
+ }
245
+ private readonly _rayWorldQuaternion: Quaternion = new Quaternion();
246
+
247
+ get pinchPosition() {
248
+ return getTempVector(this._pinchPosition);
249
+ }
250
+ private readonly _pinchPosition: Vector3 = new Vector3();
251
+
252
+ /** Recalculates the ray world quaternion */
253
+ updateRayWorldQuaternion() {
254
+ const parent = this.xr.context.mainCamera?.parent;
255
+ const parentWorldQuaternion = parent ? getWorldQuaternion(parent) : undefined;
256
+ this._rayWorldQuaternion.copy(this._rayRotationRaw)
257
+ // flip forward because we want +Z to be forward
258
+ .multiply(flipForwardQuaternion);
259
+ if (parentWorldQuaternion) this._rayWorldQuaternion.premultiply(parentWorldQuaternion)
260
+ }
261
+
262
+ /** The controller ray in worldspace */
263
+ get ray(): Ray {
264
+ this._ray.origin.copy(this.rayWorldPosition);
265
+ this._ray.direction.copy(getTempVector(0, 0, 1).applyQuaternion(this.rayWorldQuaternion));
266
+ return this._ray;
267
+ }
268
+ private readonly _ray;
269
+
270
+ /** Recalculated once per update */
271
+ private _hand_wristDotUp: number | undefined = undefined;
272
+ /**
273
+ * The dot product of the hand palm with the up vector.
274
+ * This is a number between -1 and 1, where 1 means the palm is directly up and -1 means the palm is directly down (upside down).
275
+ * This value is undefined if there's no hand
276
+ */
277
+ get handWristDotUp(): number | undefined {
278
+ if (this._hand_wristDotUp !== undefined) return this._hand_wristDotUp;
279
+ const handPalm = this.handObject?.joints["wrist"];
280
+ if (handPalm) {
281
+ const up = getTempVector(0, 1, 0).applyQuaternion(handPalm.quaternion);
282
+ const dot = getTempVector(0, 1, 0).dot(up);
283
+ return this._hand_wristDotUp = dot;
284
+ }
285
+ return undefined;
286
+ }
287
+ /**
288
+ * @returns true if the hand is upside down
289
+ */
290
+ get isHandUpsideDown() {
291
+ return this.handWristDotUp !== undefined ? this.handWristDotUp < -.7 : false;
292
+ }
293
+ /**
294
+ * @returns true if the hand is upside down and we got a pinch down event this frame.
295
+ */
296
+ get isTeleportGesture() {
297
+ return this.isHandUpsideDown && this.getGesture("pinch")?.isDown;
298
+ }
299
+
300
+ /** The controller object space.
301
+ * You can use it to attach objects to the controller.
302
+ * Children will be automatically detached and put into the scene when the controller disconnects
303
+ */
304
+ get object() { return this._object; }
305
+ private readonly _object: IGameObject;
306
+ private readonly _gripSpaceObject?: IGameObject;
307
+ private readonly _raySpaceObject?: IGameObject;
308
+
309
+ /** Assigned the model that you use for rendering. This can be used as a hint for other components */
310
+ model: Object3D | null = null;
311
+
312
+ private readonly _debugAxesHelper = new AxesHelper(.15);
313
+ private readonly _debugGripAxesHelper = new AxesHelper(.07);
314
+ private readonly _debugRayAxesHelper = new AxesHelper(.07);
315
+
316
+ /** returns the URL of the default controller model */
317
+ async getModelUrl(): Promise<string | null> {
318
+ return this.getMotionController?.then(res => res?.assetUrl || null);
319
+ }
320
+
321
+ constructor(session: NeedleXRSession, device: XRInputSource, index: number) {
322
+ this.xr = session;
323
+ this.inputSource = device;
324
+ this.index = index;
325
+ this._object = new Object3D() as unknown as IGameObject;
326
+ this._object.name = `NeedleXRController_${index}`;
327
+
328
+ if (debug) {
329
+ this._object.add(this._debugAxesHelper);
330
+ this._gripSpaceObject = new Object3D() as unknown as IGameObject;
331
+ this._raySpaceObject = new Object3D() as unknown as IGameObject;
332
+ this._gripSpaceObject.name = `NeedleXRController_${index}_gripSpace`;
333
+ this._raySpaceObject.name = `NeedleXRController_${index}_raySpace`;
334
+ this._gripSpaceObject.add(this._debugGripAxesHelper);
335
+ this._raySpaceObject.add(this._debugRayAxesHelper);
336
+ this.xr.context.scene.add(this._gripSpaceObject);
337
+ this.xr.context.scene.add(this._raySpaceObject);
338
+ }
339
+ this.xr.context.scene.add(this._object);
340
+ this._ray = new Ray();
341
+ this.pointerInit = {
342
+ origin: this,
343
+ pointerType: this.hand ? "hand" : "controller",
344
+ deviceIndex: this.index,
345
+ pointerId: -1, // < this will be updated in the emitPointerEvent method
346
+ mode: this.inputSource.targetRayMode,
347
+ ray: this._ray,
348
+ device: this._object,
349
+ buttonName: "none",
350
+ }
351
+ this.initialize();
352
+ this.subscribeEvents();
353
+
354
+ }
355
+
356
+ private _hitTestSourcePromise: Promise<XRTransientInputHitTestSource | null> | null = null;
357
+ private _requestHitTestSource(): Promise<XRTransientInputHitTestSource | null> | null {
358
+ if (this._hitTestSourcePromise) return this._hitTestSourcePromise;
359
+ // We only request a hit test source when we need it - meaning e.g. when we want to place the scene in AR
360
+ // Make sure to cancel the hittest source when we don't need it anymore for performance reasons
361
+
362
+ // // TODO: change this to check if we have hit-testing enabled instead of pass through.
363
+ if (this.xr.mode === "immersive-ar" && this.inputSource.targetRayMode === "tracked-pointer" && this.xr.session.requestHitTestSourceForTransientInput) {
364
+ // request hittest source
365
+ return this._hitTestSourcePromise = this.xr.session.requestHitTestSourceForTransientInput({
366
+ profile: this.inputSource.profiles[0],
367
+ offsetRay: new XRRay(),
368
+ })?.then(hitTestSource => {
369
+ this._hitTestSourcePromise = null;
370
+ if (!this.connected) {
371
+ hitTestSource.cancel();
372
+ return null;
373
+ }
374
+ return this._hitTestSource = hitTestSource;
375
+ }) ?? null;
376
+ }
377
+ return null;
378
+ }
379
+
380
+ onPointerHits = _evt => {
381
+ }
382
+
383
+ onUpdate(frame: XRFrame) {
384
+ performance.mark('NeedleXRController onUpdate start');
385
+ this.onUpdateFrame(frame);
386
+ this.updateInputEvents();
387
+ this.onUpdateMove();
388
+ performance.mark('NeedleXRController onUpdate end');
389
+ performance.measure('NeedleXRController onUpdate', 'NeedleXRController onUpdate start', 'NeedleXRController onUpdate end');
390
+ }
391
+
392
+ onRenderDebug() {
393
+ Gizmos.DrawSphere(this.rayWorldPosition, .003);
394
+ Gizmos.DrawDirection(this.rayWorldPosition, getTempVector(0, 0, 10).applyQuaternion(this.rayWorldQuaternion));
395
+ const labelPosition = this.inputSource.gripSpace ? this.gripWorldPosition : this.object.worldPosition;
396
+ const debugLabelPosition = labelPosition.sub(this.object.worldForward.multiplyScalar(.1));
397
+ const profileStr = this.inputSource.profiles.join("\n");
398
+ let debugStr = `Controller[${this.index}] (${this.inputSource.targetRayMode}, ${this.side})
399
+ C:${this.connected ? "x" : "-"} T:${this.isTracking ? "x" : "-"} Hand:${this.inputSource.hand ? "x" : "-"} Pen: ${this._isMxInk ? "x" : "-"}`;
400
+ if (this.inputSource.hand) debugStr += `\nPinch: ${this.getGesture("pinch")?.value.toFixed(3)}`;
401
+ debugStr += "\n" + profileStr;
402
+ debugStr += "\n" + (this.inputSource.targetRaySpace ? `Ray: x` : "Ray: -") +
403
+ (this.inputSource.gripSpace ? " Grip: x" : " Grip: -") +
404
+ (this.inputSource.gamepad ? ` Gamepad: ${this.inputSource.gamepad.mapping}` : " Gamepad: -");
405
+ if (this.inputSource.gamepad) {
406
+ const gp = this.inputSource.gamepad;
407
+ let gamepadStr = "[btns " + gp.buttons.length + "]: " + gp.buttons.map(b => b.value.toPrecision(1)).join(",");
408
+ gamepadStr += "\n[axes " + gp.axes.length + "]: " + gp.axes.map(a => a.toPrecision(1)).join(",");
409
+ debugStr += "\n" + gamepadStr;
410
+ }
411
+ Gizmos.DrawLabel(debugLabelPosition, debugStr, .006);
412
+ }
413
+
414
+ private onUpdateFrame(frame: XRFrame) {
415
+ // make sure this is cleared every frame
416
+ this._handJointPoses.clear();
417
+ this._hand_wristDotUp = undefined;
418
+
419
+ if (!this.xr.referenceSpace) {
420
+ this._isTracking = false;
421
+ return;
422
+ }
423
+
424
+ const rayPose = frame.getPose(this.inputSource.targetRaySpace, this.xr.referenceSpace);
425
+ this._isTracking = rayPose != null;
426
+ let gripPositionRaw: Vector3 | null = null;
427
+ let gripQuaternionRaw: Quaternion | null = null;
428
+ let rayPositionRaw: Vector3 | null = null;
429
+ let rayQuaternionRaw: Quaternion | null = null;
430
+
431
+ if (rayPose) {
432
+ const t = rayPose.transform;
433
+ this._rayMatrix
434
+ .fromArray(t.matrix)
435
+ .premultiply(flipForwardMatrix);
436
+ this._rayMatrix.decompose(this._rayPosition, this._rayQuaternion, getTempVector(1, 1, 1));
437
+ rayPositionRaw = getTempVector(t.position);
438
+ rayQuaternionRaw = getTempQuaternion(t.orientation);
439
+
440
+ this._rayPositionRaw.copy(rayPositionRaw);
441
+ this._rayRotationRaw.copy(rayQuaternionRaw);
442
+ }
443
+
444
+ if (this.inputSource.gripSpace) {
445
+ const gripPose = frame.getPose(this.inputSource.gripSpace, this.xr.referenceSpace!);
446
+ if (gripPose) {
447
+ const t = gripPose.transform;
448
+ gripPositionRaw = getTempVector(t.position);
449
+ gripQuaternionRaw = getTempQuaternion(t.orientation);
450
+ this._gripMatrix
451
+ .fromArray(t.matrix)
452
+ .premultiply(flipForwardMatrix);
453
+ this._gripMatrix.decompose(this._gripPosition, this._gripQuaternion, getTempVector(1, 1, 1));
454
+
455
+ if ("linearVelocity" in gripPose && gripPose.linearVelocity) {
456
+ const p = gripPose.linearVelocity as DOMPointReadOnly;
457
+ this._linearVelocity.set(p.x, p.y, p.z);
458
+ }
459
+ }
460
+ }
461
+
462
+ // update controller object parent – needs to be parented to the rig, which
463
+ // implicitly is the same object as the camera parent.
464
+ if (this.xr.context.mainCamera?.parent) {
465
+ if (this._object.parent !== this.xr.context.mainCamera?.parent)
466
+ this.xr.context.mainCamera.parent.add(this._object);
467
+ if (this._gripSpaceObject !== undefined && this._gripSpaceObject?.parent !== this.xr.context.mainCamera?.parent)
468
+ this.xr.context.mainCamera.parent.add(this._gripSpaceObject);
469
+ if (this._raySpaceObject !== undefined && this._raySpaceObject?.parent !== this.xr.context.mainCamera?.parent)
470
+ this.xr.context.mainCamera.parent.add(this._raySpaceObject);
471
+ }
472
+ // for controllers, we set the position and rotation of the object to the ray position and rotation
473
+ // for hands, we take the wrist position and rotation
474
+ const hand = this.hand;
475
+ if (hand) {
476
+ // https://www.w3.org/TR/webxr-hand-input-1/#xrhand-interface
477
+ let gotWrist = false;
478
+ // TODO check why types are not correct here
479
+ const wrist = hand.get("wrist");
480
+ const wristPose = wrist && this.getHandJointPose(wrist, frame);
481
+ if (wristPose) {
482
+ gotWrist = true;
483
+ const p = wristPose.transform.position;
484
+ const q = wristPose.transform.orientation;
485
+ this._object.position.set(p.x, p.y, p.z);
486
+ this._object.quaternion.set(q.x, q.y, q.z, q.w).multiply(flipForwardQuaternion);
487
+ }
488
+ if (!gotWrist) {
489
+ this._object.position.copy(this._rayPosition);
490
+ this._object.quaternion.copy(this._rayQuaternion).multiply(flipForwardQuaternion);
491
+ }
492
+
493
+ //@ts-ignore
494
+ const middle = hand.get("middle-finger-metacarpal");
495
+ const middlePose = middle && this.getHandJointPose(middle, frame);
496
+ if (middlePose) {
497
+ // for some reason the grip rotation is different from the wrist rotation
498
+ // but we want to use the wrist rotation for the grip
499
+ this._gripMatrix
500
+ .fromArray(middlePose.transform.matrix)
501
+ .premultiply(flipForwardMatrix);
502
+ this._gripMatrix.decompose(this._gripPosition, this._gripQuaternion, getTempVector(1, 1, 1));
503
+
504
+ // If we don't have a grip space, we update the data from the metacarpal bone instead.
505
+ // this way, things looking for a grip pose will still find one (e.g. XRControllerFollow).
506
+ // For example, hands on VisionOS do not provide a gripSpace.
507
+ if (true || !this.inputSource.gripSpace) {
508
+ gripPositionRaw = getTempVector().copy(middlePose.transform.position);
509
+ gripQuaternionRaw = getTempQuaternion().copy(middlePose.transform.orientation);
510
+ gripQuaternionRaw.multiply(metacarpalToGripQuaternion);
511
+ gripPositionRaw.add(getTempVector(metacarpalToGripPosition).applyQuaternion(gripQuaternionRaw));
512
+ }
513
+ }
514
+ }
515
+ // on VisionOS we get a gripSpace that matches where the controller is for transient input sources
516
+ else if (this.inputSource.gripSpace && this.targetRayMode === "transient-pointer" && gripPositionRaw && gripQuaternionRaw) {
517
+ this._object.position.copy(gripPositionRaw);
518
+ this._object.quaternion.copy(gripQuaternionRaw).multiply(flipForwardQuaternion);
519
+ }
520
+ else if (rayPositionRaw && rayQuaternionRaw) {
521
+ this._object.position.copy(rayPositionRaw);
522
+ this._object.quaternion.copy(rayQuaternionRaw).multiply(flipForwardQuaternion);
523
+ }
524
+
525
+ if (debug) {
526
+ if (rayPositionRaw && rayQuaternionRaw) {
527
+ this._raySpaceObject?.position.copy(rayPositionRaw);
528
+ this._raySpaceObject?.quaternion.copy(rayQuaternionRaw).multiply(flipForwardQuaternion);
529
+ }
530
+ if (gripPositionRaw && gripQuaternionRaw) {
531
+ this._gripSpaceObject?.position.copy(gripPositionRaw);
532
+ this._gripSpaceObject?.quaternion.copy(gripQuaternionRaw).multiply(flipForwardQuaternion);
533
+ }
534
+ }
535
+
536
+ // UPDATE WORLD TRANSFORM DATA
537
+ const parent = this.xr.context.mainCamera?.parent;
538
+ const parentWorldQuaternion = parent ? getWorldQuaternion(parent) : undefined;
539
+
540
+ // GRIP
541
+ if (gripPositionRaw && gripQuaternionRaw) {
542
+ this._gripWorldPosition.copy(gripPositionRaw);
543
+ if (parent) this._gripWorldPosition.applyMatrix4(parent.matrixWorld);
544
+
545
+ this._gripWorldQuaternion.copy(gripQuaternionRaw);
546
+ // flip forward because we want +Z to be forward
547
+ this._gripWorldQuaternion.multiply(flipForwardQuaternion);
548
+ if (parentWorldQuaternion) this._gripWorldQuaternion.premultiply(parentWorldQuaternion)
549
+ }
550
+
551
+ // RAY
552
+ this.updateRayWorldPosition();
553
+ this.updateRayWorldQuaternion();
554
+ }
555
+
556
+ /** Called when the input source disconnects */
557
+ onDisconnected() {
558
+ this._connected = false;
559
+ if (debug) console.warn("Controller disconnected", this.index);
560
+ // move all attached objects into the scene
561
+ for (const child of this._object.children) {
562
+ this.xr.context.scene.attach(child);
563
+ }
564
+ this._object?.removeFromParent();
565
+ this._debugAxesHelper?.removeFromParent();
566
+ this._debugGripAxesHelper?.removeFromParent();
567
+ this._debugRayAxesHelper?.removeFromParent();
568
+ this._gripSpaceObject?.removeFromParent();
569
+ this._raySpaceObject?.removeFromParent();
570
+
571
+ this.unsubscribeEvents();
572
+ if (this._hitTestSource) {
573
+ this._hitTestSource.cancel();
574
+ this._hitTestSource = undefined;
575
+ }
576
+ }
577
+
578
+ /**
579
+ * Get a gamepad button
580
+ * @link https://github.com/immersive-web/webxr-gamepads-module/blob/main/gamepads-module-explainer.md
581
+ * @param key the controller button name e.g. x-button
582
+ * @returns the gamepad button if it exists on the controller - otherwise undefined
583
+ */
584
+ getButton(key: NeedleXRControllerButtonName): NeedleGamepadButton | undefined | null {
585
+ if (!this._layout) return undefined;
586
+
587
+ switch (key) {
588
+ case "primary-button":
589
+ if (this.isLeft) key = "x-button";
590
+ else if (this.isRight) key = "a-button";
591
+ else return undefined;
592
+ break;
593
+ case "primary":
594
+ if (this.hand) {
595
+ return this.getGesture("pinch");
596
+ }
597
+ return this.toNeedleGamepadButton(0, key);
598
+ }
599
+
600
+
601
+ if (this._buttonMap.has(key)) {
602
+ return this.toNeedleGamepadButton(this._buttonMap.get(key)!, key);
603
+ }
604
+ const componentModel = this._layout?.components[key];
605
+ if (componentModel?.gamepadIndices) {
606
+ switch (componentModel.type) {
607
+ case "button":
608
+ case "squeeze":
609
+ if (this.inputSource.gamepad) {
610
+ const index = componentModel.gamepadIndices!.button!;
611
+ this._buttonMap.set(key, index);
612
+ return this.toNeedleGamepadButton(index, key);
613
+ }
614
+ break;
615
+ default:
616
+ console.warn("Unsupported component type", componentModel.type);
617
+ break;
618
+ }
619
+ }
620
+ this._buttonMap.set(key, undefined!);
621
+ return undefined;
622
+ }
623
+
624
+ /** Get a gesture state */
625
+ getGesture(key: XRGestureName): NeedleGamepadButton | null {
626
+ const state = this.states[key];
627
+ if (!state) return null;
628
+ this.states[key] = state;
629
+ const needleButton = this._needleGamepadButtons[key] || new NeedleGamepadButton(undefined, key);
630
+ needleButton.pressed = state.pressed;
631
+ needleButton.value = state.value;
632
+ needleButton.isDown = state.isDown;
633
+ needleButton.isUp = state.isUp;
634
+ this._needleGamepadButtons[key] = needleButton;
635
+ return needleButton;
636
+ }
637
+
638
+ /**
639
+ * Get the pointer id for a specific button of this input device.
640
+ * This is useful if you want to check if a button (e.g. trigger) is currently being in use which can be queried on the inputsystem.
641
+ * @returns the pointer id for the button or undefined if the button is not supported
642
+ * @example
643
+ * ```ts
644
+ * const pointerId = controller.getPointerId("primary");
645
+ * if (pointerId !== undefined) {
646
+ * const isUsed = this.context.input.getPointerUsed(pointerId);
647
+ * console.log(controller.side, "used?", isUsed);
648
+ * }
649
+ * ```
650
+ */
651
+ getPointerId(button: number): number;
652
+ getPointerId(button: NeedleXRControllerButtonName | XRGestureName): number | undefined;
653
+ getPointerId(button: number | NeedleXRControllerButtonName | XRGestureName): number | undefined {
654
+ if (button === "primary") {
655
+ button = 0;
656
+ }
657
+ else if (button === "pinch") {
658
+ button = 0;
659
+ }
660
+ if (typeof button !== "number") {
661
+ const needleButton = this._buttonMap.get(button);
662
+ if (needleButton === undefined) {
663
+ return undefined;
664
+ }
665
+ button = needleButton;
666
+ }
667
+ return this.index * 10 + button;
668
+ }
669
+
670
+ private readonly _needleGamepadButtons: { [key: number | string]: NeedleGamepadButton } = {};
671
+ /** combine the InputState information + the GamepadButton information (since GamepadButtons can not be extended) */
672
+ private toNeedleGamepadButton(index: number, name: string): NeedleGamepadButton | undefined {
673
+ if (!this.inputSource.gamepad?.buttons) return undefined
674
+ const button = this.inputSource.gamepad?.buttons[index];
675
+ const state = this.states[index];
676
+ const needleButton = this._needleGamepadButtons[index] || new NeedleGamepadButton(index, name);
677
+ if (button) {
678
+ needleButton.pressed = button.pressed;
679
+ needleButton.value = button.value;
680
+ needleButton.touched = button.touched;
681
+ }
682
+ if (state) {
683
+ needleButton.isDown = state.isDown;
684
+ needleButton.isUp = state.isUp;
685
+ }
686
+ this._needleGamepadButtons[index] = needleButton;
687
+ return needleButton;
688
+ }
689
+
690
+ /**
691
+ * Get the values of a controller joystick
692
+ * @link https://github.com/immersive-web/webxr-gamepads-module/blob/main/gamepads-module-explainer.md
693
+ * @returns the stick values where x is left/right, y is up/down and z is the button value
694
+ */
695
+ getStick(key: StickName | "primary"): Vec3 {
696
+ if (!this._layout) return { x: 0, y: 0, z: 0 };
697
+
698
+ if (key === "primary") {
699
+ const x = this.inputSource.gamepad?.axes[0] || 0;
700
+ const y = this.inputSource.gamepad?.axes[1] || 0;
701
+ // the primary thumbstick is button 3 (see gamepads module explainer)
702
+ const z = this.inputSource.gamepad?.buttons[3]?.value || 0;
703
+ return { x, y, z }
704
+ }
705
+
706
+ const componentModel = this._layout?.components[key];
707
+ if (componentModel?.gamepadIndices) {
708
+ switch (componentModel.type) {
709
+ case "thumbstick":
710
+ if (this.inputSource.gamepad) {
711
+ const xIndex = componentModel.gamepadIndices!.xAxis!;
712
+ const yIndex = componentModel.gamepadIndices!.yAxis!;
713
+ let x = this.inputSource.gamepad?.axes[xIndex];
714
+ let y = this.inputSource.gamepad?.axes[yIndex];
715
+ x *= -1;
716
+ y *= -1;
717
+ const buttonIndex = componentModel.gamepadIndices!.button!;
718
+ const z = this.inputSource.gamepad?.buttons[buttonIndex]?.value;
719
+ return { x, y, z }
720
+ }
721
+ }
722
+ }
723
+ return { x: 0, y: 0, z: 0 }
724
+ }
725
+
726
+ private readonly _buttonMap = new Map<NeedleXRControllerButtonName, number>();
727
+
728
+ // the motion controller contains the controller scheme, we use this to simplify button access
729
+ private _motioncontroller?: MotionController;
730
+ private _layout: InputDeviceLayout | undefined;
731
+ private getMotionController!: Promise<MotionController>;
732
+ private initialize() {
733
+ // WORKAROUND for hand controllers that don't have a select event
734
+ this._hasSelectEvent = this.profiles.includes("generic-hand-select") || this.profiles.some(p => p.startsWith("generic-trigger"));
735
+
736
+ // Used to determine special layout for Quest controllers, e.g. last button is menu button
737
+ this._isMetaQuestTouchController = this.profiles.includes("meta-quest-touch-plus") || this.profiles.includes("oculus-touch-v3");
738
+
739
+ // Proper profile starting with v69 and browser 35.1
740
+ this._isMxInk = this.profiles.includes("logitech-mx-ink")
741
+
742
+ if (!this._layout) {
743
+ // Ignore transient-pointer since we likely don't want to spawn a controller visual just for a temporary pointer.
744
+ // TODO we should check how this is actually handled on Quest Browser when the transient-pointer flag is on.
745
+ if (this.inputSource.targetRayMode as XRTargetRayMode | "transient-pointer" === "transient-pointer") return;
746
+
747
+ // TODO: we should fetch the profiles or better yet the profile list once and cache it
748
+ const fetchProfileCall = fetchProfile(this.inputSource, DEFAULT_PROFILES_PATH, DEFAULT_PROFILE);
749
+ /** @ts-ignore */
750
+ this.getMotionController = fetchProfileCall.then(res => {
751
+
752
+ if (!this.connected) return null;
753
+
754
+ this._motioncontroller = new MotionController(
755
+ this.inputSource,
756
+ res.profile,
757
+ res.assetPath || ""
758
+ );
759
+
760
+ const profile = res.profile as InputDeviceProfile;
761
+ const layout = profile.layouts[this.inputSource.handedness];
762
+ this._layout = layout;
763
+ if (this._layout) {
764
+ if (!this._layout.gamepad?.length) {
765
+ this._layout.gamepad = [];
766
+ for (const key in this._layout.components) {
767
+ const component = this._layout.components[key];
768
+ this._layout.gamepad[component.gamepadIndices!.button!] = key as XRControllerButtonName;
769
+ }
770
+ }
771
+ }
772
+ // if (debug) console.log(this._layout, this.inputSource);
773
+ // debugger;
774
+ // this.getButton("a-button")
775
+ return this._motioncontroller;
776
+ }).catch(err => {
777
+ if (this.inputSource)
778
+ console.warn("Couldn't initialize motion controller profile for ", this.inputSource, err);
779
+ return null;
780
+ });
781
+ }
782
+ }
783
+
784
+ /**
785
+ * When enabled the controller will automatically emit pointer down events to the Needle Engine Input System.
786
+ * @default true
787
+ */
788
+ emitPointerDownEvent: boolean = true;
789
+
790
+ /**
791
+ * When enabled the controller will automatically emit pointer up events to the Needle Engine Input System.
792
+ * @default true
793
+ */
794
+ emitPointerUpEvent: boolean = true;
795
+
796
+ /**
797
+ * When enabled the controller will automatically emit pointer move events to the Needle Engine Input System.
798
+ * @default true
799
+ */
800
+ emitPointerMoveEvent: boolean = true;
801
+
802
+ /**
803
+ * The distance threshold for pointer move events. This value is in units in rig space
804
+ * @default 0.03
805
+ */
806
+ pointerMoveDistanceThreshold = 0.03;
807
+
808
+ /**
809
+ * The angle threshold for pointer move events. This value is in radians.
810
+ * @default 0.05
811
+ */
812
+ pointerMoveAngleThreshold = 0.05;
813
+
814
+ private subscribeEvents() {
815
+ // https://developer.mozilla.org/en-US/docs/Web/API/XRSession/selectstart_event
816
+ this.xr.session.addEventListener("selectstart", this.onSelectStart);
817
+ this.xr.session.addEventListener("selectend", this.onSelectEnd);
818
+ // https://developer.mozilla.org/en-US/docs/Web/API/XRSession/squeeze_event
819
+ this.xr.session.addEventListener("squeezestart", this.onSequeezeStart);
820
+ this.xr.session.addEventListener("squeezeend", this.onSequeezeEnd);
821
+ }
822
+ private unsubscribeEvents() {
823
+ this.xr.session.removeEventListener("selectstart", this.onSelectStart);
824
+ this.xr.session.removeEventListener("selectend", this.onSelectEnd);
825
+ this.xr.session.removeEventListener("squeezestart", this.onSequeezeStart);
826
+ this.xr.session.removeEventListener("squeezeend", this.onSequeezeEnd);
827
+ }
828
+
829
+ private _selectButtonIndex: number | undefined = undefined;
830
+ private _squeezeButtonIndex: number | undefined = undefined;
831
+
832
+ private onSelectStart = (evt: XRInputSourceEvent) => {
833
+ if (!this.emitPointerDownEvent) return;
834
+ if (this.inputSource !== evt.inputSource) return;
835
+ // if a selectstart event happens right after an input source is connected, we may even receive this event before
836
+ // requestAnimationFrame callback with the current session. So, we need to update the frame here.
837
+ this.onUpdateFrame(evt.frame);
838
+ // if we receive a select event we can be true that this device supports select events
839
+ this._hasSelectEvent = true;
840
+ const selectComponentId = this._layout?.selectComponentId;
841
+ const i = this._layout?.components[selectComponentId!]?.gamepadIndices?.button;
842
+ if (i !== undefined) this._selectButtonIndex = i;
843
+ if (debugCustomGesture) return;
844
+ /*
845
+ if (!_didReceiveSelectStartEvent) {
846
+ _didReceiveSelectStartEvent = true;
847
+ // safeguard first pinch event - check if the pinch gesture is already down
848
+ const pinch = this.getGesture("pinch");
849
+ if (pinch?.pressed) {
850
+ console.warn("Select start event was received but the pinch gesture is already down. This might happen the first time you start pinching", this.index, this.side);
851
+ return;
852
+ }
853
+ }
854
+ */
855
+ if (debug) Gizmos.DrawDirection(this.rayWorldPosition, getTempVector(0, .01, 1).applyQuaternion(this.rayWorldQuaternion), 0xff0000, 10);
856
+ this.emitPointerEvent(InputEvents.PointerDown, this._selectButtonIndex || 0, "xr-standard-trigger", true, evt);
857
+ }
858
+ private onSelectEnd = (evt: XRInputSourceEvent) => {
859
+ if (!this.emitPointerUpEvent) return;
860
+ if (debugCustomGesture) return;
861
+ if (this.inputSource !== evt.inputSource) return;
862
+ this.emitPointerEvent(InputEvents.PointerUp, this._selectButtonIndex || 0, "xr-standard-trigger", true, evt);
863
+ }
864
+ private onSequeezeStart = (evt: XRInputSourceEvent) => {
865
+ if (!this.emitPointerDownEvent) return;
866
+ if (this.inputSource !== evt.inputSource) return;
867
+ this._squeezeButtonIndex = this._layout?.components["xr-standard-squeeze"]?.gamepadIndices?.button;
868
+ if (this._squeezeButtonIndex !== undefined) {
869
+ if (debug) Gizmos.DrawDirection(this.rayWorldPosition, getTempVector(0, .01, 1).applyQuaternion(this.rayWorldQuaternion), 0x0000ff, 10);
870
+ this.emitPointerEvent(InputEvents.PointerDown, this._squeezeButtonIndex || 0, "xr-standard-squeeze", true, evt);
871
+ }
872
+ };
873
+ private onSequeezeEnd = (evt: XRInputSourceEvent) => {
874
+ if (!this.emitPointerUpEvent) return;
875
+ if (this.inputSource !== evt.inputSource) return;
876
+ if (this._squeezeButtonIndex !== undefined)
877
+ this.emitPointerEvent(InputEvents.PointerUp, this._squeezeButtonIndex || 0, "xr-standard-squeeze", true, evt);
878
+ };
879
+
880
+ /** Index = button index */
881
+ private readonly states: { [key: number | string]: InputState } = {};
882
+ // If we want to invoke button events for ALL buttons we need to keep track of the previous state
883
+ // instead of using XR input select start events which is only raised for the primary button
884
+ // we should probably do both but then we need to ignore the primary index in the following function (to not raise an event for the same button twice)
885
+ // and start with index = 1
886
+ private updateInputEvents() {
887
+ // https://immersive-web.github.io/webxr-gamepads-module/#xr-standard-heading
888
+ if (this.gamepad?.buttons) {
889
+ for (let index = 0; index < this.gamepad.buttons.length; index++) {
890
+ const button = this.gamepad.buttons[index];
891
+ const state = this.states[index] || new InputState();
892
+ let eventName: InputEventNames | null = null;
893
+
894
+ // Special handling for MX Ink stylus on Quest OS v69+.
895
+ // We're never getting a "pressed" state here, so we determine pressed state based on the value.
896
+ if (this._isMxInk && (index === 4 || index === 5)) {
897
+ if (button.value > 0 && !state.pressed) {
898
+ eventName = "pointerdown";
899
+ state.isDown = true;
900
+ state.isUp = false;
901
+ }
902
+ else if (button.value === 0 && state.pressed) {
903
+ eventName = "pointerup";
904
+ state.isDown = false;
905
+ state.isUp = true;
906
+ }
907
+ else if (state.pressed) {
908
+ eventName = "pointermove";
909
+ state.isDown = false;
910
+ state.isUp = false;
911
+ }
912
+ state.pressed = button.value > 0;
913
+ state.value = button.value;
914
+ }
915
+ // Regular controller handling.
916
+ else {
917
+ // is down
918
+ if (button.pressed && !state.pressed) {
919
+ eventName = "pointerdown";
920
+ state.isDown = true;
921
+ state.isUp = false;
922
+ }
923
+ // is up
924
+ else if (!button.pressed && state.pressed) {
925
+ eventName = "pointerup"
926
+ state.isDown = false;
927
+ state.isUp = true;
928
+ }
929
+ else {
930
+ state.isDown = false;
931
+ state.isUp = false;
932
+ }
933
+ state.pressed = button.pressed;
934
+ state.value = button.value;
935
+ }
936
+ this.states[index] = state;
937
+
938
+ // the selection event is handled in the "selectstart" callback
939
+ const emitEvent = index !== this._selectButtonIndex && index !== this._squeezeButtonIndex;
940
+
941
+ if (eventName != null && emitEvent) {
942
+ let name = this._layout?.gamepad[index];
943
+ if (this._isMxInk && index === 4) name = "stylus-touch";
944
+ if (this._isMxInk && index === 5) name = "stylus-tip";
945
+ if (debug || debugCustomGesture) console.log("Emitting pointer event", eventName, index, name, button.value, this.gamepad, this._layout);
946
+ this.emitPointerEvent(eventName, index, name ?? "none", false, null, button.value);
947
+ }
948
+ }
949
+
950
+ // For Quest controllers, the last button is the menu button
951
+ if (this._isMetaQuestTouchController) {
952
+ const menuButtonIndex = this.gamepad.buttons.length - 1;
953
+ const menuButtonState = this.states[menuButtonIndex];
954
+ if (menuButtonState) {
955
+ if (menuButtonState.isDown) {
956
+ const menu = this.context.menu;
957
+ if (menu.spatialMenuIsVisible)
958
+ menu.setSpatialMenuVisible(false);
959
+ else
960
+ this.context.menu.setSpatialMenuVisible(true);
961
+ }
962
+ }
963
+ }
964
+ }
965
+
966
+ // update hand gesture states
967
+ if (this.hand) {
968
+ const handObject = this.handObject;
969
+ if (handObject) {
970
+ // update pinch state
971
+ const indexTip = handObject.joints["index-finger-tip"];
972
+ const thumbTip = handObject.joints["thumb-tip"];
973
+ if (indexTip && thumbTip) {
974
+ const distance = indexTip.position.distanceTo(thumbTip.position);
975
+
976
+ // upddate position of the pinch point
977
+ this._pinchPosition.lerpVectors(indexTip.position, thumbTip.position, .5);
978
+ const parent = this.xr.context.mainCamera?.parent;
979
+ if (parent) this._pinchPosition.applyMatrix4(parent.matrixWorld);
980
+
981
+ if (distance !== 0) { // ignore exactly 0 which happens when we switch from hands to controllers
982
+ const pinchThreshold = .02;
983
+ const pinchHysteresis = .01;
984
+ const state = this.states["pinch"] || new InputState();
985
+ const maxDistance = (pinchThreshold + pinchHysteresis) * 1.5;
986
+ state.value = 1 - ((distance - pinchThreshold) / maxDistance);
987
+
988
+ const isPressed = distance < (pinchThreshold - pinchHysteresis);
989
+ const isReleased = distance > (pinchThreshold + pinchHysteresis);
990
+ if (isPressed && !state.pressed) {
991
+ if (debugCustomGesture) console.log("pinch start", distance);
992
+ state.isDown = true;
993
+ state.isUp = false;
994
+ state.pressed = true;
995
+ }
996
+ else if (isReleased && state.pressed) {
997
+ state.isDown = false;
998
+ state.isUp = true;
999
+ state.pressed = false;
1000
+ }
1001
+ else {
1002
+ state.isDown = false;
1003
+ state.isUp = false;
1004
+ }
1005
+ this.states["pinch"] = state;
1006
+ }
1007
+
1008
+ /** Workaround for visionOS not emitting selectstart. See https://linear.app/needle/issue/NE-4212
1009
+ * If a selectstart event was never received we do a manual check here if the user is pinching
1010
+ * Update: VisionOS 1.1 now properly emits select events from transient input sources, based on gaze.
1011
+ * We're keeping this code commented for now since there may be future changes before VisionOS WebXR ships.
1012
+ */
1013
+ /*
1014
+ if (!_didReceiveSelectStartEvent && (state.isDown || state.isUp)) {
1015
+ const eventName = isPressed ? "pointerdown" : "pointerup";
1016
+ const pressure = distance / pinchThreshold;
1017
+ if (debugCustomGesture) {
1018
+ const p = this.rayWorldPosition.add(this.object.worldForward.multiplyScalar(.2));
1019
+ p.y += .05;
1020
+ p.y += Math.random() * .02;
1021
+ Gizmos.DrawLabel(p, "pinch:" + eventName + ", " + this.index + ", " + this.side + "\n" + handObject.uuid, 0.01, 5, 0x000000, new RGBAColor(1, 1, 1, .1));
1022
+ }
1023
+ this.emitPointerEvent(eventName, 0, "pinch", false, null, pressure);
1024
+ }
1025
+ */
1026
+ }
1027
+ }
1028
+ }
1029
+ }
1030
+
1031
+ private _didMoveLastFrame = false;
1032
+ private readonly _lastPointerMovePosition = new Vector3();
1033
+ private readonly _lastPointerMoveQuaternion = new Quaternion();
1034
+
1035
+ private onUpdateMove() {
1036
+ if (!this.emitPointerMoveEvent) return;
1037
+ let didMove = false;
1038
+ const dist = this._lastPointerMovePosition.distanceTo(this.gripWorldPosition);
1039
+ if (dist > this.pointerMoveDistanceThreshold * this.xr.rigScale) didMove = true;
1040
+ if (!didMove) {
1041
+ const angle = this._lastPointerMoveQuaternion.angleTo(this.gripWorldQuaternion);
1042
+ if (angle > this.pointerMoveAngleThreshold) didMove = true;
1043
+ }
1044
+ if (didMove) {
1045
+ this._didMoveLastFrame = true;
1046
+ this._lastPointerMovePosition.copy(this.gripWorldPosition);
1047
+ this._lastPointerMoveQuaternion.copy(this.gripWorldQuaternion);
1048
+ if (debug) Gizmos.DrawLabel(this.rayWorldPosition.add(this.object.worldForward.multiplyScalar(.1)), "move", .01);
1049
+
1050
+ let button = this.xr.context.input.getFirstPressedButtonForPointer(this.index);
1051
+ if (button === undefined) button = 0;
1052
+ const pressure = this.gamepad?.buttons[button]?.value;
1053
+ this.emitPointerEvent("pointermove", button, "none", false, null, pressure);
1054
+ }
1055
+ else {
1056
+ this._didMoveLastFrame = false;
1057
+ }
1058
+ }
1059
+
1060
+
1061
+ /** cached spatial pointer init object. We re-use it to not have */
1062
+ private readonly pointerInit: NEPointerEventInit;
1063
+ private emitPointerEvent(type: InputEventNames, button: number, buttonName: ButtonName | "none", primary: boolean, source: Event | null = null, pressure?: number) {
1064
+
1065
+ if (!this.emitEvents) {
1066
+ if (debug && type !== InputEvents.PointerMove) console.warn("Pointer events are disabled for this controller", this.index, type, button);
1067
+ return;
1068
+ }
1069
+
1070
+ // Currently we do only want to emit pointer events for NON screen based events
1071
+ // that means if the input device is spatial (AR touch on a screen should be handled via touchdown etc still)
1072
+ // Not sure if *this* is enough to determine if the event is spatial or not
1073
+ if (this.xr.mode === "immersive-vr" || this.xr.isPassThrough) {
1074
+ this.pointerInit.origin = this;
1075
+ this.pointerInit.pointerId = this.getPointerId(button);
1076
+ this.pointerInit.pointerType = this.hand ? "hand" : "controller";
1077
+ this.pointerInit.button = button;
1078
+ this.pointerInit.buttonName = buttonName;
1079
+ this.pointerInit.isPrimary = primary;
1080
+ this.pointerInit.mode = this.inputSource.targetRayMode;
1081
+ this.pointerInit.ray = this.ray;
1082
+ this.pointerInit.device = this.object;
1083
+ this.pointerInit.pressure = pressure;
1084
+ this.pointerInit.clientX = this._rayPosition.x / this.xr.rigScale;
1085
+ this.pointerInit.clientY = this._rayPosition.y / this.xr.rigScale;
1086
+ this.pointerInit.clientZ = this._rayPosition.z / this.xr.rigScale;
1087
+
1088
+ const prevContext = Context.Current;
1089
+ Context.Current = this.xr.context;
1090
+ if (debug && type !== "pointermove") console.warn("Pointer event", type, button, buttonName, { ...this.pointerInit });
1091
+ this.xr.context.input.createInputEvent(new NEPointerEvent(type, source, this.pointerInit));
1092
+ Context.Current = prevContext;
1093
+ }
1094
+ }
1095
+
1096
+
1097
+ }
1098
+
1099
+ class InputState {
1100
+ /** if the button was pressed the last update */
1101
+ isDown: boolean = false;
1102
+ /** if the button was released the last update */
1103
+ isUp: boolean = false;
1104
+
1105
+ pressed: boolean = false;
1106
+ value: number = 0;
1107
+ };
1108
+
1109
+ /** Enhanced GamepadButton with `isDown` and `isUp` information */
1110
+ class NeedleGamepadButton {
1111
+ /** The index of the button in the input gamepad */
1112
+ readonly index: number | undefined;
1113
+ readonly name: string;
1114
+
1115
+ touched: boolean = false;
1116
+ pressed: boolean = false;
1117
+ value: number = 0;
1118
+ /** was the button just pressed down the last update */
1119
+ isDown: boolean = false;
1120
+ /** was the button just released the last update */
1121
+ isUp: boolean = false;
1122
+
1123
+ constructor(index: number | undefined, name: string) {
1124
+ this.index = index;
1125
+ this.name = name;
1126
+ }
1127
1127
  }