@needle-tools/engine 4.4.0-beta → 4.4.0-beta.1

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 (1045) 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/config.cjs +31 -31
  652. package/plugins/common/config.js +35 -35
  653. package/plugins/common/files.js +31 -31
  654. package/plugins/common/generator.js +10 -10
  655. package/plugins/common/license.js +249 -224
  656. package/plugins/common/npm.js +15 -15
  657. package/plugins/common/timers.js +7 -7
  658. package/plugins/common/version.js +37 -37
  659. package/plugins/gltf-packer.mjs +1 -1
  660. package/plugins/next/alias.cjs +39 -39
  661. package/plugins/next/license.cjs +18 -18
  662. package/plugins/next/meshbvhworker.cjs +18 -18
  663. package/plugins/next/next.js +128 -128
  664. package/plugins/types/index.d.ts +2 -2
  665. package/plugins/types/license.d.ts +24 -24
  666. package/plugins/types/needleConfig.d.ts +27 -27
  667. package/plugins/types/userconfig.d.ts +105 -105
  668. package/plugins/types/webmanifest.d.ts +32 -32
  669. package/plugins/vite/alias.js +174 -174
  670. package/plugins/vite/asap.js +245 -245
  671. package/plugins/vite/build-pipeline.js +354 -354
  672. package/plugins/vite/build.js +19 -19
  673. package/plugins/vite/buildinfo.js +41 -41
  674. package/plugins/vite/config.js +106 -106
  675. package/plugins/vite/copyfiles.js +138 -138
  676. package/plugins/vite/defines.js +66 -66
  677. package/plugins/vite/dependencies.js +213 -213
  678. package/plugins/vite/dependency-watcher.js +231 -231
  679. package/plugins/vite/drop-client.js +76 -76
  680. package/plugins/vite/drop.js +87 -87
  681. package/plugins/vite/editor-connection.js +124 -124
  682. package/plugins/vite/facebook-instant-games.js +99 -99
  683. package/plugins/vite/gzip.js +5 -5
  684. package/plugins/vite/imports-logger.js +143 -143
  685. package/plugins/vite/index.js +139 -139
  686. package/plugins/vite/license.js +55 -55
  687. package/plugins/vite/meta.js +163 -163
  688. package/plugins/vite/npm.js +7 -7
  689. package/plugins/vite/peer.js +29 -29
  690. package/plugins/vite/poster-client.js +73 -73
  691. package/plugins/vite/poster.js +79 -79
  692. package/plugins/vite/pwa.js +604 -604
  693. package/plugins/vite/reload-client.js +15 -15
  694. package/plugins/vite/reload.js +363 -363
  695. package/plugins/vite/server.js +66 -66
  696. package/plugins/vite/transform-codegen.js +55 -55
  697. package/plugins/vite/transform.js +31 -31
  698. package/plugins/vite/vite-4.4-hack.js +31 -31
  699. package/src/asap/needle-asap.ts +111 -111
  700. package/src/asap/sessiongranted.ts +75 -75
  701. package/src/asap/utils.ts +4 -4
  702. package/src/engine/analytics/index.ts +10 -10
  703. package/src/engine/analytics/lcp.ts +35 -35
  704. package/src/engine/api.ts +81 -81
  705. package/src/engine/assets/index.ts +59 -59
  706. package/src/engine/assets/static.js +5 -5
  707. package/src/engine/codegen/register_types.ts +298 -298
  708. package/src/engine/debug/debug.ts +51 -51
  709. package/src/engine/debug/debug_console.ts +333 -333
  710. package/src/engine/debug/debug_overlay.ts +332 -332
  711. package/src/engine/debug/debug_spatial_console.ts +429 -429
  712. package/src/engine/debug/index.ts +1 -1
  713. package/src/engine/engine_addressables.ts +679 -679
  714. package/src/engine/engine_animation.ts +145 -145
  715. package/src/engine/engine_application.ts +113 -113
  716. package/src/engine/engine_assetdatabase.ts +387 -387
  717. package/src/engine/engine_audio.ts +24 -24
  718. package/src/engine/engine_camera.ts +39 -39
  719. package/src/engine/engine_components.ts +373 -373
  720. package/src/engine/engine_components_internal.ts +40 -40
  721. package/src/engine/engine_constants.ts +52 -52
  722. package/src/engine/engine_context.ts +1662 -1662
  723. package/src/engine/engine_context_registry.ts +129 -129
  724. package/src/engine/engine_coroutine.ts +54 -54
  725. package/src/engine/engine_create_objects.ts +398 -398
  726. package/src/engine/engine_default_parameters.ts +3 -3
  727. package/src/engine/engine_editor-sync.ts +28 -28
  728. package/src/engine/engine_element.ts +871 -871
  729. package/src/engine/engine_element_attributes.ts +85 -85
  730. package/src/engine/engine_element_extras.ts +16 -16
  731. package/src/engine/engine_element_loading.ts +381 -381
  732. package/src/engine/engine_element_overlay.ts +186 -186
  733. package/src/engine/engine_fileloader.js +9 -9
  734. package/src/engine/engine_gameobject.ts +684 -684
  735. package/src/engine/engine_generic_utils.js +13 -13
  736. package/src/engine/engine_gizmos.ts +577 -577
  737. package/src/engine/engine_gltf.ts +29 -29
  738. package/src/engine/engine_gltf_builtin_components.ts +403 -403
  739. package/src/engine/engine_hot_reload.ts +198 -198
  740. package/src/engine/engine_input.ts +1460 -1460
  741. package/src/engine/engine_input_utils.ts +23 -23
  742. package/src/engine/engine_instancing.ts +45 -45
  743. package/src/engine/engine_license.ts +329 -329
  744. package/src/engine/engine_lifecycle_api.ts +106 -106
  745. package/src/engine/engine_lifecycle_functions_internal.ts +193 -193
  746. package/src/engine/engine_lightdata.ts +117 -117
  747. package/src/engine/engine_loaders.ts +82 -82
  748. package/src/engine/engine_lods.ts +168 -168
  749. package/src/engine/engine_mainloop_utils.ts +472 -472
  750. package/src/engine/engine_math.ts +282 -282
  751. package/src/engine/engine_modules.ts +83 -83
  752. package/src/engine/engine_networking.ts +841 -841
  753. package/src/engine/engine_networking_auto.ts +352 -352
  754. package/src/engine/engine_networking_blob.ts +254 -254
  755. package/src/engine/engine_networking_files.ts +217 -217
  756. package/src/engine/engine_networking_files_default_components.ts +58 -58
  757. package/src/engine/engine_networking_instantiate.ts +419 -419
  758. package/src/engine/engine_networking_peer.ts +159 -159
  759. package/src/engine/engine_networking_streams.ts +713 -713
  760. package/src/engine/engine_networking_types.ts +24 -24
  761. package/src/engine/engine_networking_utils.ts +23 -23
  762. package/src/engine/engine_networking_websocket.ts +2 -2
  763. package/src/engine/engine_patcher.ts +199 -199
  764. package/src/engine/engine_physics.ts +783 -783
  765. package/src/engine/engine_physics.types.ts +46 -46
  766. package/src/engine/engine_physics_rapier.ts +1579 -1579
  767. package/src/engine/engine_playerview.ts +80 -80
  768. package/src/engine/engine_scenelighting.ts +294 -294
  769. package/src/engine/engine_scenetools.ts +365 -365
  770. package/src/engine/engine_serialization.ts +2 -2
  771. package/src/engine/engine_serialization_builtin_serializer.ts +473 -473
  772. package/src/engine/engine_serialization_core.ts +705 -705
  773. package/src/engine/engine_serialization_decorator.ts +80 -80
  774. package/src/engine/engine_setup.ts +1 -1
  775. package/src/engine/engine_shaders.ts +267 -267
  776. package/src/engine/engine_shims.ts +32 -32
  777. package/src/engine/engine_test_utils.ts +109 -109
  778. package/src/engine/engine_texture.ts +82 -82
  779. package/src/engine/engine_three_utils.ts +849 -849
  780. package/src/engine/engine_time.ts +94 -94
  781. package/src/engine/engine_time_utils.ts +237 -237
  782. package/src/engine/engine_tonemapping.ts +210 -210
  783. package/src/engine/engine_types.ts +714 -714
  784. package/src/engine/engine_typestore.ts +63 -63
  785. package/src/engine/engine_util_decorator.ts +136 -136
  786. package/src/engine/engine_utils.ts +972 -972
  787. package/src/engine/engine_utils_format.ts +212 -212
  788. package/src/engine/engine_utils_screenshot.ts +698 -698
  789. package/src/engine/engine_utils_screenshot.xr.ts +103 -103
  790. package/src/engine/export/gltf/Writers.ts +34 -34
  791. package/src/engine/export/gltf/index.ts +158 -158
  792. package/src/engine/export/index.ts +2 -2
  793. package/src/engine/export/state.ts +19 -19
  794. package/src/engine/export/utils.ts +9 -9
  795. package/src/engine/extensions/EXT_texture_exr.ts +50 -50
  796. package/src/engine/extensions/NEEDLE_animator_controller_model.ts +195 -195
  797. package/src/engine/extensions/NEEDLE_components.ts +268 -268
  798. package/src/engine/extensions/NEEDLE_gameobject_data.ts +81 -81
  799. package/src/engine/extensions/NEEDLE_lighting_settings.ts +185 -185
  800. package/src/engine/extensions/NEEDLE_lightmaps.ts +119 -119
  801. package/src/engine/extensions/NEEDLE_persistent_assets.ts +76 -76
  802. package/src/engine/extensions/NEEDLE_render_objects.ts +209 -209
  803. package/src/engine/extensions/NEEDLE_techniques_webgl.ts +640 -640
  804. package/src/engine/extensions/extension_resolver.ts +4 -4
  805. package/src/engine/extensions/extension_utils.ts +166 -166
  806. package/src/engine/extensions/extensions.ts +135 -135
  807. package/src/engine/extensions/index.ts +5 -5
  808. package/src/engine/extensions/usage_tracker.ts +100 -100
  809. package/src/engine/js-extensions/Camera.ts +37 -37
  810. package/src/engine/js-extensions/ExtensionUtils.ts +85 -85
  811. package/src/engine/js-extensions/Layers.ts +23 -23
  812. package/src/engine/js-extensions/Object3D.ts +296 -296
  813. package/src/engine/js-extensions/RGBAColor.ts +75 -75
  814. package/src/engine/js-extensions/Vector.ts +18 -18
  815. package/src/engine/js-extensions/index.ts +4 -4
  816. package/src/engine/physics/workers/mesh-bvh/GenerateMeshBVHWorker.js +122 -122
  817. package/src/engine/shaders/shaderData.ts +67 -67
  818. package/src/engine/tests/test_utils.ts +63 -63
  819. package/src/engine/webcomponents/WebXRButtons.ts +260 -260
  820. package/src/engine/webcomponents/api.ts +6 -6
  821. package/src/engine/webcomponents/buttons.ts +262 -262
  822. package/src/engine/webcomponents/fonts.ts +41 -41
  823. package/src/engine/webcomponents/icons.ts +57 -57
  824. package/src/engine/webcomponents/index.ts +1 -1
  825. package/src/engine/webcomponents/logo-element.ts +78 -78
  826. package/src/engine/webcomponents/needle menu/needle-menu-spatial.ts +573 -573
  827. package/src/engine/webcomponents/needle menu/needle-menu.ts +1107 -1107
  828. package/src/engine/webcomponents/needle-button.ts +181 -181
  829. package/src/engine/xr/NeedleXRController.ts +1126 -1126
  830. package/src/engine/xr/NeedleXRSession.ts +1585 -1585
  831. package/src/engine/xr/NeedleXRSync.ts +220 -220
  832. package/src/engine/xr/SceneTransition.ts +78 -78
  833. package/src/engine/xr/TempXRContext.ts +216 -216
  834. package/src/engine/xr/XRRig.ts +9 -9
  835. package/src/engine/xr/api.ts +5 -5
  836. package/src/engine/xr/events.ts +102 -102
  837. package/src/engine/xr/internal.ts +34 -34
  838. package/src/engine/xr/usdz.ts +30 -30
  839. package/src/engine/xr/utils.ts +39 -39
  840. package/src/engine-components/AlignmentConstraint.ts +36 -36
  841. package/src/engine-components/Animation.ts +557 -557
  842. package/src/engine-components/AnimationCurve.ts +150 -150
  843. package/src/engine-components/AnimationUtils.ts +28 -28
  844. package/src/engine-components/AnimationUtilsAutoplay.ts +43 -43
  845. package/src/engine-components/Animator.ts +397 -397
  846. package/src/engine-components/AnimatorController.ts +1293 -1293
  847. package/src/engine-components/AudioListener.ts +92 -92
  848. package/src/engine-components/AudioSource.ts +639 -639
  849. package/src/engine-components/AvatarLoader.ts +263 -263
  850. package/src/engine-components/AxesHelper.ts +59 -59
  851. package/src/engine-components/BasicIKConstraint.ts +54 -54
  852. package/src/engine-components/BoxCollider.ts +1 -1
  853. package/src/engine-components/BoxHelperComponent.ts +114 -114
  854. package/src/engine-components/Camera.ts +707 -707
  855. package/src/engine-components/CameraUtils.ts +144 -144
  856. package/src/engine-components/CharacterController.ts +253 -253
  857. package/src/engine-components/Collider.ts +374 -374
  858. package/src/engine-components/Component.ts +1294 -1294
  859. package/src/engine-components/ContactShadows.ts +482 -482
  860. package/src/engine-components/DeleteBox.ts +62 -62
  861. package/src/engine-components/DeviceFlag.ts +46 -46
  862. package/src/engine-components/DragControls.ts +1623 -1623
  863. package/src/engine-components/DropListener.ts +698 -698
  864. package/src/engine-components/Duplicatable.ts +198 -198
  865. package/src/engine-components/EventList.ts +266 -266
  866. package/src/engine-components/EventTrigger.ts +74 -74
  867. package/src/engine-components/EventType.ts +22 -22
  868. package/src/engine-components/Fog.ts +60 -60
  869. package/src/engine-components/Gizmos.ts +56 -56
  870. package/src/engine-components/GridHelper.ts +48 -48
  871. package/src/engine-components/GroundProjection.ts +356 -356
  872. package/src/engine-components/Interactable.ts +14 -14
  873. package/src/engine-components/Joints.ts +52 -52
  874. package/src/engine-components/LODGroup.ts +153 -153
  875. package/src/engine-components/Light.ts +558 -558
  876. package/src/engine-components/LookAtConstraint.ts +25 -25
  877. package/src/engine-components/NeedleMenu.ts +84 -84
  878. package/src/engine-components/NestedGltf.ts +86 -86
  879. package/src/engine-components/Networking.ts +114 -114
  880. package/src/engine-components/OffsetConstraint.ts +60 -60
  881. package/src/engine-components/OrbitControls.ts +1068 -1068
  882. package/src/engine-components/PlayerColor.ts +103 -103
  883. package/src/engine-components/ReflectionProbe.ts +209 -209
  884. package/src/engine-components/Renderer.ts +900 -900
  885. package/src/engine-components/RendererInstancing.ts +855 -855
  886. package/src/engine-components/RendererLightmap.ts +198 -198
  887. package/src/engine-components/RigidBody.ts +526 -526
  888. package/src/engine-components/SceneSwitcher.ts +1013 -1013
  889. package/src/engine-components/ScreenCapture.ts +592 -592
  890. package/src/engine-components/ShadowCatcher.ts +172 -172
  891. package/src/engine-components/Skybox.ts +455 -455
  892. package/src/engine-components/SmoothFollow.ts +76 -76
  893. package/src/engine-components/SpatialTrigger.ts +229 -229
  894. package/src/engine-components/SpectatorCamera.ts +787 -787
  895. package/src/engine-components/SphereCollider.ts +1 -1
  896. package/src/engine-components/SpriteRenderer.ts +468 -468
  897. package/src/engine-components/SyncedCamera.ts +220 -220
  898. package/src/engine-components/SyncedRoom.ts +380 -380
  899. package/src/engine-components/SyncedTransform.ts +378 -378
  900. package/src/engine-components/TestRunner.ts +118 -118
  901. package/src/engine-components/TransformGizmo.ts +219 -219
  902. package/src/engine-components/VideoPlayer.ts +1025 -1025
  903. package/src/engine-components/Voip.ts +363 -363
  904. package/src/engine-components/api.ts +60 -60
  905. package/src/engine-components/avatar/AvatarBlink_Simple.ts +69 -69
  906. package/src/engine-components/avatar/AvatarEyeLook_Rotation.ts +63 -63
  907. package/src/engine-components/avatar/Avatar_Brain_LookAt.ts +139 -139
  908. package/src/engine-components/avatar/Avatar_MouthShapes.ts +83 -83
  909. package/src/engine-components/avatar/Avatar_MustacheShake.ts +31 -31
  910. package/src/engine-components/codegen/components.ts +216 -216
  911. package/src/engine-components/debug/LogStats.ts +21 -21
  912. package/src/engine-components/export/gltf/GltfExport.ts +265 -265
  913. package/src/engine-components/export/usdz/Extension.ts +24 -24
  914. package/src/engine-components/export/usdz/ThreeUSDZExporter.ts +2413 -2413
  915. package/src/engine-components/export/usdz/USDZExporter.ts +705 -705
  916. package/src/engine-components/export/usdz/extensions/Animation.ts +1204 -1204
  917. package/src/engine-components/export/usdz/extensions/DocumentExtension.ts +9 -9
  918. package/src/engine-components/export/usdz/extensions/USDZText.ts +240 -240
  919. package/src/engine-components/export/usdz/extensions/USDZUI.ts +189 -189
  920. package/src/engine-components/export/usdz/extensions/behavior/Actions.ts +99 -99
  921. package/src/engine-components/export/usdz/extensions/behavior/AudioExtension.ts +102 -102
  922. package/src/engine-components/export/usdz/extensions/behavior/Behaviour.ts +320 -320
  923. package/src/engine-components/export/usdz/extensions/behavior/BehaviourComponents.ts +1225 -1225
  924. package/src/engine-components/export/usdz/extensions/behavior/BehavioursBuilder.ts +646 -646
  925. package/src/engine-components/export/usdz/extensions/behavior/PhysicsExtension.ts +132 -132
  926. package/src/engine-components/export/usdz/index.ts +2 -2
  927. package/src/engine-components/export/usdz/utils/animationutils.ts +191 -191
  928. package/src/engine-components/export/usdz/utils/quicklook.ts +50 -50
  929. package/src/engine-components/particlesystem/ParticleSystem.ts +1287 -1287
  930. package/src/engine-components/particlesystem/ParticleSystemModules.ts +1765 -1765
  931. package/src/engine-components/particlesystem/ParticleSystemSubEmitter.ts +111 -111
  932. package/src/engine-components/particlesystem/api.ts +1 -1
  933. package/src/engine-components/postprocessing/Effects/Antialiasing.ts +55 -55
  934. package/src/engine-components/postprocessing/Effects/BloomEffect.ts +116 -116
  935. package/src/engine-components/postprocessing/Effects/ChromaticAberration.ts +37 -37
  936. package/src/engine-components/postprocessing/Effects/ColorAdjustments.ts +95 -95
  937. package/src/engine-components/postprocessing/Effects/DepthOfField.ts +103 -103
  938. package/src/engine-components/postprocessing/Effects/EffectWrapper.ts +25 -25
  939. package/src/engine-components/postprocessing/Effects/Pixelation.ts +32 -32
  940. package/src/engine-components/postprocessing/Effects/ScreenspaceAmbientOcclusion.ts +90 -90
  941. package/src/engine-components/postprocessing/Effects/ScreenspaceAmbientOcclusionN8.ts +191 -191
  942. package/src/engine-components/postprocessing/Effects/Sharpening.ts +138 -138
  943. package/src/engine-components/postprocessing/Effects/TiltShiftEffect.ts +61 -61
  944. package/src/engine-components/postprocessing/Effects/Tonemapping.ts +155 -155
  945. package/src/engine-components/postprocessing/Effects/Vignette.ts +59 -59
  946. package/src/engine-components/postprocessing/PostProcessingEffect.ts +172 -172
  947. package/src/engine-components/postprocessing/PostProcessingHandler.ts +398 -398
  948. package/src/engine-components/postprocessing/Volume.ts +410 -410
  949. package/src/engine-components/postprocessing/VolumeParameter.ts +158 -158
  950. package/src/engine-components/postprocessing/VolumeProfile.ts +61 -61
  951. package/src/engine-components/postprocessing/index.ts +5 -5
  952. package/src/engine-components/postprocessing/utils.ts +52 -52
  953. package/src/engine-components/timeline/PlayableDirector.ts +722 -722
  954. package/src/engine-components/timeline/SignalAsset.ts +144 -144
  955. package/src/engine-components/timeline/TimelineModels.ts +92 -92
  956. package/src/engine-components/timeline/TimelineTracks.ts +967 -967
  957. package/src/engine-components/timeline/index.ts +3 -3
  958. package/src/engine-components/ui/BaseUIComponent.ts +203 -203
  959. package/src/engine-components/ui/Button.ts +307 -307
  960. package/src/engine-components/ui/Canvas.ts +419 -419
  961. package/src/engine-components/ui/CanvasGroup.ts +54 -54
  962. package/src/engine-components/ui/EventSystem.ts +853 -853
  963. package/src/engine-components/ui/Graphic.ts +275 -275
  964. package/src/engine-components/ui/Image.ts +112 -112
  965. package/src/engine-components/ui/InputField.ts +321 -321
  966. package/src/engine-components/ui/Interfaces.ts +57 -57
  967. package/src/engine-components/ui/Layout.ts +334 -334
  968. package/src/engine-components/ui/Outline.ts +13 -13
  969. package/src/engine-components/ui/PointerEvents.ts +206 -206
  970. package/src/engine-components/ui/RaycastUtils.ts +69 -69
  971. package/src/engine-components/ui/Raycaster.ts +102 -102
  972. package/src/engine-components/ui/RectTransform.ts +372 -372
  973. package/src/engine-components/ui/SpatialHtml.ts +79 -79
  974. package/src/engine-components/ui/Text.ts +578 -578
  975. package/src/engine-components/ui/Utils.ts +112 -112
  976. package/src/engine-components/utils/EnvironmentScene.ts +245 -245
  977. package/src/engine-components/utils/LookAt.ts +88 -88
  978. package/src/engine-components/utils/OpenURL.ts +114 -114
  979. package/src/engine-components/webxr/Avatar.ts +265 -265
  980. package/src/engine-components/webxr/TeleportTarget.ts +9 -9
  981. package/src/engine-components/webxr/WebARCameraBackground.ts +175 -175
  982. package/src/engine-components/webxr/WebARSessionRoot.ts +880 -880
  983. package/src/engine-components/webxr/WebXR.ts +585 -585
  984. package/src/engine-components/webxr/WebXRAvatar.ts +66 -66
  985. package/src/engine-components/webxr/WebXRImageTracking.ts +518 -518
  986. package/src/engine-components/webxr/WebXRPlaneTracking.ts +570 -570
  987. package/src/engine-components/webxr/WebXRRig.ts +77 -77
  988. package/src/engine-components/webxr/XRFlag.ts +147 -147
  989. package/src/engine-components/webxr/controllers/XRControllerFollow.ts +118 -118
  990. package/src/engine-components/webxr/controllers/XRControllerModel.ts +374 -374
  991. package/src/engine-components/webxr/controllers/XRControllerMovement.ts +550 -550
  992. package/src/engine-components/webxr/index.ts +2 -2
  993. package/src/engine-components/webxr/types.ts +3 -3
  994. package/src/engine-components-experimental/Presentation.ts +12 -12
  995. package/src/engine-components-experimental/api.ts +4 -4
  996. package/src/engine-components-experimental/networking/PlayerSync.ts +401 -401
  997. package/src/engine-schemes/COMPILE_SCHEMES.bat +3 -3
  998. package/src/engine-schemes/COMPILE_TS.bat +11 -11
  999. package/src/engine-schemes/README.md +1 -1
  1000. package/src/engine-schemes/api.ts +12 -12
  1001. package/src/engine-schemes/schemes.ts +28 -28
  1002. package/src/engine-schemes/synced-camera-model.ts +92 -92
  1003. package/src/engine-schemes/synced-transform-model.ts +90 -90
  1004. package/src/engine-schemes/syncedCamera.fbs +10 -10
  1005. package/src/engine-schemes/transform.ts +50 -50
  1006. package/src/engine-schemes/transforms.fbs +25 -25
  1007. package/src/engine-schemes/vec.fbs +19 -19
  1008. package/src/engine-schemes/vec2.ts +33 -33
  1009. package/src/engine-schemes/vec3.ts +38 -38
  1010. package/src/engine-schemes/vec4.ts +43 -43
  1011. package/src/engine-schemes/vr-user-state-buffer.ts +145 -145
  1012. package/src/engine-schemes/vrUserStateBuffer.fbs +17 -17
  1013. package/src/include/draco/draco_decoder.js +34 -34
  1014. package/src/include/ktx2/basis_transcoder.js +21 -21
  1015. package/src/include/needle/arial-msdf.json +1471 -1471
  1016. package/src/include/three/DragControls.js +231 -231
  1017. package/src/include/three/EXT_mesh_gpu_instancing_exporter.js +66 -66
  1018. package/src/needle-engine.ts +70 -70
  1019. package/lib/engine/engine.d.ts +0 -4
  1020. package/lib/engine/engine.js +0 -12
  1021. package/lib/engine/engine.js.map +0 -1
  1022. package/lib/engine/engine_web_api.d.ts +0 -12
  1023. package/lib/engine/engine_web_api.js +0 -113
  1024. package/lib/engine/engine_web_api.js.map +0 -1
  1025. package/lib/engine-components/FlyControls.d.ts +0 -10
  1026. package/lib/engine-components/FlyControls.js +0 -29
  1027. package/lib/engine-components/FlyControls.js.map +0 -1
  1028. package/src/engine-schemes/dist/api.js +0 -17
  1029. package/src/engine-schemes/dist/api.js.meta +0 -7
  1030. package/src/engine-schemes/dist/schemes.js +0 -25
  1031. package/src/engine-schemes/dist/schemes.js.meta +0 -7
  1032. package/src/engine-schemes/dist/synced-camera-model.js +0 -74
  1033. package/src/engine-schemes/dist/synced-camera-model.js.meta +0 -7
  1034. package/src/engine-schemes/dist/synced-transform-model.js +0 -73
  1035. package/src/engine-schemes/dist/synced-transform-model.js.meta +0 -7
  1036. package/src/engine-schemes/dist/transform.js +0 -46
  1037. package/src/engine-schemes/dist/transform.js.meta +0 -7
  1038. package/src/engine-schemes/dist/vec2.js +0 -32
  1039. package/src/engine-schemes/dist/vec2.js.meta +0 -7
  1040. package/src/engine-schemes/dist/vec3.js +0 -36
  1041. package/src/engine-schemes/dist/vec3.js.meta +0 -7
  1042. package/src/engine-schemes/dist/vec4.js +0 -40
  1043. package/src/engine-schemes/dist/vec4.js.meta +0 -7
  1044. package/src/engine-schemes/dist/vr-user-state-buffer.js +0 -110
  1045. 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
  }