@needle-tools/engine 4.12.0-next.de80571 → 4.12.0-next.e42c144

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 (193) hide show
  1. package/CHANGELOG.md +1 -1
  2. package/components.needle.json +1 -1
  3. package/dist/generateMeshBVH.worker-iyfPIK6R.js +21 -0
  4. package/dist/{gltf-progressive-DZrY8VT6.min.js → gltf-progressive-BmSygnAC.min.js} +2 -2
  5. package/dist/{gltf-progressive-DgYz5BYa.js → gltf-progressive-DnLBuGK5.js} +24 -24
  6. package/dist/{gltf-progressive-DWcmTMCh.umd.cjs → gltf-progressive-Rs-ojtXy.umd.cjs} +1 -1
  7. package/dist/{loader.worker-Dip-PthR.js → loader.worker-DWzfDpAl.js} +4 -4
  8. package/dist/needle-engine.bundle-CW5U4a3u.umd.cjs +1647 -0
  9. package/dist/needle-engine.bundle-HNQiYV9e.min.js +1647 -0
  10. package/dist/{needle-engine.bundle-CbrIHiN8.js → needle-engine.bundle-qrrKb_xn.js} +8886 -8598
  11. package/dist/needle-engine.d.ts +108 -43
  12. package/dist/needle-engine.js +48 -48
  13. package/dist/needle-engine.min.js +1 -1
  14. package/dist/needle-engine.umd.cjs +1 -1
  15. package/dist/{postprocessing-DYDtB188.min.js → postprocessing-B5ksn9-G.min.js} +54 -54
  16. package/dist/{postprocessing-CMgoN5t5.umd.cjs → postprocessing-DZtb9Nnn.umd.cjs} +81 -81
  17. package/dist/{postprocessing-BTW9pD_s.js → postprocessing-__7s9wON.js} +450 -441
  18. package/dist/{three-DfMvBzXi.js → three-BCCkyCA5.js} +1 -7
  19. package/dist/{three-qj71I7J3.umd.cjs → three-Bf2NBxAw.umd.cjs} +2 -2
  20. package/dist/{three-B7CT31Bt.min.js → three-W7zWTcfP.min.js} +1 -1
  21. package/dist/{three-examples-CsW4_6LI.umd.cjs → three-examples-Dho7cuu4.umd.cjs} +4 -4
  22. package/dist/{three-examples-D1P7eEhn.min.js → three-examples-MsJjauyk.min.js} +10 -10
  23. package/dist/{three-examples-D1SK93ek.js → three-examples-y2GeYlze.js} +2 -20
  24. package/dist/{three-mesh-ui-C_uSB5dD.js → three-mesh-ui-3nSSizT4.js} +1 -1
  25. package/dist/{three-mesh-ui-LQ44s0AL.min.js → three-mesh-ui-CIez6qJQ.min.js} +1 -1
  26. package/dist/{three-mesh-ui-DpATDXwU.umd.cjs → three-mesh-ui-zsOOA5Pq.umd.cjs} +1 -1
  27. package/dist/{vendor-D0zoswDa.js → vendor-DMZcbVO1.js} +3707 -3527
  28. package/dist/vendor-sURMCFSI.min.js +1116 -0
  29. package/dist/{vendor-UCpFAwt1.umd.cjs → vendor-tyBvnMF-.umd.cjs} +39 -39
  30. package/lib/engine/codegen/register_types.js +0 -2
  31. package/lib/engine/codegen/register_types.js.map +1 -1
  32. package/lib/engine/debug/debug_console.js +403 -1
  33. package/lib/engine/debug/debug_console.js.map +1 -1
  34. package/lib/engine/engine_components.js +3 -3
  35. package/lib/engine/engine_components.js.map +1 -1
  36. package/lib/engine/engine_context.js +2 -0
  37. package/lib/engine/engine_context.js.map +1 -1
  38. package/lib/engine/engine_input.d.ts +5 -0
  39. package/lib/engine/engine_input.js +6 -0
  40. package/lib/engine/engine_input.js.map +1 -1
  41. package/lib/engine/engine_license.d.ts +18 -0
  42. package/lib/engine/engine_license.js +115 -18
  43. package/lib/engine/engine_license.js.map +1 -1
  44. package/lib/engine/engine_networking.js +5 -5
  45. package/lib/engine/engine_networking.js.map +1 -1
  46. package/lib/engine/engine_physics.js.map +1 -1
  47. package/lib/engine/engine_physics_rapier.js +1 -1
  48. package/lib/engine/engine_physics_rapier.js.map +1 -1
  49. package/lib/engine/engine_serialization_builtin_serializer.js +1 -1
  50. package/lib/engine/engine_serialization_builtin_serializer.js.map +1 -1
  51. package/lib/engine/engine_utils.d.ts +4 -1
  52. package/lib/engine/engine_utils.js +28 -4
  53. package/lib/engine/engine_utils.js.map +1 -1
  54. package/lib/engine/extensions/extensions.d.ts +29 -7
  55. package/lib/engine/extensions/extensions.js.map +1 -1
  56. package/lib/engine/webcomponents/WebXRButtons.js +13 -5
  57. package/lib/engine/webcomponents/WebXRButtons.js.map +1 -1
  58. package/lib/engine/webcomponents/needle menu/needle-menu.js +4 -5
  59. package/lib/engine/webcomponents/needle menu/needle-menu.js.map +1 -1
  60. package/lib/engine/webcomponents/needle-engine.ar-overlay.js +4 -0
  61. package/lib/engine/webcomponents/needle-engine.ar-overlay.js.map +1 -1
  62. package/lib/engine/webcomponents/needle-engine.js +1 -1
  63. package/lib/engine/webcomponents/needle-engine.js.map +1 -1
  64. package/lib/engine/xr/NeedleXRSession.d.ts +1 -1
  65. package/lib/engine/xr/NeedleXRSession.js +101 -22
  66. package/lib/engine/xr/NeedleXRSession.js.map +1 -1
  67. package/lib/engine/xr/TempXRContext.js +12 -2
  68. package/lib/engine/xr/TempXRContext.js.map +1 -1
  69. package/lib/engine/xr/usdz.js +6 -2
  70. package/lib/engine/xr/usdz.js.map +1 -1
  71. package/lib/engine-components/AlignmentConstraint.d.ts +1 -1
  72. package/lib/engine-components/AlignmentConstraint.js +1 -1
  73. package/lib/engine-components/Animation.d.ts +1 -1
  74. package/lib/engine-components/Animation.js +1 -1
  75. package/lib/engine-components/Animator.d.ts +1 -1
  76. package/lib/engine-components/Animator.js +1 -1
  77. package/lib/engine-components/AudioListener.d.ts +1 -1
  78. package/lib/engine-components/AudioListener.js +1 -1
  79. package/lib/engine-components/AudioSource.d.ts +1 -1
  80. package/lib/engine-components/AudioSource.js +1 -1
  81. package/lib/engine-components/Camera.d.ts +1 -1
  82. package/lib/engine-components/Camera.js +5 -2
  83. package/lib/engine-components/Camera.js.map +1 -1
  84. package/lib/engine-components/CharacterController.d.ts +6 -2
  85. package/lib/engine-components/CharacterController.js +6 -2
  86. package/lib/engine-components/CharacterController.js.map +1 -1
  87. package/lib/engine-components/Collider.d.ts +1 -1
  88. package/lib/engine-components/Collider.js.map +1 -1
  89. package/lib/engine-components/Component.d.ts +2 -1
  90. package/lib/engine-components/Component.js +3 -2
  91. package/lib/engine-components/Component.js.map +1 -1
  92. package/lib/engine-components/DragControls.js +4 -1
  93. package/lib/engine-components/DragControls.js.map +1 -1
  94. package/lib/engine-components/Joints.d.ts +14 -0
  95. package/lib/engine-components/Joints.js +14 -0
  96. package/lib/engine-components/Joints.js.map +1 -1
  97. package/lib/engine-components/LookAtConstraint.d.ts +1 -1
  98. package/lib/engine-components/LookAtConstraint.js +1 -1
  99. package/lib/engine-components/OrbitControls.d.ts +1 -1
  100. package/lib/engine-components/OrbitControls.js +1 -1
  101. package/lib/engine-components/Renderer.d.ts +6 -0
  102. package/lib/engine-components/Renderer.js +6 -0
  103. package/lib/engine-components/Renderer.js.map +1 -1
  104. package/lib/engine-components/RendererInstancing.js +5 -3
  105. package/lib/engine-components/RendererInstancing.js.map +1 -1
  106. package/lib/engine-components/SceneSwitcher.js +18 -14
  107. package/lib/engine-components/SceneSwitcher.js.map +1 -1
  108. package/lib/engine-components/SpectatorCamera.js +15 -7
  109. package/lib/engine-components/SpectatorCamera.js.map +1 -1
  110. package/lib/engine-components/SpriteRenderer.d.ts +2 -1
  111. package/lib/engine-components/SpriteRenderer.js +2 -1
  112. package/lib/engine-components/SpriteRenderer.js.map +1 -1
  113. package/lib/engine-components/api.d.ts +1 -0
  114. package/lib/engine-components/api.js +1 -0
  115. package/lib/engine-components/api.js.map +1 -1
  116. package/lib/engine-components/codegen/components.d.ts +0 -1
  117. package/lib/engine-components/codegen/components.js +0 -1
  118. package/lib/engine-components/codegen/components.js.map +1 -1
  119. package/lib/engine-components/timeline/SignalAsset.d.ts +1 -1
  120. package/lib/engine-components/timeline/SignalAsset.js +1 -1
  121. package/lib/engine-components/ui/Raycaster.d.ts +3 -2
  122. package/lib/engine-components/ui/Raycaster.js +3 -2
  123. package/lib/engine-components/ui/Raycaster.js.map +1 -1
  124. package/lib/engine-components/ui/RectTransform.d.ts +6 -0
  125. package/lib/engine-components/ui/RectTransform.js +6 -0
  126. package/lib/engine-components/ui/RectTransform.js.map +1 -1
  127. package/lib/engine-components/utils/LookAt.d.ts +2 -1
  128. package/lib/engine-components/utils/LookAt.js +2 -1
  129. package/lib/engine-components/utils/LookAt.js.map +1 -1
  130. package/lib/engine-components/web/CursorFollow.d.ts +1 -1
  131. package/lib/engine-components/web/CursorFollow.js +1 -1
  132. package/lib/engine-components/web/HoverAnimation.d.ts +1 -1
  133. package/lib/engine-components/web/HoverAnimation.js +1 -1
  134. package/lib/engine-components/web/ViewBox.d.ts +1 -1
  135. package/lib/engine-components/web/ViewBox.js +1 -1
  136. package/lib/engine-components/webxr/Avatar.js +2 -0
  137. package/lib/engine-components/webxr/Avatar.js.map +1 -1
  138. package/lib/engine-components/webxr/WebXR.js +18 -12
  139. package/lib/engine-components/webxr/WebXR.js.map +1 -1
  140. package/package.json +3 -3
  141. package/plugins/vite/poster-client.js +8 -1
  142. package/src/engine/codegen/register_types.ts +0 -2
  143. package/src/engine/debug/debug_console.ts +449 -1
  144. package/src/engine/engine_components.ts +4 -4
  145. package/src/engine/engine_context.ts +2 -0
  146. package/src/engine/engine_input.ts +7 -0
  147. package/src/engine/engine_license.ts +128 -17
  148. package/src/engine/engine_networking.ts +5 -5
  149. package/src/engine/engine_physics.ts +3 -3
  150. package/src/engine/engine_physics_rapier.ts +1 -1
  151. package/src/engine/engine_serialization_builtin_serializer.ts +1 -1
  152. package/src/engine/engine_utils.ts +23 -4
  153. package/src/engine/extensions/extensions.ts +30 -6
  154. package/src/engine/webcomponents/WebXRButtons.ts +15 -5
  155. package/src/engine/webcomponents/needle menu/needle-menu.ts +4 -5
  156. package/src/engine/webcomponents/needle-engine.ar-overlay.ts +6 -0
  157. package/src/engine/webcomponents/needle-engine.ts +2 -2
  158. package/src/engine/xr/NeedleXRSession.ts +114 -24
  159. package/src/engine/xr/TempXRContext.ts +12 -2
  160. package/src/engine/xr/usdz.ts +6 -1
  161. package/src/engine-components/AlignmentConstraint.ts +1 -1
  162. package/src/engine-components/Animation.ts +1 -1
  163. package/src/engine-components/Animator.ts +1 -1
  164. package/src/engine-components/AudioListener.ts +1 -1
  165. package/src/engine-components/AudioSource.ts +1 -1
  166. package/src/engine-components/Camera.ts +5 -2
  167. package/src/engine-components/CharacterController.ts +6 -2
  168. package/src/engine-components/Collider.ts +1 -1
  169. package/src/engine-components/Component.ts +5 -4
  170. package/src/engine-components/DragControls.ts +5 -1
  171. package/src/engine-components/Joints.ts +14 -0
  172. package/src/engine-components/LookAtConstraint.ts +1 -1
  173. package/src/engine-components/OrbitControls.ts +1 -1
  174. package/src/engine-components/Renderer.ts +6 -0
  175. package/src/engine-components/RendererInstancing.ts +6 -3
  176. package/src/engine-components/SceneSwitcher.ts +17 -17
  177. package/src/engine-components/SpectatorCamera.ts +21 -10
  178. package/src/engine-components/SpriteRenderer.ts +2 -1
  179. package/src/engine-components/api.ts +2 -1
  180. package/src/engine-components/codegen/components.ts +0 -1
  181. package/src/engine-components/timeline/SignalAsset.ts +1 -1
  182. package/src/engine-components/ui/Raycaster.ts +3 -2
  183. package/src/engine-components/ui/RectTransform.ts +6 -0
  184. package/src/engine-components/utils/LookAt.ts +2 -1
  185. package/src/engine-components/web/CursorFollow.ts +1 -1
  186. package/src/engine-components/web/HoverAnimation.ts +1 -1
  187. package/src/engine-components/web/ViewBox.ts +1 -1
  188. package/src/engine-components/webxr/Avatar.ts +4 -0
  189. package/src/engine-components/webxr/WebXR.ts +19 -11
  190. package/dist/generateMeshBVH.worker-mO20N_b8.js +0 -21
  191. package/dist/needle-engine.bundle-BGixCtrn.umd.cjs +0 -1647
  192. package/dist/needle-engine.bundle-S3PalR7r.min.js +0 -1647
  193. package/dist/vendor-BKGa4GE0.min.js +0 -1116
@@ -6,6 +6,7 @@ import { Context, FrameEvent } from "../engine_context.js";
6
6
  import { ContextEvent, ContextRegistry } from "../engine_context_registry.js";
7
7
  import { isDestroyed } from "../engine_gameobject.js";
8
8
  import { Gizmos } from "../engine_gizmos.js";
9
+ import { Telemetry } from "../engine_license.js";
9
10
  import { registerFrameEventCallback, unregisterFrameEventCallback } from "../engine_lifecycle_functions_internal.js";
10
11
  import { getBoundingBox, getTempQuaternion, getTempVector, getWorldPosition, getWorldQuaternion, getWorldScale, setWorldPosition, setWorldQuaternion, setWorldScale } from "../engine_three_utils.js";
11
12
  import type { ICamera, IComponent, INeedleXRSession } from "../engine_types.js";
@@ -82,6 +83,34 @@ function getDOMOverlayElement(domElement: HTMLElement) {
82
83
  handleSessionGranted();
83
84
  async function handleSessionGranted() {
84
85
 
86
+ // await delay(400);
87
+
88
+
89
+ let defaultMode: XRSessionMode = "immersive-vr";
90
+
91
+ try {
92
+ // In app clips we default to AR
93
+ if (DeviceUtilities.isNeedleAppClip()) {
94
+ defaultMode = "immersive-ar";
95
+ }
96
+ // Check if VR is even supported, otherwise try AR
97
+ else if (!(await navigator.xr?.isSessionSupported("immersive-vr"))) {
98
+ defaultMode = "immersive-ar";
99
+ }
100
+ // Check if AR is supported, otherwise we can't do anything
101
+ if (!(await navigator.xr?.isSessionSupported("immersive-ar")) && defaultMode === "immersive-ar") {
102
+ // console.warn("[NeedleXRSession:granted] Neither VR nor AR supported, aborting session start.");
103
+ // showBalloonMessage("NeidleXRSession: Neither VR nor AR supported, aborting session start.");
104
+ return;
105
+ }
106
+ } catch (e) {
107
+ console.debug("[NeedleXRSession:granted] Error while checking XR support:", e);
108
+ // showBalloonWarning("NeedleXRSession: Error while checking XR support: " + (e as Error).message);
109
+ return;
110
+ }
111
+
112
+ // showBalloonMessage("sessiongranted: " + defaultMode);
113
+
85
114
  // TODO: asap session granted doesnt handle the pre-room yet
86
115
  if (getParam("debugasap")) {
87
116
  let asapSession = globalThis["needle:XRSession"] as XRSession | undefined | Promise<XRSession>;
@@ -94,11 +123,11 @@ async function handleSessionGranted() {
94
123
  enableSpatialConsole(true);
95
124
  const session = await asapSession;
96
125
  if (session) {
97
- const sessionInit = NeedleXRSession.getDefaultSessionInit("immersive-vr");
98
- NeedleXRSession.setSession("immersive-vr", session, sessionInit, cb.context);
126
+ const sessionInit = NeedleXRSession.getDefaultSessionInit(defaultMode);
127
+ NeedleXRSession.setSession(defaultMode, session, sessionInit, cb.context);
99
128
  }
100
129
  else {
101
- console.error("NeedleXRSession: ASAP session was rejected");
130
+ console.error("[NeedleXRSession:granted] ASAP session was rejected");
102
131
  }
103
132
  asapSession = undefined;
104
133
  });
@@ -106,6 +135,9 @@ async function handleSessionGranted() {
106
135
  }
107
136
  }
108
137
 
138
+ // console.log("Attaching sessiongranted handler...", { haveXR: 'xr' in navigator });
139
+ // setTimeout(() => console.log("Session Granted handler attached.", { haveXR: 'xr' in navigator }), 1000);
140
+
109
141
  if ('xr' in navigator) {
110
142
  // WebXRViewer (based on Firefox) has a bug where addEventListener
111
143
  // throws a silent exception and aborts execution entirely.
@@ -115,10 +147,8 @@ async function handleSessionGranted() {
115
147
  }
116
148
 
117
149
  navigator.xr?.addEventListener('sessiongranted', async () => {
118
- enableSpatialConsole(true);
150
+ // enableSpatialConsole(true);
119
151
 
120
- console.log("Received Session Granted...")
121
- await delay(100);
122
152
 
123
153
  const lastSessionMode = sessionStorage.getItem("needle_xr_session_mode") as XRSessionMode;
124
154
  const lastSessionInit = sessionStorage.getItem("needle_xr_session_init") ?? null;
@@ -126,7 +156,8 @@ async function handleSessionGranted() {
126
156
 
127
157
  let info: SessionInfo | null = null;
128
158
  if (contextIsLoading()) {
129
- await TemporaryXRContext.start(lastSessionMode || "immersive-vr", init || NeedleXRSession.getDefaultSessionInit("immersive-vr"));
159
+ await TemporaryXRContext.start(lastSessionMode || defaultMode, init || NeedleXRSession.getDefaultSessionInit(defaultMode))
160
+ .catch(e => console.warn("[NeedleXRSession:granted] TemporaryXRContext start failed:", e));
130
161
  await waitForContextLoadingFinished();
131
162
  info = await TemporaryXRContext.handoff();
132
163
  }
@@ -134,17 +165,19 @@ async function handleSessionGranted() {
134
165
  NeedleXRSession.setSession(info.mode, info.session, info.init, Context.Current);
135
166
  }
136
167
  else if (lastSessionMode && lastSessionInit) {
137
- console.log("Session Granted: Restore last session")
168
+ console.log("[NeedleXRSession:granted] Restore last session")
138
169
  const init = JSON.parse(lastSessionInit);
139
170
  NeedleXRSession.start(lastSessionMode as XRSessionMode, init).catch(e => console.warn(e));
140
171
  }
141
172
  else {
142
173
  // if no session was found we start VR by default
143
- NeedleXRSession.start("immersive-vr").catch(e => console.warn("Session Granted failed:", e));
174
+ NeedleXRSession.start(defaultMode).catch(e => console.warn("[NeedleXRSession:granted] failed:", e));
144
175
  }
145
176
  // make sure we only subscribe to the event once
146
177
  }, { once: true });
147
-
178
+ }
179
+ else {
180
+ // showBalloonWarning("NeedleXRSession: WebXR not available in this browser.");
148
181
  }
149
182
  }
150
183
  function saveSessionInfo(mode: XRSessionMode, init: XRSessionInit) {
@@ -419,27 +452,78 @@ export class NeedleXRSession implements INeedleXRSession {
419
452
  * @param init The XRSessionInit to use (optional), docs: https://developer.mozilla.org/en-US/docs/Web/API/XRSessionInit
420
453
  * @param context The Needle Engine context to use
421
454
  */
422
- static async start(mode: XRSessionMode | "ar", init?: XRSessionInit, context?: Context): Promise<NeedleXRSession | null> {
455
+ static async start(mode: XRSessionMode | "ar" | "quicklook", init?: XRSessionInit, context?: Context): Promise<NeedleXRSession | null> {
423
456
 
424
- // handle iOS platform where "immersive-ar" is not supported
457
+ // handle iOS platform where "immersive-ar" is special:
458
+ // - we either launch QuickLook
459
+ // - or forward to the Needle App Clip experience for WebXR AR
425
460
  // TODO: should we add a separate mode (e.g. "AR")? https://linear.app/needle/issue/NE-5303
426
461
  if (DeviceUtilities.isiOS()) {
427
- if (mode === "ar") {
428
- const arSupported = await this.isARSupported();
429
- if (!arSupported) {
430
- InternalUSDZRegistry.exportAndOpen();
431
- return null;
462
+
463
+ const arSupported = await this.isARSupported().catch(() => false);
464
+
465
+ // On VisionOS, we use QuickLook for AR experiences; no AppClip support for now.
466
+ if (DeviceUtilities.isVisionOS() && !arSupported && (mode === "ar" || mode === "immersive-ar")) {
467
+ mode = "quicklook";
468
+ }
469
+
470
+ if (mode === "quicklook") {
471
+ Telemetry.sendEvent(Context.Current, "xr", {
472
+ action: "quicklook_export",
473
+ source: "NeedleXRSession.start",
474
+ });
475
+ InternalUSDZRegistry.exportAndOpen();
476
+ return null;
477
+ }
478
+
479
+ if (!arSupported && (mode === "immersive-ar" || mode === "ar")) {
480
+ // const debugAppClip = getParam("debugappclip")
481
+ // Forward to the AppClip experience (Using the apple.com url the appclip overlay shows immediately)
482
+ // const url =`https://appclip.needle.tools/ar?url=${(location.href)}`;
483
+ const url = new URL("https://appclip.apple.com/id?p=tools.needle.launch-app.Clip");
484
+ url.searchParams.set("url", location.href);
485
+
486
+ const urlStr = url.toString();
487
+
488
+ Telemetry.sendEvent(Context.Current, "xr", {
489
+ action: "app_clip_launch",
490
+ source: "NeedleXRSession.start",
491
+ url: urlStr,
492
+ });
493
+
494
+ // if we are in an iframe, we need to navigate the top window
495
+ const topWindow = window.top || window;
496
+ try {
497
+ console.debug("iOS device detected - opening Needle App Clip for AR experience", { mode, init, url });
498
+ // navigate to app clip url but keep the current url in history, open in same tab
499
+ // eslint-disable-next-line xss/no-location-href-assign
500
+ topWindow.location.href = urlStr;
432
501
  }
433
- else {
434
- mode = "immersive-ar";
502
+ catch (e) {
503
+ console.warn("Error navigating to AppClip " + urlStr + "\n", e);
504
+ // if top window navigation fails and we are in an iframe, we try to navigate the top window directly
505
+ const weAreInIframe = window !== window.top;
506
+ if (weAreInIframe) {
507
+ // we can try to open a new tab as a fallback
508
+ window.open(urlStr, "_blank");
509
+ }
510
+ // eslint-disable-next-line xss/no-location-href-assign
511
+ else window.location.href = urlStr;
435
512
  }
513
+
514
+ return null;
436
515
  }
437
516
  }
438
- else if (mode == "ar") {
439
- mode = "immersive-ar";
440
- }
441
517
 
518
+ if (mode === "quicklook") {
519
+ console.warn("QuickLook mode is only supported on iOS devices");
520
+ return null;
521
+ }
442
522
 
523
+ // Since we now know we are not on iOS, ar mode becomes "immersive-ar"
524
+ if (mode == "ar") {
525
+ mode = "immersive-ar";
526
+ }
443
527
 
444
528
  if (isDevEnvironment() && getParam("debugxrpreroom")) {
445
529
  console.warn("Debug: Starting temporary XR session");
@@ -535,12 +619,18 @@ export class NeedleXRSession implements INeedleXRSession {
535
619
  listener({ mode, init });
536
620
  }
537
621
  if (debug) showBalloonMessage("Requesting " + mode + " session (" + Date.now() + ")");
622
+ Telemetry.sendEvent(Context.Current, "xr", {
623
+ action: "session_request",
624
+ mode: mode,
625
+ features: ((init.requiredFeatures ?? []).concat(init.optionalFeatures ?? [])).join(","),
626
+ source: "NeedleXRSession.start",
627
+ });
538
628
  this._currentSessionRequest = navigator?.xr?.requestSession(mode, init);
539
629
  this._currentSessionRequestMode = mode;
540
630
  /**@type {XRSystem} */
541
631
  const newSession = await (this._currentSessionRequest)?.catch(e => {
542
- console.error(e, "Code: " + e.code);
543
- if (e.code === 9) showBalloonWarning("Make sure your device has the required permissions (e.g. camera access)")
632
+ console.error(e, "Code: " + e?.code);
633
+ if (e?.code === 9) showBalloonWarning("Couldn't start XR session. Make sure you allow the required permissions.")
544
634
  console.log("If the specified XR configuration is not supported (e.g. entering AR doesnt work) - make sure you access the website on a secure connection (HTTPS) and your device has the required permissions (e.g. camera access)");
545
635
  const notSecure = location.protocol === 'http:';
546
636
  if (notSecure) showBalloonWarning("XR requires a secure connection (HTTPS)");
@@ -32,7 +32,13 @@ export class TemporaryXRContext {
32
32
  return null;
33
33
  }
34
34
  this._requestInFlight = true;
35
- const session = await navigator.xr.requestSession(mode, init);
35
+ const session = await navigator.xr.requestSession(mode, init).catch(err => {
36
+ console.error("Failed to start temporary XR session:", err);
37
+ });
38
+ if (!session) {
39
+ this._requestInFlight = false;
40
+ return null;
41
+ }
36
42
  session.addEventListener("end", () => {
37
43
  this._active = null;
38
44
  });
@@ -82,10 +88,14 @@ export class TemporaryXRContext {
82
88
  this._session = session;
83
89
  this._session.addEventListener("end", this.onEnd);
84
90
 
85
- this._renderer = new WebGLRenderer({ alpha: true });
91
+ this._renderer = new WebGLRenderer({ alpha: true, antialias: true });
92
+ this._renderer.outputColorSpace = 'srgb';
86
93
  this._renderer.setAnimationLoop(this.onFrame);
87
94
  this._renderer.xr.setSession(session);
88
95
  this._renderer.xr.enabled = true;
96
+ // Set pixel ratio and size
97
+ this._renderer.setPixelRatio(Math.min(2, window.devicePixelRatio));
98
+ this._renderer.setSize(window.innerWidth, window.innerHeight);
89
99
  this._camera = new PerspectiveCamera();
90
100
  this._scene = new Scene();
91
101
  this._scene.fog = new Fog(0x444444, 10, 250);
@@ -1,3 +1,4 @@
1
+ import { isDevEnvironment } from "../debug/index.js";
1
2
 
2
3
  declare type USDZExporter = {
3
4
  exportAndOpen(): Promise<any>,
@@ -11,7 +12,11 @@ export namespace InternalUSDZRegistry {
11
12
  const usdzExporter: USDZExporter[] = [];
12
13
 
13
14
  export function exportAndOpen(): boolean {
14
- if (!usdzExporter?.length) return false;
15
+ if (!usdzExporter?.length) {
16
+ if (isDevEnvironment()) {
17
+ console.warn("No USDZ exporters found – cannot export USDZ for QuickLook.");
18
+ }
19
+ }
15
20
  for (const exp of usdzExporter) {
16
21
  exp.exportAndOpen();
17
22
  }
@@ -9,7 +9,7 @@ import { Behaviour, GameObject } from "./Component.js";
9
9
  * You can use this to create dynamic beams or connectors between objects.
10
10
  *
11
11
  * @summary Aligns and scales the object between two target GameObjects
12
- * @category Utility
12
+ * @category Constraints
13
13
  * @group Components
14
14
  **/
15
15
  export class AlignmentConstraint extends Behaviour {
@@ -60,7 +60,7 @@ class Vec2 { x!: number; y!: number }
60
60
  /**
61
61
  * Animation component to play animations on a GameObject.
62
62
  *
63
- * @summary Play animations
63
+ * @summary Plays animations from AnimationClips
64
64
  * @category Animation and Sequencing
65
65
  * @group Components
66
66
  */
@@ -43,7 +43,7 @@ export declare class PlayOptions {
43
43
  * It works with an {@link AnimatorController} to handle state transitions and animation blending.
44
44
  * A new AnimatorController can be created from code via `AnimatorController.createFromClips`.
45
45
  *
46
- * @summary Animation controller and playback component
46
+ * @summary Plays and manages animations on a GameObject based on an AnimatorController
47
47
  * @category Animation and Sequencing
48
48
  * @group Components
49
49
  */
@@ -9,7 +9,7 @@ import { Behaviour, GameObject } from "./Component.js";
9
9
  * This component creates and manages a Three.js {@link three#AudioListener}, automatically connecting it
10
10
  * to the main camera or a Camera in the parent hierarchy.
11
11
  *
12
- * @summary Audio listener for 3D audio capture
12
+ * @summary Receives audio in the scene and outputs it to speakers
13
13
  * @category Multimedia
14
14
  * @group Components
15
15
  */
@@ -52,7 +52,7 @@ export enum AudioRolloffMode {
52
52
  * is muted, the volume is set to 0. When unmuted, the volume
53
53
  * returns to its previous value.
54
54
  *
55
- * @summary 3D audio source with spatial positioning and playback controls
55
+ * @summary Plays audio clips from files or media streams
56
56
  * @category Multimedia
57
57
  * @group Components
58
58
  */
@@ -8,7 +8,7 @@ import { Context } from "../engine/engine_setup.js";
8
8
  import { RenderTexture } from "../engine/engine_texture.js";
9
9
  import { getTempColor, getWorldPosition } from "../engine/engine_three_utils.js";
10
10
  import type { ICamera } from "../engine/engine_types.js"
11
- import { getParam } from "../engine/engine_utils.js";
11
+ import { DeviceUtilities, getParam } from "../engine/engine_utils.js";
12
12
  import { NeedleXREventArgs } from "../engine/engine_xr.js";
13
13
  import { RGBAColor } from "../engine/js-extensions/index.js";
14
14
  import { Behaviour, GameObject } from "./Component.js";
@@ -37,7 +37,7 @@ const debugscreenpointtoray = getParam("debugscreenpointtoray");
37
37
  * Internally, this component uses {@link PerspectiveCamera} and {@link OrthographicCamera} three.js objects.
38
38
  *
39
39
  * @summary Rendering scenes from a specific viewpoint
40
- * @category Camera
40
+ * @category Camera and Controls
41
41
  * @group Components
42
42
  */
43
43
  export class Camera extends Behaviour implements ICamera {
@@ -664,6 +664,9 @@ export class Camera extends Behaviour implements ICamera {
664
664
  else if (navigator.userAgent?.includes("Mozilla") && navigator.userAgent?.includes("Mobile WebXRViewer/v2")) {
665
665
  transparent = true;
666
666
  }
667
+ else if(DeviceUtilities.isNeedleAppClip()) {
668
+ return true;
669
+ }
667
670
  }
668
671
  }
669
672
 
@@ -110,8 +110,12 @@ export class CharacterController extends Behaviour {
110
110
  }
111
111
 
112
112
  /**
113
- * @category Camera
114
- * @category Interactivity
113
+ * CharacterControllerInput handles user input to control a CharacterController.
114
+ * It supports movement, looking around, jumping, and double jumping.
115
+ * You can customize movement speed, rotation speed, and jump forces.
116
+ * It also integrates with an Animator component for character animations.
117
+ * @summary User Input for Character Controller
118
+ * @category Character
115
119
  * @group Components
116
120
  */
117
121
  export class CharacterControllerInput extends Behaviour {
@@ -26,7 +26,7 @@ import { Rigidbody } from "./RigidBody.js";
26
26
  * @category Physics
27
27
  * @group Components
28
28
  */
29
- export class Collider extends Behaviour implements ICollider {
29
+ export abstract class Collider extends Behaviour implements ICollider {
30
30
 
31
31
  /**
32
32
  * Identifies this component as a collider.
@@ -476,10 +476,11 @@ export abstract class GameObject extends Object3D implements Object3D, IGameObje
476
476
  * Gets a component of the specified type in the gameObject's children hierarchy
477
477
  * @param go GameObject to search in
478
478
  * @param typeName Constructor of the component type
479
+ * @param includeInactive Whether to include inactive objects in the search
479
480
  * @returns The first matching component if found, otherwise null
480
481
  */
481
- public static getComponentInChildren<T extends IComponent>(go: IGameObject | Object3D, typeName: Constructor<T>): T | null {
482
- return getComponentInChildren(go, typeName);
482
+ public static getComponentInChildren<T extends IComponent>(go: IGameObject | Object3D, typeName: Constructor<T>, includeInactive: boolean = false): T | null {
483
+ return getComponentInChildren(go, typeName, includeInactive);
483
484
  }
484
485
 
485
486
  /**
@@ -588,7 +589,7 @@ export abstract class Component implements IComponent, EventTarget,
588
589
  */
589
590
  get [$componentName]() { return TypeStore.getKey(this.constructor as any) || undefined; }
590
591
 
591
-
592
+
592
593
  private __context: Context | undefined;
593
594
 
594
595
  /**
@@ -1106,7 +1107,7 @@ export abstract class Component implements IComponent, EventTarget,
1106
1107
  this.dispatchEvent(new CustomEvent("destroyed", { detail: this }));
1107
1108
  }
1108
1109
  destroyComponentInstance(this as any);
1109
- if(isHotReloadEnabled()) unregisterHotReloadType(this);
1110
+ if (isHotReloadEnabled()) unregisterHotReloadType(this);
1110
1111
  }
1111
1112
 
1112
1113
  /**
@@ -447,7 +447,7 @@ export class DragControls extends Behaviour implements IPointerEventHandler {
447
447
  if (!this || !this._isDragging) return;
448
448
  this._isDragging = false;
449
449
  for (const rb of this._draggingRigidbodies) {
450
- rb.setVelocity(rb.smoothedVelocity);
450
+ rb.setVelocity(rb.smoothedVelocity.multiplyScalar(this.context.time.deltaTime));
451
451
  }
452
452
  this._draggingRigidbodies.length = 0;
453
453
  this._targetObject = null;
@@ -485,6 +485,8 @@ interface IDragHandler {
485
485
  onDragUpdate?(numberOfPointers: number): void;
486
486
  }
487
487
 
488
+
489
+ // #region MultiTouchDragHandler
488
490
  /**
489
491
  * Handles two touch points affecting one object.
490
492
  * Enables multi-touch interactions that allow movement, scaling, and rotation of objects.
@@ -734,6 +736,7 @@ class MultiTouchDragHandler implements IDragHandler {
734
736
  }
735
737
 
736
738
 
739
+ // #region DragPointerHandler
737
740
  /**
738
741
  * Handles a single pointer on an object.
739
742
  * DragPointerHandlers manage determining if a drag operation has started, tracking pointer movement,
@@ -1335,6 +1338,7 @@ class DragPointerHandler implements IDragHandler {
1335
1338
  }
1336
1339
  }
1337
1340
 
1341
+ // #region LegacyDragVisualsHelper
1338
1342
  /**
1339
1343
  * Provides visual helper elements for DragControls.
1340
1344
  * Shows where objects will be placed and their relation to surfaces below them.
@@ -36,6 +36,12 @@ export abstract class Joint extends Behaviour {
36
36
  protected abstract createJoint(self: Rigidbody, other: Rigidbody);
37
37
  }
38
38
 
39
+ /**
40
+ * The FixedJoint groups together 2 rigidbodies, making them stick together in their bound position
41
+ * @summary Connect two Rigidbodies and make them stick together
42
+ * @category Physics
43
+ * @group Components
44
+ */
39
45
  export class FixedJoint extends Joint {
40
46
 
41
47
  protected createJoint(self: Rigidbody, other: Rigidbody) {
@@ -43,6 +49,14 @@ export class FixedJoint extends Joint {
43
49
  }
44
50
  }
45
51
 
52
+ /**
53
+ * The HingeJoint groups together 2 rigid bodies, constraining them to move like connected by a hinge.
54
+ *
55
+ * You can specify the anchor point and axis of rotation for the hinge.
56
+ * @summary Connect two Rigidbodies with a hinge
57
+ * @category Physics
58
+ * @group Components
59
+ */
46
60
  export class HingeJoint extends Joint {
47
61
 
48
62
  @serializable(Vector3)
@@ -9,7 +9,7 @@ import type { OrbitControls } from "./OrbitControls.js";
9
9
  * This component is used by {@link OrbitControls} internally.
10
10
  *
11
11
  * @summary Look At Constraint for OrbitControls
12
- * @category Camera Controls
12
+ * @category Camera and Controls
13
13
  * @group Components
14
14
  */
15
15
  export class LookAtConstraint extends Behaviour {
@@ -64,7 +64,7 @@ declare module 'three/examples/jsm/controls/OrbitControls.js' {
64
64
  * The three OrbitControls object can be accessed via the `controls` property.
65
65
  * The object being controlled by the OrbitControls (usually the camera) can be accessed via the `controllerObject` property.
66
66
  * @summary Camera controller using three.js OrbitControls
67
- * @category Camera
67
+ * @category Camera and Controls
68
68
  * @group Components
69
69
  */
70
70
  export class OrbitControls extends Behaviour implements ICameraController {
@@ -805,6 +805,12 @@ export class Renderer extends Behaviour implements IRenderer {
805
805
  export class MeshRenderer extends Renderer {
806
806
  }
807
807
 
808
+ /**
809
+ * Renders deformable meshes that deform via bones and/or blend shapes.
810
+ * @summary Renderer for deformable meshes
811
+ * @category Rendering
812
+ * @group Components
813
+ **/
808
814
  export class SkinnedMeshRenderer extends MeshRenderer {
809
815
 
810
816
  private _needUpdateBoundingSphere = false;
@@ -669,14 +669,17 @@ class InstancedMeshRenderer {
669
669
  private grow(geometry: BufferGeometry) {
670
670
  const id = ++this._growId;
671
671
  const growFactor = 2;
672
- const newSize = Math.ceil(this._maxInstanceCount * growFactor);
672
+
673
+ const growInstances = this.count >= this._maxInstanceCount;
674
+ const newSize = growInstances ? Math.ceil(this._maxInstanceCount * growFactor) : this._maxInstanceCount;
673
675
 
674
676
  // create a new BatchedMesh instance
675
677
  // TODO: we should keep track of how many instances for each geometry we have and consider that when estimating new space
676
678
  const estimatedSpace = this.tryEstimateVertexCountSize(newSize, [geometry]);// geometry.attributes.position.count;
677
679
  // const indices = geometry.index ? geometry.index.count : 0;
678
- const newMaxVertexCount = Math.max(this._maxVertexCount, estimatedSpace.vertexCount);
679
- const newMaxIndexCount = Math.max(this._maxIndexCount, estimatedSpace.indexCount);//, Math.ceil(this._maxVertexCount * growFactor));
680
+ const vertexGrowFactor = 1.25;
681
+ const newMaxVertexCount = Math.max(this._maxVertexCount, Math.ceil(estimatedSpace.vertexCount * vertexGrowFactor));
682
+ const newMaxIndexCount = Math.max(this._maxIndexCount, Math.ceil(estimatedSpace.indexCount * vertexGrowFactor));
680
683
 
681
684
  if (debugInstancing) {
682
685
  const geometryInfo = getMeshInformation(geometry);