@needle-tools/engine 5.1.12 → 5.1.14

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 (149) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/SKILL.md +1 -1
  3. package/dist/gltf-progressive-BnLTsXZw.umd.cjs +10 -0
  4. package/dist/gltf-progressive-D6RT9IkB.min.js +10 -0
  5. package/dist/{gltf-progressive-B4DQEeBz.js → gltf-progressive-DaeP5KBr.js} +527 -514
  6. package/dist/{materialx-BBv-42Th.min.js → materialx-D3ZU4CP6.min.js} +1 -1
  7. package/dist/{materialx-0m1xlF5m.js → materialx-FhjQQgNu.js} +1 -1
  8. package/dist/{materialx-8OpPwNc7.umd.cjs → materialx-RxB3AN7d.umd.cjs} +2 -2
  9. package/dist/{needle-engine.bundle-9m-VdGg2.js → needle-engine.bundle-BEa7CABw.js} +7420 -7254
  10. package/dist/{needle-engine.bundle-BmdWLmLa.min.js → needle-engine.bundle-mkmiuKZB.min.js} +145 -145
  11. package/dist/{needle-engine.bundle-DdH96IqH.umd.cjs → needle-engine.bundle-y1_E7Ovt.umd.cjs} +146 -146
  12. package/dist/needle-engine.d.ts +37 -29
  13. package/dist/needle-engine.js +519 -520
  14. package/dist/needle-engine.min.js +1 -1
  15. package/dist/needle-engine.umd.cjs +1 -1
  16. package/dist/{vendor-DiDMDryv.js → vendor-BVzQovzJ.js} +5161 -5161
  17. package/dist/{vendor-DE_m5MvL.umd.cjs → vendor-C5YVRzxf.umd.cjs} +5 -5
  18. package/dist/{vendor-B7IfFLrR.min.js → vendor-rvNvQ-IU.min.js} +5 -5
  19. package/lib/engine/api.d.ts +4 -3
  20. package/lib/engine/api.js +3 -9
  21. package/lib/engine/api.js.map +1 -1
  22. package/lib/engine/engine_addressables.d.ts +32 -1
  23. package/lib/engine/engine_addressables.js +71 -9
  24. package/lib/engine/engine_addressables.js.map +1 -1
  25. package/lib/engine/engine_application.d.ts +2 -0
  26. package/lib/engine/engine_application.js +7 -3
  27. package/lib/engine/engine_application.js.map +1 -1
  28. package/lib/engine/engine_context.js +25 -1
  29. package/lib/engine/engine_context.js.map +1 -1
  30. package/lib/engine/engine_gameobject.js +1 -1
  31. package/lib/engine/engine_gameobject.js.map +1 -1
  32. package/lib/engine/engine_gltf_builtin_components.js +1 -1
  33. package/lib/engine/engine_gltf_builtin_components.js.map +1 -1
  34. package/lib/engine/engine_init.js +2 -2
  35. package/lib/engine/engine_init.js.map +1 -1
  36. package/lib/engine/engine_input.js +1 -1
  37. package/lib/engine/engine_input.js.map +1 -1
  38. package/lib/engine/engine_instantiate_resolve.js.map +1 -1
  39. package/lib/engine/engine_license.d.ts +7 -7
  40. package/lib/engine/engine_license.js +123 -80
  41. package/lib/engine/engine_license.js.map +1 -1
  42. package/lib/engine/engine_lifecycle_functions_internal.js +20 -2
  43. package/lib/engine/engine_lifecycle_functions_internal.js.map +1 -1
  44. package/lib/engine/engine_materialpropertyblock.js +18 -0
  45. package/lib/engine/engine_materialpropertyblock.js.map +1 -1
  46. package/lib/engine/engine_networking.d.ts +1 -1
  47. package/lib/engine/engine_networking.js +1 -1
  48. package/lib/engine/engine_networking.js.map +1 -1
  49. package/lib/engine/engine_networking_blob.js +3 -3
  50. package/lib/engine/engine_networking_blob.js.map +1 -1
  51. package/lib/engine/engine_networking_streams.d.ts +3 -0
  52. package/lib/engine/engine_networking_streams.js +64 -17
  53. package/lib/engine/engine_networking_streams.js.map +1 -1
  54. package/lib/engine/engine_utils_qrcode.js +2 -2
  55. package/lib/engine/engine_utils_qrcode.js.map +1 -1
  56. package/lib/engine/physics/workers/mesh-bvh/GenerateMeshBVHWorker.js +5 -0
  57. package/lib/engine/physics/workers/mesh-bvh/GenerateMeshBVHWorker.js.map +1 -1
  58. package/lib/engine/webcomponents/fonts.d.ts +1 -1
  59. package/lib/engine/webcomponents/fonts.js +16 -1
  60. package/lib/engine/webcomponents/fonts.js.map +1 -1
  61. package/lib/engine/webcomponents/icons.js +8 -1
  62. package/lib/engine/webcomponents/icons.js.map +1 -1
  63. package/lib/engine/webcomponents/needle menu/needle-menu-spatial.js +2 -2
  64. package/lib/engine/webcomponents/needle menu/needle-menu-spatial.js.map +1 -1
  65. package/lib/engine/webcomponents/needle menu/needle-menu.d.ts +5 -0
  66. package/lib/engine/webcomponents/needle menu/needle-menu.js +34 -16
  67. package/lib/engine/webcomponents/needle menu/needle-menu.js.map +1 -1
  68. package/lib/engine/webcomponents/needle-engine.d.ts +1 -0
  69. package/lib/engine/webcomponents/needle-engine.js +18 -6
  70. package/lib/engine/webcomponents/needle-engine.js.map +1 -1
  71. package/lib/engine/webcomponents/needle-engine.loading.d.ts +2 -0
  72. package/lib/engine/webcomponents/needle-engine.loading.js +12 -2
  73. package/lib/engine/webcomponents/needle-engine.loading.js.map +1 -1
  74. package/lib/engine/xr/NeedleXRSession.js +11 -10
  75. package/lib/engine/xr/NeedleXRSession.js.map +1 -1
  76. package/lib/engine/xr/TempXRContext.js +2 -2
  77. package/lib/engine/xr/TempXRContext.js.map +1 -1
  78. package/lib/engine-components/AnimationBuilder.d.ts +1 -1
  79. package/lib/engine-components/AnimationBuilder.js.map +1 -1
  80. package/lib/engine-components/AnimatorController.builder.d.ts +2 -2
  81. package/lib/engine-components/AnimatorController.builder.js +2 -2
  82. package/lib/engine-components/AnimatorController.builder.js.map +1 -1
  83. package/lib/engine-components/Component.d.ts +1 -1
  84. package/lib/engine-components/Component.js +1 -1
  85. package/lib/engine-components/Component.js.map +1 -1
  86. package/lib/engine-components/DragControls.d.ts +2 -2
  87. package/lib/engine-components/DragControls.js +2 -2
  88. package/lib/engine-components/DragControls.js.map +1 -1
  89. package/lib/engine-components/DragControlsConstraints.js.map +1 -1
  90. package/lib/engine-components/Light.js +1 -1
  91. package/lib/engine-components/Light.js.map +1 -1
  92. package/lib/engine-components/api.d.ts +2 -2
  93. package/lib/engine-components/api.js +1 -1
  94. package/lib/engine-components/api.js.map +1 -1
  95. package/lib/engine-components/export/usdz/USDZExporter.js +4 -4
  96. package/lib/engine-components/export/usdz/USDZExporter.js.map +1 -1
  97. package/lib/engine-components/export/usdz/extensions/behavior/BehaviourComponents.js.map +1 -1
  98. package/lib/engine-components/postprocessing/index.d.ts +1 -1
  99. package/lib/engine-components/postprocessing/index.js +1 -1
  100. package/lib/engine-components/postprocessing/index.js.map +1 -1
  101. package/lib/engine-components/timeline/TimelineBuilder.d.ts +4 -4
  102. package/lib/engine-components/timeline/TimelineBuilder.js +2 -2
  103. package/lib/engine-components/timeline/TimelineBuilder.js.map +1 -1
  104. package/lib/engine-components/timeline/TimelineTracks.js +24 -15
  105. package/lib/engine-components/timeline/TimelineTracks.js.map +1 -1
  106. package/lib/engine-components/webxr/WebXRImageTracking.utils.js.map +1 -1
  107. package/package.json +2 -2
  108. package/plugins/common/license.js +4 -4
  109. package/plugins/vite/license.js +4 -4
  110. package/src/engine/api.ts +5 -3
  111. package/src/engine/engine_addressables.ts +75 -9
  112. package/src/engine/engine_application.ts +7 -2
  113. package/src/engine/engine_context.ts +25 -1
  114. package/src/engine/engine_gameobject.ts +2 -2
  115. package/src/engine/engine_gltf_builtin_components.ts +1 -1
  116. package/src/engine/engine_init.ts +2 -2
  117. package/src/engine/engine_input.ts +2 -2
  118. package/src/engine/engine_instantiate_resolve.ts +1 -1
  119. package/src/engine/engine_license.ts +115 -77
  120. package/src/engine/engine_lifecycle_functions_internal.ts +21 -2
  121. package/src/engine/engine_materialpropertyblock.ts +21 -0
  122. package/src/engine/engine_networking.ts +2 -2
  123. package/src/engine/engine_networking_blob.ts +3 -3
  124. package/src/engine/engine_networking_streams.ts +61 -17
  125. package/src/engine/engine_utils_qrcode.ts +2 -2
  126. package/src/engine/physics/workers/mesh-bvh/GenerateMeshBVHWorker.js +8 -1
  127. package/src/engine/webcomponents/fonts.ts +16 -1
  128. package/src/engine/webcomponents/icons.ts +8 -1
  129. package/src/engine/webcomponents/needle menu/needle-menu-spatial.ts +2 -2
  130. package/src/engine/webcomponents/needle menu/needle-menu.ts +33 -15
  131. package/src/engine/webcomponents/needle-engine.loading.ts +19 -7
  132. package/src/engine/webcomponents/needle-engine.ts +16 -6
  133. package/src/engine/xr/NeedleXRSession.ts +11 -10
  134. package/src/engine/xr/TempXRContext.ts +2 -2
  135. package/src/engine-components/AnimationBuilder.ts +1 -1
  136. package/src/engine-components/AnimatorController.builder.ts +4 -4
  137. package/src/engine-components/Component.ts +1 -1
  138. package/src/engine-components/DragControls.ts +2 -3
  139. package/src/engine-components/DragControlsConstraints.ts +1 -2
  140. package/src/engine-components/Light.ts +1 -1
  141. package/src/engine-components/api.ts +2 -2
  142. package/src/engine-components/export/usdz/USDZExporter.ts +4 -4
  143. package/src/engine-components/export/usdz/extensions/behavior/BehaviourComponents.ts +1 -1
  144. package/src/engine-components/postprocessing/index.ts +1 -1
  145. package/src/engine-components/timeline/TimelineBuilder.ts +5 -5
  146. package/src/engine-components/timeline/TimelineTracks.ts +25 -17
  147. package/src/engine-components/webxr/WebXRImageTracking.utils.ts +0 -1
  148. package/dist/gltf-progressive-CdjpU5eP.min.js +0 -10
  149. package/dist/gltf-progressive-DeZioRZe.umd.cjs +0 -10
@@ -8,6 +8,7 @@ import { getPeerjsInstance } from "../engine/engine_networking_peer.js";
8
8
  import { showBalloonMessage } from "./debug/index.js";
9
9
  import { Application } from "./engine_application.js";
10
10
  import { Context } from "./engine_context.js";
11
+ import { ContextEvent, ContextRegistry } from "./engine_context_registry.js";
11
12
  import type { IModel } from "./engine_networking_types.js";
12
13
  import { type IComponent, isComponent } from "./engine_types.js";
13
14
  import { getParam } from "./engine_utils.js";
@@ -121,17 +122,31 @@ function applySdpTransform(sdp) {
121
122
 
122
123
  export class PeerHandle extends EventDispatcher<any> {
123
124
 
124
- private static readonly instances: Map<string, PeerHandle> = new Map();
125
+ // Weak ownership prevents this cache from rooting a context and its scene indefinitely.
126
+ // IDs are scoped to contexts: separate engine elements may use the same stream ID.
127
+ private static readonly instances = new WeakMap<Context, Map<string, PeerHandle>>();
128
+
129
+ static {
130
+ ContextRegistry.registerCallback(ContextEvent.ContextDestroying, ({ context }) => {
131
+ const handles = PeerHandle.instances.get(context as Context);
132
+ if (!handles) return;
133
+ PeerHandle.instances.delete(context as Context);
134
+ for (const handle of handles.values()) handle.dispose();
135
+ handles.clear();
136
+ });
137
+ }
125
138
 
126
139
  static getOrCreate(context: Context, guid: string): PeerHandle {
127
- // if (id === undefined) {
128
- // // randomId
129
- // id = Math.random().toFixed(5);
130
- // }
131
- if (PeerHandle.instances.has(guid))
132
- return PeerHandle.instances.get(guid)!;
133
- const peer = new PeerHandle(context, guid);
134
- PeerHandle.instances.set(guid, peer);
140
+ let handles = PeerHandle.instances.get(context);
141
+ if (!handles) {
142
+ handles = new Map();
143
+ PeerHandle.instances.set(context, handles);
144
+ }
145
+ let peer = handles.get(guid);
146
+ if (!peer) {
147
+ peer = new PeerHandle(context, guid);
148
+ handles.set(guid, peer);
149
+ }
135
150
  return peer;
136
151
  }
137
152
 
@@ -151,6 +166,7 @@ export class PeerHandle extends EventDispatcher<any> {
151
166
  }
152
167
 
153
168
  makeCall(peerId: string, stream: MediaStream): CallHandle | undefined {
169
+ if (this._disposed) return undefined;
154
170
  if (!stream?.id) {
155
171
  if (debug) console.warn("Can not make a call: mediastream has no id or is undefined");
156
172
  else console.debug("Can not make a call: mediastream has no id or is undefined");
@@ -179,10 +195,8 @@ export class PeerHandle extends EventDispatcher<any> {
179
195
  }
180
196
 
181
197
  closeAll() {
182
- for (const call of this._incomingCalls) {
183
- call.close();
184
- }
185
- for (const call of this._outgoingCalls) {
198
+ // Closing a call synchronously removes it from the original array.
199
+ for (const call of [...this._incomingCalls, ...this._outgoingCalls]) {
186
200
  call.close();
187
201
  }
188
202
  this.updateCalls();
@@ -253,6 +267,23 @@ export class PeerHandle extends EventDispatcher<any> {
253
267
  }
254
268
  }
255
269
 
270
+ private _disposed = false;
271
+ private readonly _callIntervals = new Set<ReturnType<typeof setInterval>>();
272
+
273
+ private dispose() {
274
+ if (this._disposed) return;
275
+ this._disposed = true;
276
+ this.disable();
277
+ this.unsubscribePeerEvents();
278
+ for (const interval of this._callIntervals) clearInterval(interval);
279
+ this._callIntervals.clear();
280
+ this.closeAll();
281
+ this._incomingCalls.length = 0;
282
+ this._outgoingCalls.length = 0;
283
+ this._peer?.destroy();
284
+ this._peer = undefined;
285
+ }
286
+
256
287
  private _enabled: boolean = false;
257
288
  private _enabledPeer: boolean = false;
258
289
  private onConnectRoomFn: () => void = this.onConnectRoom.bind(this);
@@ -262,7 +293,7 @@ export class PeerHandle extends EventDispatcher<any> {
262
293
  // private _connectionPeerIdMap : Map<string, string> = new Map();
263
294
 
264
295
  enable() {
265
- if (this._enabled) return;
296
+ if (this._disposed || this._enabled) return;
266
297
  this._enabled = true;
267
298
  this.context.connection.beginListen(RoomEvents.JoinedRoom, this.onConnectRoomFn);
268
299
  // this.context.connection.beginListen(RoomEvents.UserJoinedRoom, this.onUserJoinedOrLeftRoomFn);
@@ -287,13 +318,19 @@ export class PeerHandle extends EventDispatcher<any> {
287
318
  // };
288
319
 
289
320
  private async setupPeer() {
290
- if (!this.context.connection.connectionId) return;
321
+ if (this._disposed || !this.context.connection.connectionId) return;
291
322
  if (this._enabledPeer) return;
292
323
  this._enabledPeer = true;
293
324
  if (!this._peer) {
294
325
  const peerId = this.getMyPeerId();
295
- if (peerId)
296
- this._peer = await getPeerjsInstance(peerId);
326
+ if (peerId) {
327
+ const peer = await getPeerjsInstance(peerId);
328
+ if (this._disposed) {
329
+ peer.destroy();
330
+ return;
331
+ }
332
+ this._peer = peer;
333
+ }
297
334
  else console.error("Failed to setup peerjs because we dont have a connection id", this.context.connection.connectionId);
298
335
  }
299
336
  if (this._enabled)
@@ -415,6 +452,7 @@ export class PeerHandle extends EventDispatcher<any> {
415
452
  });
416
453
 
417
454
  call.on("stream", () => {
455
+ if (this._disposed) return;
418
456
  if (debug) console.log("Received stream for call", call.metadata);
419
457
  // workaround for https://github.com/peers/peerjs/issues/636
420
458
  let intervalCounter = 0;
@@ -424,9 +462,15 @@ export class PeerHandle extends EventDispatcher<any> {
424
462
  if (debug) console.warn("Close call because stream is not active", call.metadata);
425
463
  intervalCounter += 1;
426
464
  clearInterval(closeInterval);
465
+ this._callIntervals.delete(closeInterval);
427
466
  handle.close();
428
467
  }
429
468
  }, 2000);
469
+ this._callIntervals.add(closeInterval);
470
+ call.on("close", () => {
471
+ clearInterval(closeInterval);
472
+ this._callIntervals.delete(closeInterval);
473
+ });
430
474
  });
431
475
  }
432
476
  return handle;
@@ -4,7 +4,7 @@ import { Quaternion, Vector2, Vector3, Vector4 } from "three";
4
4
 
5
5
  import { needleLogoOnlySVG } from "./assets/index.js";
6
6
  import { isDevEnvironment } from "./debug/debug.js";
7
- import { $uxbFepO } from "./engine_license.js";
7
+ import { __alnviAJA } from "./engine_license.js";
8
8
  import { InternalAttributeUtils } from "./engine_utils_attributes.js";
9
9
  import type { NeedleEngineWebComponent } from "./webcomponents/needle-engine.js";
10
10
 
@@ -135,7 +135,7 @@ async function internalRenderQRCodeOverlays(canvas: HTMLCanvasElement, args: { s
135
135
  console.debug("[QR Code] No web component found")
136
136
  }
137
137
 
138
- const canUseCustomLogo = $uxbFepO();
138
+ const canUseCustomLogo = __alnviAJA();
139
139
 
140
140
  // Query logo src from needle-engine attribute.
141
141
  // For any supported attribute it's possible to use "falsey" values (e.g. "0" or "false" to disable the logo in the QR code)
@@ -121,7 +121,14 @@ export class GenerateMeshBVHWorker extends WorkerBase {
121
121
 
122
122
  }, transferable.map( arr => arr.buffer ).filter( v => ( typeof SharedArrayBuffer === 'undefined' ) || ! ( v instanceof SharedArrayBuffer ) ) );
123
123
 
124
- } );
124
+ } ).finally( () => {
125
+
126
+ // Pooled workers outlive each job. Both handlers capture the job scope,
127
+ // including its geometry, even after the promise has settled.
128
+ worker.onmessage = null;
129
+ worker.onerror = null;
130
+
131
+ } );
125
132
 
126
133
  }
127
134
 
@@ -22,7 +22,7 @@ export function loadFont(url: string, opts?: LoadFontOptions) {
22
22
  // crossorigin must match the HTML <link> attribute (set by asap.js) so the browser
23
23
  // can reuse the cached CORS response instead of making a separate non-CORS request
24
24
  fontLink.crossOrigin = "";
25
- fontLink.onload = () => { fontLink.rel = "stylesheet"; };
25
+ fontLink.onload = activateFontStylesheet;
26
26
  element.appendChild(fontLink);
27
27
  elements.push(fontLink);
28
28
  }
@@ -35,9 +35,24 @@ export function loadFont(url: string, opts?: LoadFontOptions) {
35
35
  }
36
36
  }
37
37
  }
38
+ // The link lives in document.head and can outlive the component using this callback.
39
+ return () => {
40
+ if (opts?.loadedCallback) {
41
+ for (const link of elements) link.removeEventListener("load", opts.loadedCallback);
42
+ }
43
+ };
38
44
  }
39
45
 
40
46
 
47
+ // Keep this outside loadFont: its scope includes consumer callbacks and their contexts.
48
+ function activateFontStylesheet(event: Event) {
49
+ const link = event.currentTarget;
50
+ if (link instanceof HTMLLinkElement) {
51
+ link.rel = "stylesheet";
52
+ link.onload = null;
53
+ }
54
+ }
55
+
41
56
  /** Ensure the fonts that needle engine uses are loaded */
42
57
  export function ensureFonts() {
43
58
  loadFont(robotoFlexFontUrl);
@@ -19,7 +19,15 @@ export function getIconElement(str: string): HTMLElement {
19
19
  span.setAttribute("role", "img");
20
20
  span.setAttribute("aria-label", str + " icon");
21
21
  span.setAttribute("aria-hidden", "true");
22
+ updateIconWhenFontReady(new WeakRef(span), str);
23
+ return span;
24
+ }
25
+
26
+ // Pending font loads must not retain detached menu trees.
27
+ function updateIconWhenFontReady(reference: WeakRef<HTMLSpanElement>, str: string) {
22
28
  fontReady(fontname).then(res => {
29
+ const span = reference.deref();
30
+ if (!span) return;
23
31
  if (res) {
24
32
  span.style.visibility = "";
25
33
  span.innerText = str; // re-assign, otherise more_vert doesnt show up. Maybe because of layout restructuring on mobile
@@ -34,7 +42,6 @@ export function getIconElement(str: string): HTMLElement {
34
42
  }
35
43
  }
36
44
  });
37
- return span;
38
45
  }
39
46
 
40
47
  /**@returns true if the element is an needle engine icon element */
@@ -2,7 +2,7 @@ import { Mesh, Object3D, TextureLoader, Vector4 } from "three";
2
2
  import ThreeMeshUI from "three-mesh-ui";
3
3
 
4
4
  import { addNewComponent } from "../../engine_components.js";
5
- import { $uxbFepO } from "../../engine_license.js";
5
+ import { __alnviAJA } from "../../engine_license.js";
6
6
  import { OneEuroFilterXYZ } from "../../engine_math.js";
7
7
  import type { Context } from "../../engine_setup.js";
8
8
  import { lookAtObject } from "../../engine_three_utils.js";
@@ -321,7 +321,7 @@ export class NeedleSpatialMenu {
321
321
  }
322
322
  if (this.menu) {
323
323
  const index = this.menu.children.indexOf(this._poweredByNeedleElement as any);
324
- if (!this._showNeedleLogo && $uxbFepO()) {
324
+ if (!this._showNeedleLogo && __alnviAJA()) {
325
325
  if (index >= 0) {
326
326
  this._poweredByNeedleElement.removeFromParent();
327
327
  this.markDirty();
@@ -1,10 +1,10 @@
1
1
  import { showBalloonMessage } from "../../debug/debug.js";
2
2
  import type { Context } from "../../engine_context.js";
3
- import { $uxbFepO, OLF, Telemetry } from "../../engine_license.js";
3
+ import { __alnviAJA, dRcEhn, Telemetry } from "../../engine_license.js";
4
4
  import { isLocalNetwork } from "../../engine_networking_utils.js";
5
5
  import { HTMLElementBase } from "../../engine_ssr.js";
6
6
  import { DeviceUtilities, getParam } from "../../engine_utils.js";
7
- import { onXRSessionStart, XRSessionEventArgs } from "../../xr/events.js";
7
+ import { offXRSessionStart, onXRSessionStart, XRSessionEventArgs } from "../../xr/events.js";
8
8
  import { ButtonsFactory } from "../buttons.js";
9
9
  import { ensureFonts, iconFontUrl, loadFont } from "../fonts.js";
10
10
  import { getIconElement } from "../icons.js";
@@ -136,7 +136,9 @@ export class NeedleMenu {
136
136
 
137
137
  /** @ignore internal method */
138
138
  onDestroy() {
139
+ offXRSessionStart(this.onStartXR);
139
140
  window.removeEventListener("message", this.onPostMessage);
141
+ this._menu.dispose();
140
142
  this._menu.remove();
141
143
  this._spatialMenu.onDestroy();
142
144
  }
@@ -345,6 +347,19 @@ export class NeedleMenuElement extends HTMLElementBase {
345
347
 
346
348
  private _domElement: HTMLElement | null = null;
347
349
  private _context: Context | null = null;
350
+ private _unsubscribeFont?: () => void;
351
+ private _licenseFrame?: number;
352
+ private _unsubscribeLicense?: () => void;
353
+
354
+ /** @internal Release font and license subscriptions when the owning context is destroyed. */
355
+ dispose() {
356
+ this._unsubscribeFont?.();
357
+ this._unsubscribeFont = undefined;
358
+ if (this._licenseFrame !== undefined) window.cancelAnimationFrame(this._licenseFrame);
359
+ this._licenseFrame = undefined;
360
+ this._unsubscribeLicense?.();
361
+ this._unsubscribeLicense = undefined;
362
+ }
348
363
  private _didInitialize = false;
349
364
 
350
365
  constructor() {
@@ -755,7 +770,7 @@ export class NeedleMenuElement extends HTMLElementBase {
755
770
  // we need to add the icons to both the shadow dom as well as the HEAD to work
756
771
  // https://github.com/google/material-design-icons/issues/1165
757
772
  ensureFonts();
758
- loadFont(iconFontUrl, { loadedCallback: () => { this.handleSizeChange() } });
773
+ this._unsubscribeFont = loadFont(iconFontUrl, { loadedCallback: () => { this.handleSizeChange() } });
759
774
  loadFont(iconFontUrl, { element: shadow });
760
775
 
761
776
  const content = template.content.cloneNode(true) as DocumentFragment;
@@ -784,16 +799,19 @@ export class NeedleMenuElement extends HTMLElementBase {
784
799
  try {
785
800
  // if the user has a license then we CAN hide the needle logo
786
801
  // calling this method immediately will cause an issue with vite bundling tho
787
- window.requestAnimationFrame(() => OLF(res => {
788
- if (res == true && $uxbFepO() && !debugNonCommercial) {
789
- let visible = this._userRequestedLogoVisible;
790
- if (visible === undefined) visible = false;
791
- this.___onSetLogoVisible(visible);
792
- }
793
- else {
794
- this.___onSetLogoVisible(true);
795
- }
796
- }));
802
+ this._licenseFrame = window.requestAnimationFrame(() => {
803
+ this._licenseFrame = undefined;
804
+ this._unsubscribeLicense = dRcEhn(res => {
805
+ if (res == true && __alnviAJA() && !debugNonCommercial) {
806
+ let visible = this._userRequestedLogoVisible;
807
+ if (visible === undefined) visible = false;
808
+ this.___onSetLogoVisible(visible);
809
+ }
810
+ else {
811
+ this.___onSetLogoVisible(true);
812
+ }
813
+ });
814
+ });
797
815
  } catch (e) {
798
816
  console.error("[Needle Menu] License check failed.", e);
799
817
  }
@@ -836,7 +854,7 @@ export class NeedleMenuElement extends HTMLElementBase {
836
854
  // ensure the menu is not hidden or removed
837
855
  const requiredParent = this?.parentNode;
838
856
  if (this.style.display != "flex" || this.style.visibility != "visible" || this.style.opacity != "1" || requiredParent != this._domElement?.shadowRoot) {
839
- if (!$uxbFepO()) {
857
+ if (!__alnviAJA()) {
840
858
  const change = changeEventCounter++;
841
859
  // if a user doesn't have a local pro license *but* for development the menu is hidden then we show a warning
842
860
  if (isLocalNetwork() && this._userRequestedMenuVisible === false) {
@@ -932,7 +950,7 @@ export class NeedleMenuElement extends HTMLElementBase {
932
950
  showNeedleLogo(visible: boolean) {
933
951
  this._userRequestedLogoVisible = visible;
934
952
  if (!visible) {
935
- if (!$uxbFepO() || debugNonCommercial) {
953
+ if (!__alnviAJA() || debugNonCommercial) {
936
954
  console.warn("[Needle Engine] You need a commercial license to hide the Needle Engine logo in production.");
937
955
  const localNetwork = isLocalNetwork()
938
956
  if (!localNetwork) return;
@@ -1,6 +1,6 @@
1
1
  import { needleLogoOnlySVG } from "../assets/index.js"
2
2
  import { isDevEnvironment, showBalloonWarning } from "../debug/index.js";
3
- import { $uxbFepO, _$VfAckLjB, UxRDt } from "../engine_license.js";
3
+ import { __alnviAJA, _$xBhfh, _$bDVSQf } from "../engine_license.js";
4
4
  import { Mathf } from "../engine_math.js";
5
5
  import { LoadingProgressArgs } from "../engine_setup.js";
6
6
  import { getParam } from "../engine_utils.js";
@@ -19,6 +19,7 @@ export class LoadingElementOptions {
19
19
 
20
20
  /** @internal */
21
21
  export interface ILoadingViewHandler {
22
+ dispose?(): void;
22
23
  onLoadingBegin(message?: string)
23
24
  onLoadingUpdate(progress: LoadingProgressArgs | number, message?: string);
24
25
  onLoadingFinished(message?: string);
@@ -79,6 +80,17 @@ export class EngineLoadingView implements ILoadingViewHandler {
79
80
  this._loadingElementOptions = opts;
80
81
  }
81
82
 
83
+ dispose() {
84
+ // Removal can happen before ready stops the progress interval.
85
+ clearInterval(this._progressLoop);
86
+ this._progressLoop = null;
87
+ this._loadingElement?.remove();
88
+ this._loadingElement = undefined;
89
+ this._loadingTextContainer = null;
90
+ this._loadingBar = null;
91
+ this._messageContainer = null;
92
+ }
93
+
82
94
  async onLoadingBegin(message?: string) {
83
95
  const _element = this._element.shadowRoot || this._element;
84
96
  if (debug) console.warn("Begin Loading")
@@ -205,7 +217,7 @@ export class EngineLoadingView implements ILoadingViewHandler {
205
217
  }
206
218
 
207
219
 
208
- const hasLicense = _$VfAckLjB();
220
+ const hasLicense = _$xBhfh();
209
221
  if (!existing) {
210
222
  this._loadingElement.style.position = "absolute";
211
223
  this._loadingElement.style.width = "100%";
@@ -373,7 +385,7 @@ export class EngineLoadingView implements ILoadingViewHandler {
373
385
 
374
386
  // private async handleRuntimeLicense(loadingElement: HTMLElement) {
375
387
  // // First check if we have a commercial license
376
- // let commercialLicense = $uxbFepO();
388
+ // let commercialLicense = __alnviAJA();
377
389
  // // if it's the case then we don't need to perform a runtime check
378
390
  // if (commercialLicense) return;
379
391
 
@@ -393,13 +405,13 @@ export class EngineLoadingView implements ILoadingViewHandler {
393
405
  // loadingElement.appendChild(nonCommercialContainer);
394
406
 
395
407
  // // Use the runtime license check
396
- // if (!isDevEnvironment() && UxRDt) {
408
+ // if (!isDevEnvironment() && _$bDVSQf) {
397
409
  // if (debugLicense) console.log("Waiting for runtime license check");
398
- // await UxRDt;
399
- // commercialLicense = $uxbFepO();
410
+ // await _$bDVSQf;
411
+ // commercialLicense = __alnviAJA();
400
412
  // }
401
413
  // if (commercialLicense) return;
402
414
  // nonCommercialContainer.style.transition = "opacity .5s ease-in-out";
403
415
  // nonCommercialContainer.style.opacity = "1";
404
416
  // }
405
- }
417
+ }
@@ -3,7 +3,7 @@ import type { Texture } from "three";
3
3
  import { isDevEnvironment, showBalloonWarning } from "../debug/index.js";
4
4
  import { PUBLIC_KEY, VERSION } from "../engine_constants.js";
5
5
  import { ContextEvent, ContextRegistry } from "../engine_context_registry.js";
6
- import { $uxbFepO, Telemetry } from "../engine_license.js";
6
+ import { __alnviAJA, Telemetry } from "../engine_license.js";
7
7
  import { onStart } from "../engine_lifecycle_api.js";
8
8
  import { setDracoDecoderPath, setDracoDecoderType, setKtx2TranscoderPath } from "../engine_loaders.gltf.js";
9
9
  import { Context, ContextCreateArgs } from "../engine_setup.js";
@@ -248,6 +248,7 @@ export class NeedleEngineWebComponent extends HTMLElementBase implements INeedle
248
248
  private _overlay_ar!: AROverlayHandler;
249
249
  private _loadingProgress01: number = 0;
250
250
  private _loadingView?: ILoadingViewHandler;
251
+ private _cancelLoadingBlur?: () => void;
251
252
  private _previousSrc: string | null | string[] = null;
252
253
  /** @private set to true after <needle-engine> did load completely at least once. Set to false when < to false when <needle-engine> is removed from the document removed from the document */
253
254
  private _didFullyLoad: boolean = false;
@@ -419,6 +420,9 @@ export class NeedleEngineWebComponent extends HTMLElementBase implements INeedle
419
420
  const dispose = keepAlive == undefined || (keepAlive?.length > 0 && keepAlive !== "true" && keepAlive !== "1");
420
421
  if (debug) console.warn("<needle-engine> disconnected, keep-alive: \"" + keepAlive + "\"", typeof keepAlive, "Dispose=", dispose);
421
422
  if (dispose) {
423
+ this._cancelLoadingBlur?.();
424
+ this._cancelLoadingBlur = undefined;
425
+ this._loadingView?.dispose?.();
422
426
  if (debug)
423
427
  console.warn("<needle-engine> dispose");
424
428
  this._context?.dispose();
@@ -579,7 +583,7 @@ export class NeedleEngineWebComponent extends HTMLElementBase implements INeedle
579
583
 
580
584
 
581
585
  // Loading start events
582
- const allowOverridingDefaultLoading = $uxbFepO();
586
+ const allowOverridingDefaultLoading = __alnviAJA();
583
587
  // default loading can be overriden by calling preventDefault in the onload start event
584
588
  this.ensureLoadStartIsRegistered();
585
589
  let useDefaultLoading = this.dispatchEvent(new CustomEvent("loadstart", {
@@ -670,7 +674,8 @@ export class NeedleEngineWebComponent extends HTMLElementBase implements INeedle
670
674
 
671
675
 
672
676
  // Experimental loading blur
673
- handleLoadingBlur(this);
677
+ this._cancelLoadingBlur?.();
678
+ this._cancelLoadingBlur = handleLoadingBlur(this);
674
679
 
675
680
  const currentHash = this.getAttribute("hash");
676
681
  if (currentHash !== null && currentHash !== undefined)
@@ -1205,8 +1210,12 @@ function getDisplayName(str: string) {
1205
1210
 
1206
1211
 
1207
1212
  function handleLoadingBlur(needleEngineElement: NeedleEngineWebComponent) {
1208
- onStart((ctx) => {
1209
- const userBlurSetting = needleEngineElement.getAttribute("loading-blur");
1213
+ const userBlurSetting = needleEngineElement.getAttribute("loading-blur");
1214
+ if (userBlurSetting === null || userBlurSetting === "0") return undefined;
1215
+ const unregister = onStart((ctx) => {
1216
+ // A different context starting must not consume this element's callback.
1217
+ if (ctx.domElement !== needleEngineElement) return;
1218
+ unregister();
1210
1219
  if (userBlurSetting !== null && userBlurSetting !== "0") {
1211
1220
  if (ctx.domElement === needleEngineElement) {
1212
1221
 
@@ -1259,5 +1268,6 @@ function handleLoadingBlur(needleEngineElement: NeedleEngineWebComponent) {
1259
1268
  }
1260
1269
  }
1261
1270
  }
1262
- }, { once: true });
1271
+ });
1272
+ return unregister;
1263
1273
  }
@@ -111,19 +111,20 @@ async function handleSessionGranted() {
111
111
  // wasn't granted it), XR can't run here — skip the probe so we don't log a
112
112
  // Permissions-Policy violation on load.
113
113
  if (!isXrSpatialTrackingAllowed()) return;
114
- // In app clips we default to AR
114
+ // An App Clip has already granted AR. Register its sessiongranted listener
115
+ // synchronously: awaiting a support probe here can miss the native event.
115
116
  if (DeviceUtilities.isNeedleAppClip()) {
116
117
  defaultMode = "immersive-ar";
117
118
  }
118
- // Check if VR is even supported, otherwise try AR
119
- else if (!(await navigator.xr?.isSessionSupported("immersive-vr"))) {
120
- defaultMode = "immersive-ar";
121
- }
122
- // Check if AR is supported, otherwise we can't do anything
123
- if (!(await navigator.xr?.isSessionSupported("immersive-ar")) && defaultMode === "immersive-ar") {
124
- // console.warn("[NeedleXRSession:granted] Neither VR nor AR supported, aborting session start.");
125
- // showBalloonMessage("NeidleXRSession: Neither VR nor AR supported, aborting session start.");
126
- return;
119
+ else {
120
+ // Check if VR is even supported, otherwise try AR.
121
+ if (!(await navigator.xr?.isSessionSupported("immersive-vr"))) {
122
+ defaultMode = "immersive-ar";
123
+ }
124
+ // Check if AR is supported, otherwise we can't do anything.
125
+ if (defaultMode === "immersive-ar" && !(await navigator.xr?.isSessionSupported("immersive-ar"))) {
126
+ return;
127
+ }
127
128
  }
128
129
  } catch (e) {
129
130
  console.debug("[NeedleXRSession:granted] Error while checking XR support:", e);
@@ -3,7 +3,7 @@ import { ArrayCamera, AxesHelper, Camera, Color, DirectionalLight, Fog, GridHelp
3
3
  import { needleLogoOnlySVG } from "../assets/index.js";
4
4
  import { isDevEnvironment } from "../debug/index.js";
5
5
  import { ObjectUtils, PrimitiveType } from "../engine_create_objects.js";
6
- import { $uxbFepO } from "../engine_license.js";
6
+ import { __alnviAJA } from "../engine_license.js";
7
7
  import { Mathf } from "../engine_math.js";
8
8
  import { delay, DeviceUtilities } from "../engine_utils.js";
9
9
 
@@ -215,7 +215,7 @@ export class TemporaryXRContext {
215
215
  this._scene.background = new Color(0x000000);
216
216
 
217
217
  let logoSrc = needleLogoOnlySVG;
218
- if ($uxbFepO()) {
218
+ if (__alnviAJA()) {
219
219
  const htmlComponent = document.querySelector("needle-engine");
220
220
  if (htmlComponent) {
221
221
  const licenseLogo = htmlComponent.getAttribute("logo-src");
@@ -1,8 +1,8 @@
1
+ import type { Camera, InterpolationModes, Light, Material, PerspectiveCamera } from "three";
1
2
  import { AnimationClip, BooleanKeyframeTrack, Color, ColorKeyframeTrack, Euler,
2
3
  InterpolateDiscrete, InterpolateLinear, InterpolateSmooth, KeyframeTrack,
3
4
  NumberKeyframeTrack, Object3D, Quaternion, QuaternionKeyframeTrack,
4
5
  Vector3, VectorKeyframeTrack } from "three";
5
- import type { Camera, InterpolationModes, Light, Material, PerspectiveCamera } from "three";
6
6
 
7
7
  import { isDevEnvironment } from "../engine/debug/index.js";
8
8
 
@@ -1,11 +1,11 @@
1
- import { AnimationClip, Object3D } from "three";
2
1
  import type { Light, Material, PerspectiveCamera } from "three";
2
+ import { AnimationClip, Object3D } from "three";
3
3
 
4
- import { AnimatorConditionMode, AnimatorControllerParameterType } from "../engine/extensions/NEEDLE_animator_controller_model.js";
5
- import type { AnimatorControllerModel, Condition, Parameter, State, Transition } from "../engine/extensions/NEEDLE_animator_controller_model.js";
6
4
  import { InstantiateIdProvider } from "../engine/engine_networking_instantiate.js";
5
+ import type { AnimatorControllerModel, Condition, Parameter, State, Transition } from "../engine/extensions/NEEDLE_animator_controller_model.js";
6
+ import { AnimatorConditionMode, AnimatorControllerParameterType } from "../engine/extensions/NEEDLE_animator_controller_model.js";
7
+ import { type AnimationKeyframe, type ColorValue,type EulerValue, type QuatValue, resolveClipSource, track as trackFn, type TrackDescriptor, type TrackOptions, type Tween, type Vec3Value } from "./AnimationBuilder.js";
7
8
  import { AnimatorController } from "./AnimatorController.js";
8
- import { resolveClipSource, track as trackFn, type TrackDescriptor, type TrackOptions, type AnimationKeyframe, type Tween, type Vec3Value, type QuatValue, type EulerValue, type ColorValue } from "./AnimationBuilder.js";
9
9
 
10
10
  /** Keyframe array or tween shorthand */
11
11
  type KF<V> = AnimationKeyframe<V>[] | Tween<V>;
@@ -4,6 +4,7 @@ import { isDevEnvironment } from "../engine/debug/index.js";
4
4
  import { type AssetReference } from "../engine/engine_addressables.js";
5
5
  import { addComponent, destroyComponentInstance, findObjectOfType, findObjectsOfType, getComponent, getComponentInChildren, getComponentInParent, getComponents, getComponentsInChildren, getComponentsInParent, getOrAddComponent, removeComponent } from "../engine/engine_components.js";
6
6
  import { activeInHierarchyFieldName } from "../engine/engine_constants.js";
7
+ import { DisposableStore, type DisposeFn,type IDisposable } from "../engine/engine_disposable.js";
7
8
  import { destroy, findByGuid, foreachComponent, type IInstantiateOptions, instantiate, isActiveInHierarchy, isActiveSelf, isDestroyed, isUsingInstancing, markAsInstancedRendered, setActive } from "../engine/engine_gameobject.js";
8
9
  import { isHotReloadEnabled, registerHotReloadType, unregisterHotReloadType } from "../engine/engine_hot_reload.js";
9
10
  import * as main from "../engine/engine_mainloop_utils.js";
@@ -13,7 +14,6 @@ import * as threeutils from "../engine/engine_three_utils.js";
13
14
  import { $componentName, type Collision, type ComponentInit, type Constructor, type ConstructorConcrete, type ICollider, type IComponent, type IGameObject, type SourceIdentifier } from "../engine/engine_types.js";
14
15
  import { TypeStore } from "../engine/engine_typestore.js";
15
16
  import type { INeedleXRSessionEventReceiver, NeedleXRControllerEventArgs, NeedleXREventArgs } from "../engine/engine_xr.js";
16
- import { DisposableStore, type IDisposable, type DisposeFn } from "../engine/engine_disposable.js";
17
17
  import { type IPointerEventHandler, PointerEventData } from "./ui/PointerEvents.js";
18
18
 
19
19
 
@@ -1,9 +1,7 @@
1
1
  import { AxesHelper, Box3, Euler, Matrix4, Object3D, Plane, Quaternion, Ray, Vector3 } from "three";
2
2
 
3
3
  export type { IDragConstraint, IDragConstraintContext } from "./DragControlsConstraints.js";
4
- export { AxisRotationConstraint, GridSnapConstraint, GrabPointPlaneConstraint, KeepRotationConstraint, KeepScaleConstraint, ScaleLimitConstraint, RotationAxis, FixedRotationAxesConstraint, PlaneHeightLockConstraint, SnapToSurfaceConstraint, applyFollowObjectConstraints } from "./DragControlsConstraints.js";
5
- import { IDragConstraint, IDragConstraintContext, AxisRotationConstraint, GridSnapConstraint, GrabPointPlaneConstraint, KeepRotationConstraint, ScaleLimitConstraint, FixedRotationAxesConstraint, PlaneHeightLockConstraint, SnapToSurfaceConstraint, applyFollowObjectConstraints } from "./DragControlsConstraints.js";
6
-
4
+ export { applyFollowObjectConstraints,AxisRotationConstraint, FixedRotationAxesConstraint, GrabPointPlaneConstraint, GridSnapConstraint, KeepRotationConstraint, KeepScaleConstraint, PlaneHeightLockConstraint, RotationAxis, ScaleLimitConstraint, SnapToSurfaceConstraint } from "./DragControlsConstraints.js";
7
5
  import { Gizmos } from "../engine/engine_gizmos.js";
8
6
  import { InstancingUtil } from "../engine/engine_instancing.js";
9
7
  import { Mathf } from "../engine/engine_math.js";
@@ -14,6 +12,7 @@ import { type IGameObject } from "../engine/engine_types.js";
14
12
  import { getParam } from "../engine/engine_utils.js";
15
13
  import { NeedleXRSession } from "../engine/engine_xr.js";
16
14
  import { Behaviour, GameObject } from "./Component.js";
15
+ import { applyFollowObjectConstraints,AxisRotationConstraint, FixedRotationAxesConstraint, GrabPointPlaneConstraint, GridSnapConstraint, IDragConstraint, IDragConstraintContext, KeepRotationConstraint, PlaneHeightLockConstraint, ScaleLimitConstraint, SnapToSurfaceConstraint } from "./DragControlsConstraints.js";
17
16
  import { EventList } from "./EventList.js";
18
17
  import { UsageMarker } from "./Interactable.js";
19
18
  import { Rigidbody } from "./RigidBody.js";
@@ -1,9 +1,8 @@
1
1
  import { Box3, Euler, Object3D, Plane, Quaternion, Ray, Vector3 } from "three";
2
2
 
3
3
  import type { Context } from "../engine/engine_setup.js";
4
-
5
- import { GameObject } from "./Component.js";
6
4
  import type { IGameObject } from "../engine/engine_types.js";
5
+ import { GameObject } from "./Component.js";
7
6
 
8
7
  // #region Interfaces
9
8
  /**
@@ -1,12 +1,12 @@
1
1
  import { CameraHelper, Color, DirectionalLight, DirectionalLightHelper, Light as ThreeLight, OrthographicCamera, PointLight, SpotLight, Vector3 } from "three";
2
2
 
3
+ import { isDevEnvironment } from "../engine/debug/index.js";
3
4
  import { serializable } from "../engine/engine_serialization_decorator.js";
4
5
  import { setWorldPositionXYZ } from "../engine/engine_three_utils.js";
5
6
  import type { ILight } from "../engine/engine_types.js";
6
7
  import { getParam } from "../engine/engine_utils.js";
7
8
  import type { NeedleXREventArgs } from "../engine/xr/api.js";
8
9
  import { Behaviour, GameObject } from "./Component.js";
9
- import { isDevEnvironment } from "../engine/debug/index.js";
10
10
 
11
11
  // https://threejs.org/examples/webgl_shadowmap_csm.html
12
12