@needle-tools/engine 5.1.12 → 5.1.13

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 (85) hide show
  1. package/CHANGELOG.md +5 -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/{needle-engine.bundle-DdH96IqH.umd.cjs → needle-engine.bundle-B_ZctaDt.umd.cjs} +146 -146
  7. package/dist/{needle-engine.bundle-BmdWLmLa.min.js → needle-engine.bundle-DR967VjU.min.js} +145 -145
  8. package/dist/{needle-engine.bundle-9m-VdGg2.js → needle-engine.bundle-DTvJ1zCJ.js} +7302 -7153
  9. package/dist/needle-engine.d.ts +23 -15
  10. package/dist/needle-engine.js +517 -518
  11. package/dist/needle-engine.min.js +1 -1
  12. package/dist/needle-engine.umd.cjs +1 -1
  13. package/lib/engine/api.d.ts +4 -3
  14. package/lib/engine/api.js +3 -9
  15. package/lib/engine/api.js.map +1 -1
  16. package/lib/engine/engine_addressables.d.ts +32 -1
  17. package/lib/engine/engine_addressables.js +71 -9
  18. package/lib/engine/engine_addressables.js.map +1 -1
  19. package/lib/engine/engine_application.d.ts +2 -0
  20. package/lib/engine/engine_application.js +7 -3
  21. package/lib/engine/engine_application.js.map +1 -1
  22. package/lib/engine/engine_context.js +25 -1
  23. package/lib/engine/engine_context.js.map +1 -1
  24. package/lib/engine/engine_init.js +2 -2
  25. package/lib/engine/engine_input.js +1 -1
  26. package/lib/engine/engine_input.js.map +1 -1
  27. package/lib/engine/engine_license.d.ts +7 -7
  28. package/lib/engine/engine_license.js +121 -78
  29. package/lib/engine/engine_license.js.map +1 -1
  30. package/lib/engine/engine_lifecycle_functions_internal.js +20 -2
  31. package/lib/engine/engine_lifecycle_functions_internal.js.map +1 -1
  32. package/lib/engine/engine_networking_blob.js +3 -3
  33. package/lib/engine/engine_networking_blob.js.map +1 -1
  34. package/lib/engine/engine_networking_streams.d.ts +3 -0
  35. package/lib/engine/engine_networking_streams.js +64 -17
  36. package/lib/engine/engine_networking_streams.js.map +1 -1
  37. package/lib/engine/engine_utils_qrcode.js +2 -2
  38. package/lib/engine/engine_utils_qrcode.js.map +1 -1
  39. package/lib/engine/physics/workers/mesh-bvh/GenerateMeshBVHWorker.js +5 -0
  40. package/lib/engine/physics/workers/mesh-bvh/GenerateMeshBVHWorker.js.map +1 -1
  41. package/lib/engine/webcomponents/fonts.d.ts +1 -1
  42. package/lib/engine/webcomponents/fonts.js +16 -1
  43. package/lib/engine/webcomponents/fonts.js.map +1 -1
  44. package/lib/engine/webcomponents/icons.js +8 -1
  45. package/lib/engine/webcomponents/icons.js.map +1 -1
  46. package/lib/engine/webcomponents/needle menu/needle-menu-spatial.js +2 -2
  47. package/lib/engine/webcomponents/needle menu/needle-menu-spatial.js.map +1 -1
  48. package/lib/engine/webcomponents/needle menu/needle-menu.d.ts +5 -0
  49. package/lib/engine/webcomponents/needle menu/needle-menu.js +34 -16
  50. package/lib/engine/webcomponents/needle menu/needle-menu.js.map +1 -1
  51. package/lib/engine/webcomponents/needle-engine.d.ts +1 -0
  52. package/lib/engine/webcomponents/needle-engine.js +18 -6
  53. package/lib/engine/webcomponents/needle-engine.js.map +1 -1
  54. package/lib/engine/webcomponents/needle-engine.loading.d.ts +2 -0
  55. package/lib/engine/webcomponents/needle-engine.loading.js +12 -2
  56. package/lib/engine/webcomponents/needle-engine.loading.js.map +1 -1
  57. package/lib/engine/xr/TempXRContext.js +2 -2
  58. package/lib/engine/xr/TempXRContext.js.map +1 -1
  59. package/lib/engine-components/export/usdz/USDZExporter.js +4 -4
  60. package/lib/engine-components/export/usdz/USDZExporter.js.map +1 -1
  61. package/package.json +2 -2
  62. package/plugins/common/license.js +4 -4
  63. package/plugins/vite/license.js +4 -4
  64. package/src/engine/api.ts +5 -3
  65. package/src/engine/engine_addressables.ts +75 -9
  66. package/src/engine/engine_application.ts +7 -2
  67. package/src/engine/engine_context.ts +25 -1
  68. package/src/engine/engine_init.ts +2 -2
  69. package/src/engine/engine_input.ts +2 -2
  70. package/src/engine/engine_license.ts +113 -75
  71. package/src/engine/engine_lifecycle_functions_internal.ts +21 -2
  72. package/src/engine/engine_networking_blob.ts +3 -3
  73. package/src/engine/engine_networking_streams.ts +61 -17
  74. package/src/engine/engine_utils_qrcode.ts +2 -2
  75. package/src/engine/physics/workers/mesh-bvh/GenerateMeshBVHWorker.js +8 -1
  76. package/src/engine/webcomponents/fonts.ts +16 -1
  77. package/src/engine/webcomponents/icons.ts +8 -1
  78. package/src/engine/webcomponents/needle menu/needle-menu-spatial.ts +2 -2
  79. package/src/engine/webcomponents/needle menu/needle-menu.ts +33 -15
  80. package/src/engine/webcomponents/needle-engine.loading.ts +19 -7
  81. package/src/engine/webcomponents/needle-engine.ts +16 -6
  82. package/src/engine/xr/TempXRContext.ts +2 -2
  83. package/src/engine-components/export/usdz/USDZExporter.ts +4 -4
  84. package/dist/gltf-progressive-CdjpU5eP.min.js +0 -10
  85. package/dist/gltf-progressive-DeZioRZe.umd.cjs +0 -10
@@ -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 { __PemN } 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 && __PemN()) {
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 { __PemN, _$EPASa, 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 = _$EPASa(res => {
805
+ if (res == true && __PemN() && !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 (!__PemN()) {
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 (!__PemN() || 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 { __PemN, __csRKN, _$zwcLl } 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 = __csRKN();
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 = __PemN();
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() && _$zwcLl) {
397
409
  // if (debugLicense) console.log("Waiting for runtime license check");
398
- // await UxRDt;
399
- // commercialLicense = $uxbFepO();
410
+ // await _$zwcLl;
411
+ // commercialLicense = __PemN();
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 { __PemN, 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 = __PemN();
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
  }
@@ -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 { __PemN } 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 (__PemN()) {
219
219
  const htmlComponent = document.querySelector("needle-engine");
220
220
  if (htmlComponent) {
221
221
  const licenseLogo = htmlComponent.getAttribute("logo-src");
@@ -3,7 +3,7 @@ import { Euler, Material, Matrix4, Mesh, Object3D, Quaternion, Vector3 } from "t
3
3
 
4
4
  import { isDevEnvironment, showBalloonMessage, showBalloonWarning } from "../../../engine/debug/index.js";
5
5
  import { findObjectOfType } from "../../../engine/engine_components.js";
6
- import { _$VfAckLjB } from "../../../engine/engine_license.js";
6
+ import { __csRKN } from "../../../engine/engine_license.js";
7
7
  import { serializable } from "../../../engine/engine_serialization.js";
8
8
  import { getFormattedDate, Progress } from "../../../engine/engine_time_utils.js";
9
9
  import { DeviceUtilities, getParam } from "../../../engine/engine_utils.js";
@@ -277,7 +277,7 @@ export class USDZExporter extends Behaviour {
277
277
  let name = this.exportFileName ?? this.objectToExport?.name ?? this.name;
278
278
  name += "-" + getFormattedDate(); // seems iOS caches the file in some cases, this ensures we always have a fresh file
279
279
 
280
- if (!_$VfAckLjB()) {
280
+ if (!__csRKN()) {
281
281
  if (name !== "") name += "-";
282
282
  name += "MadeWithNeedle";
283
283
  }
@@ -682,7 +682,7 @@ export class USDZExporter extends Behaviour {
682
682
  if (debug)
683
683
  showBalloonMessage("Quicklook url: " + callToActionURL);
684
684
  if (callToActionURL) {
685
- if (!_$VfAckLjB()) {
685
+ if (!__csRKN()) {
686
686
  console.warn("Quicklook closed: custom redirects require a Needle Engine Pro license: https://needle.tools/pricing", callToActionURL)
687
687
  }
688
688
  else {
@@ -697,7 +697,7 @@ export class USDZExporter extends Behaviour {
697
697
  private buildQuicklookOverlay(): CustomBranding {
698
698
  const obj: CustomBranding = {};
699
699
  if (this.customBranding) Object.assign(obj, this.customBranding);
700
- if (!_$VfAckLjB()) {
700
+ if (!__csRKN()) {
701
701
  console.log("Custom Quicklook banner text requires pro license: https://needle.tools/pricing");
702
702
  obj.callToAction = "Close";
703
703
  obj.checkoutTitle = "🌵 Made with Needle";
@@ -1,10 +0,0 @@
1
- import{RedFormat as Qe,RedIntegerFormat as Je,RGFormat as Ze,RGIntegerFormat as et,RGBFormat as tt,RGBAFormat as rt,RGBAIntegerFormat as st,BufferGeometry as z,Mesh as V,Box3 as ie,Vector3 as k,Sphere as Oe,CompressedTexture as ot,Texture as I,Matrix3 as nt,InterleavedBuffer as it,InterleavedBufferAttribute as at,BufferAttribute as lt,TextureLoader as ut,Matrix4 as ke,THREE as ct,Color as Re}from"./three-core.min.js";import{DRACOLoader as dt,KTX2Loader as ht,MeshoptDecoder as gt,GLTFLoader as he}from"./three-examples.min.js";const ft="";globalThis.GLTF_PROGRESSIVE_VERSION=ft,console.debug("[gltf-progressive] version -");let P="https://www.gstatic.com/draco/versioned/decoders/1.5.7/",H="https://cdn.needle.tools/static/three/0.179.1/basis2/";const mt=P,pt=H,Pe=new URL(P+"draco_decoder.js");Pe.searchParams.append("range","true"),fetch(Pe,{method:"GET",headers:{Range:"bytes=0-1"}}).catch(n=>{console.debug(`Failed to fetch remote Draco decoder from ${P} (offline: ${typeof navigator<"u"?navigator.onLine:"unknown"})`),P===mt&&Ce("./include/draco/"),H===pt&&Ae("./include/ktx2/")}).finally(()=>{Ie()});const xt=()=>({dracoDecoderPath:P,ktx2TranscoderPath:H});function Ce(n){P=n,O&&O[fe]!=P?(console.debug("Updating Draco decoder path to "+n),O[fe]=P,O.setDecoderPath(P),O.preload()):console.debug("Setting Draco decoder path to "+n)}function Ae(n){H=n,C&&C.transcoderPath!=H?(console.debug("Updating KTX2 transcoder path to "+n),C.setTranscoderPath(H),C.init()):console.debug("Setting KTX2 transcoder path to "+n)}function ae(n){return Ie(),n?C.detectSupport(n):n!==null&&console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures might fail"),{dracoLoader:O,ktx2Loader:C,meshoptDecoder:le}}function ge(n){n.dracoLoader||n.setDRACOLoader(O),n.ktx2Loader||n.setKTX2Loader(C),n.meshoptDecoder||n.setMeshoptDecoder(le)}const fe=Symbol("dracoDecoderPath");let O,le,C;function Ie(){O||(O=new dt,O[fe]=P,O.setDecoderPath(P),O.setDecoderConfig({type:"js"}),O.preload()),C||(C=new ht,C.setTranscoderPath(H),C.init()),le||(le=gt)}const me=new WeakMap;function pe(n,t){let e=me.get(n);e?e=Object.assign(e,t):e=t,me.set(n,e)}const yt=he.prototype.load;function vt(...n){const t=me.get(this);let e=n[0];const r=wt(e);if(r?.hostname.endsWith("needle.tools")){const s=t?.progressive!==void 0?t.progressive:!0,o=t?.usecase?t.usecase:"default";s?this.requestHeader.Accept=`*/*;progressive=allowed;usecase=${o}`:this.requestHeader.Accept=`*/*;usecase=${o}`,e=r.toString()}return n[0]=e,yt?.call(this,...n)}he.prototype.load=vt;function wt(n){try{if(n instanceof URL)return n;const t=globalThis.location?.href;return t?new URL(n,t):new URL(n)}catch{return null}}function Ee(n){return n!=null&&n.data!=null}function xe(n){const t=n.source?.data;return t!=null&&typeof t=="object"?t:null}function Lt(n){const t=n.image;return t!=null&&typeof t=="object"?t:null}function ye(n){const t=Lt(n),e=xe(n);return{width:t?.width||e?.width||0,height:t?.height||e?.height||0}}q("debugprogressive");function q(n){const t=globalThis.location?.href;if(!t)return!1;const e=new URL(t).searchParams.get(n);return e==null||e==="0"||e==="false"?!1:e===""?!0:e}function bt(n,t){if(t===void 0||n===void 0||t.startsWith("./")||t.startsWith("http")||t.startsWith("data:")||t.startsWith("blob:"))return t;const e=n.lastIndexOf("/");if(e>=0){const r=n.substring(0,e+1);for(;r.endsWith("/")&&t.startsWith("/");)t=t.substring(1);return r+t}return t}function ve(){if(ee!==void 0)return ee;const n=globalThis.navigator?.userAgent||"";return ee=/iPhone|iPad|iPod|Android|IEMobile/i.test(n),q("debugprogressive")&&console.log("[glTF Progressive]: isMobileDevice",ee),ee}let ee;function $e(){const n=globalThis.location?.href;if(!n)return!1;const t=new URL(n),e=t.hostname==="localhost"||/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(t.hostname);return t.hostname==="127.0.0.1"||e}class Mt{constructor(t,e={}){this.maxConcurrent=t,this.debug=e.debug??!1,typeof globalThis.requestAnimationFrame=="function"?globalThis.requestAnimationFrame(this.tick):setTimeout(this.tick,0)}_running=new Map;_queue=[];debug=!1;tick=()=>{this.internalUpdate(),setTimeout(this.tick,10)};slot(t){return this.debug&&console.debug(`[PromiseQueue]: Requesting slot for key ${t}, running: ${this._running.size}, waiting: ${this._queue.length}`),new Promise(e=>{this._queue.push({key:t,resolve:e})})}add(t,e){this._running.has(t)||(this._running.set(t,e),e.finally(()=>{this._running.delete(t),this.debug&&console.debug(`[PromiseQueue]: Promise finished now running: ${this._running.size}, waiting: ${this._queue.length}. (finished ${t})`)}),this.debug&&console.debug(`[PromiseQueue]: Added new promise, now running: ${this._running.size}, waiting: ${this._queue.length}. (added ${t})`))}internalUpdate(){const t=this.maxConcurrent-this._running.size;for(let e=0;e<t&&this._queue.length>0;e++){this.debug&&console.debug(`[PromiseQueue]: Running ${this._running.size} promises, waiting for ${this._queue.length} more.`);const{key:r,resolve:s}=this._queue.shift();s({use:o=>this.add(r,o)})}}}function Dt(n){const t=n.image,e=t?.width??0,r=t?.height??0,s=t?.depth??1,o=Math.floor(Math.log2(Math.max(e,r,s)))+1,i=_t(n);return e*r*s*i*(1-Math.pow(.25,o))/(1-.25)}function _t(n){let t=4;const e=n.format;e===Qe||e===Je?t=1:e===Ze||e===et?t=2:e===tt||e===1029?t=3:(e===rt||e===st)&&(t=4);let r=1;const s=n.type;return s===1009||s===1010?r=1:s===1011||s===1012?r=2:s===1013||s===1014||s===1015?r=4:s===1016&&(r=2),t*r}const Tt=typeof window>"u"&&typeof document>"u",we=Symbol("needle:raycast-mesh");function Y(n){return n?.[we]instanceof z?n[we]:null}function St(n,t){if((n.type==="Mesh"||n.type==="SkinnedMesh")&&!Y(n)){const e=kt(t);e.userData={isRaycastMesh:!0},n[we]=e}}function Ot(n=!0){if(n){if(te)return;const t=te=V.prototype.raycast;V.prototype.raycast=function(e,r){const s=this,o=Y(s);let i;o&&s.isMesh&&(i=s.geometry,s.geometry=o),t.call(this,e,r),i&&(s.geometry=i)}}else{if(!te)return;V.prototype.raycast=te,te=null}}let te=null;function kt(n){const t=new z;for(const e in n.attributes)t.setAttribute(e,n.getAttribute(e));return t.setIndex(n.getIndex()),t}const $=new Array,g=q("debugprogressive");let re,X=-1;if(g&&typeof window<"u"){let n=function(){X+=1,X>=t&&(X=-1),console.log(`Toggle LOD level [${X}]`)};window.addEventListener("keyup",e=>{e.key==="p"&&n(),e.key==="w"&&(re=!re,console.log(`Toggle wireframe [${re}]`));const r=parseInt(e.key);!isNaN(r)&&r>=0&&(X=r,console.log(`Set LOD level to [${X}]`))})}function Be(n){if(g&&re!==void 0)if(Array.isArray(n))for(const t of n)Be(t);else n&&"wireframe"in n&&(n.wireframe=re===!0)}const se=new Array;let Rt=0;const Pt=ve()?2:10;function Ct(n){if(se.length<Pt){const e=se.length;g&&console.warn(`[Worker] Creating new worker #${e}`);const r=Se.createWorker(n||{});return se.push(r),r}const t=Rt++%se.length;return se[t]}class Se{constructor(t,e){this.worker=t,this._debug=e.debug??!1,t.onmessage=r=>{const s=r.data;switch(this._debug&&console.log("[Worker] EVENT",s),s.type){case"loaded-gltf":for(const o of this._running)if(o.url===s.result.url){At(s.result),o.resolve(s.result);const i=o.url;i.startsWith("blob:")&&URL.revokeObjectURL(i)}}},t.onerror=r=>{console.error("[Worker] Error in gltf-progressive worker:",r)},t.postMessage({type:"init"})}static async createWorker(t){const e=new Worker(URL.createObjectURL(new Blob([`import '${new URL("./gltf-progressive.worker-BqODMeeW.js",import.meta.url).toString()}';`],{type:"text/javascript"})),{type:"module"});return new Se(e,t)}_running=[];_webglRenderer=null;async load(t,e){const r=xt();let s=e?.renderer;s||(this._webglRenderer??=(async()=>{const{WebGLRenderer:a}=await import("./three-core.min.js").then(l=>l.THREE);return new a})(),s=await this._webglRenderer);const o=ae(s).ktx2Loader.workerConfig;if(t instanceof URL)t=t.toString();else if(t.startsWith("file:"))t=URL.createObjectURL(new Blob([t]));else if(!t.startsWith("blob:")&&!t.startsWith("http:")&&!t.startsWith("https:")){const a=globalThis.location?.href;a&&(t=new URL(t,a).toString())}const i={type:"load",url:t,dracoDecoderPath:r.dracoDecoderPath,ktx2TranscoderPath:r.ktx2TranscoderPath,ktx2LoaderConfig:o};return this._debug&&console.debug("[Worker] Sending load request",i),this.worker.postMessage(i),new Promise(a=>{this._running.push({url:t.toString(),resolve:a})})}_debug=!1}function At(n){for(const t of n.geometries){const e=t.geometry,r=new z;if(r.name=e.name||"",e.index){const s=e.index;r.setIndex(Le(s))}for(const s in e.attributes){const o=e.attributes[s],i=Le(o);r.setAttribute(s,i)}if(e.morphAttributes)for(const s in e.morphAttributes){const o=e.morphAttributes[s].map(i=>Le(i));r.morphAttributes[s]=o}if(r.morphTargetsRelative=e.morphTargetsRelative??!1,r.boundingBox=new ie,r.boundingBox.min=new k(e.boundingBox?.min.x,e.boundingBox?.min.y,e.boundingBox?.min.z),r.boundingBox.max=new k(e.boundingBox?.max.x,e.boundingBox?.max.y,e.boundingBox?.max.z),r.boundingSphere=new Oe(new k(e.boundingSphere?.center.x,e.boundingSphere?.center.y,e.boundingSphere?.center.z),e.boundingSphere?.radius),e.groups)for(const s of e.groups)r.addGroup(s.start,s.count,s.materialIndex);e.userData&&(r.userData=e.userData),t.geometry=r}for(const t of n.textures){const e=t.texture;let r=null;if(e.isCompressedTexture){const s=e.mipmaps,{width:o,height:i}=ye(e);r=new ot(s,o,i,e.format,e.type,e.mapping,e.wrapS,e.wrapT,e.magFilter,e.minFilter,e.anisotropy,e.colorSpace)}else r=new I(e.image,e.mapping,e.wrapS,e.wrapT,e.magFilter,e.minFilter,e.format,e.type,e.anisotropy,e.colorSpace),r.mipmaps=e.mipmaps,r.channel=e.channel,r.source.data=e.source.data,r.flipY=e.flipY,r.premultiplyAlpha=e.premultiplyAlpha,r.unpackAlignment=e.unpackAlignment,r.matrix=new nt(...e.matrix.elements);if(!r){console.error("[Worker] Failed to create new texture from received data. Texture is not a CompressedTexture or Texture.");continue}t.texture=r}return n}function Le(n){let t=n;if("isInterleavedBufferAttribute"in n&&n.isInterleavedBufferAttribute){const e=n.data,r=e.array,s=new it(r,e.stride);t=new at(s,n.itemSize,r.byteOffset,n.normalized),t.offset=n.offset}else"isBufferAttribute"in n&&n.isBufferAttribute&&(t=new lt(n.array,n.itemSize,n.normalized),t.usage=n.usage,t.gpuType=n.gpuType,t.updateRanges=n.updateRanges);return t}const It=q("gltf-progressive-worker");q("gltf-progressive-reduce-mipmaps");const oe=q("gltf-progressive-gc"),be=Symbol("needle-progressive-texture"),B="NEEDLE_progressive";class f{get name(){return B}static getMeshLODExtension(t){const e=this.getAssignedLODInformation(t);return e?.key?this.lodInfos.get(e.key):null}static getPrimitiveIndex(t){return this.getAssignedLODInformation(t)?.index??-1}static getMaterialMinMaxLODsCount(t,e){const r=this,s="LODS:minmax",o=t[s];if(o!=null)return o;if(e||(e={min_count:1/0,max_count:0,lods:[]}),Array.isArray(t)){for(const a of t)this.getMaterialMinMaxLODsCount(a,e);return t[s]=e,e}if(g==="verbose"&&console.log("getMaterialMinMaxLODsCount",t),t.type==="ShaderMaterial"||t.type==="RawShaderMaterial"){const a=t;for(const l of Object.keys(a.uniforms)){const u=a.uniforms[l].value;u?.isTexture===!0&&i(u,e)}}else if(t.isMaterial)for(const a of Object.keys(t)){const l=t[a];l?.isTexture===!0&&i(l,e)}else g&&console.warn(`[getMaterialMinMaxLODsCount] Unsupported material type: ${t.type}`);return t[s]=e,e;function i(a,l){const u=r.getAssignedLODInformation(a);if(u){const c=r.lodInfos.get(u.key);if(c&&c.lods){l.min_count=Math.min(l.min_count,c.lods.length),l.max_count=Math.max(l.max_count,c.lods.length);for(let d=0;d<c.lods.length;d++){const p=c.lods[d];p.width&&(l.lods[d]=l.lods[d]||{min_height:1/0,max_height:0},l.lods[d].min_height=Math.min(l.lods[d].min_height,p.height),l.lods[d].max_height=Math.max(l.lods[d].max_height,p.height))}}}}}static hasLODLevelAvailable(t,e){if(Array.isArray(t)){for(const o of t)if(this.hasLODLevelAvailable(o,e))return!0;return!1}if(t.isMaterial===!0){for(const o of Object.keys(t)){const i=t[o];if(i&&i.isTexture&&this.hasLODLevelAvailable(i,e))return!0}return!1}else if(t.isGroup===!0){for(const o of t.children)if(o.isMesh===!0&&this.hasLODLevelAvailable(o,e))return!0}let r,s;if(t.isMesh?r=t.geometry:(t.isBufferGeometry||t.isTexture)&&(r=t),r&&r?.userData?.LODS){const o=r.userData.LODS;if(s=this.lodInfos.get(o.key),e===void 0)return s!=null;if(s)return Array.isArray(s.lods)?e<s.lods.length:e===0}return!1}static assignMeshLOD(t,e,r){if(!t)return Promise.resolve(null);if(t instanceof V||t.isMesh===!0){const s=t.geometry,o=this.getAssignedLODInformation(s);if(!o)return Promise.resolve(null);for(const a of $)a.onBeforeGetLODMesh?.(t,e);t["LOD:requested level"]=e;const i=()=>t["LOD:requested level"]===e||this.shouldApplyStaleMeshLOD(t,e);return f.getOrLoadLOD(s,e,{isCurrent:i}).then(a=>{if(Array.isArray(a)){const u=o.index||0;a=a[u]}const l=t["LOD:requested level"]===e;return(l||this.shouldApplyStaleMeshLOD(t,e))&&(l&&delete t["LOD:requested level"],a&&s!=a&&(a?.isBufferGeometry?typeof r?.apply=="function"?r.apply(a,e,t):r?.apply!==!1&&(t.geometry=a):g&&console.error("Invalid LOD geometry",a))),a}).catch(a=>(console.error("Error loading mesh LOD",t,a),null))}else g&&console.error("Invalid call to assignMeshLOD: Request mesh LOD but the object is not a mesh",t);return Promise.resolve(null)}static assignTextureLOD(t,e=0,r){if(!t)return Promise.resolve(null);const s=r?.force===!0;if(t.isMesh===!0){const o=t;if(Array.isArray(o.material)){const i=new Array;for(const a of o.material){const l=this.assignTextureLOD(a,e,r);i.push(l)}return Promise.all(i).then(a=>{const l=new Array;for(const u of a)Array.isArray(u)&&l.push(...u);return l})}else return this.assignTextureLOD(o.material,e,r)}if(t.isMaterial===!0){const o=t,i=[],a=new Array;if(this.trackCurrentMaterialTextureSlots(o),o.uniforms&&(o.isRawShaderMaterial||o.isShaderMaterial===!0)){const l=o;for(const u of Object.keys(l.uniforms)){const c=l.uniforms[u].value;if(c?.isTexture===!0){const d=this.assignTextureLODForSlot(c,e,o,u,s).then(p=>(p&&l.uniforms[u].value!=p&&(l.uniforms[u].value=p,l.uniformsNeedUpdate=!0),p));i.push(d),a.push(u)}}}else for(const l of Object.keys(o)){const u=o[l];if(u?.isTexture===!0){const c=this.assignTextureLODForSlot(u,e,o,l,s);i.push(c),a.push(l)}}return Promise.all(i).then(l=>{const u=new Array;for(let c=0;c<l.length;c++){const d=l[c],p=a[c];d&&d.isTexture===!0?u.push({material:o,slot:p,texture:d,level:e}):u.push({material:o,slot:p,texture:null,level:e})}return u})}if(t instanceof I||t.isTexture===!0){const o=t;return this.assignTextureLODForSlot(o,e,null,null,s)}return Promise.resolve(null)}static set maxConcurrentLoadingTasks(t){f.queue.maxConcurrent=t}static get maxConcurrentLoadingTasks(){return f.queue.maxConcurrent}static assignTextureLODForSlot(t,e,r,s,o){if(t?.isTexture!==!0)return Promise.resolve(null);if(s==="glyphMap")return Promise.resolve(t);const i=this.getAssignedLODInformation(t);if(i&&(i.level===e||!o&&i.level<e))return Promise.resolve(t);if(r&&s){const d=this.getPendingTextureSlotRequest(r,s);if(d&&d.level===e&&d.force===o)return d.promise}const a=r&&s?this.nextTextureSlotRequestId(r,s,e,o):0,l=()=>!r||!s||this.getLatestTextureSlotRequest(r,s)?.id===a,u=()=>l()||this.shouldApplyStaleTextureSlotLOD(r,s,e,o),c=f.getOrLoadLOD(t,e,{isCurrent:u}).then(d=>{if(!l()&&!this.shouldApplyStaleTextureSlotLOD(r,s,e,o))return null;if(Array.isArray(d))return console.warn("Progressive: Got an array of textures for a texture slot, this should not happen..."),null;if(d?.isTexture===!0){if(d!=t&&r&&s){const p=this.getMaterialTextureSlot(r,s)??t;if(p&&!o){const m=this.getAssignedLODInformation(p);if(m&&m?.level<e)return g==="verbose"&&console.warn("Assigned texture level is already higher: ",m.level,e,r,p,d),null}this.assignTrackedTextureSlot(r,s,d)}return d}else g=="verbose"&&console.warn("No LOD found for",t,e);return null}).catch(d=>(console.error("Error loading LOD",t,d),null));return r&&s&&this.setPendingTextureSlotRequest(r,s,e,o,a,c),c}static trackedTextureSlots=new WeakMap;static pendingTextureSlotRequests=new WeakMap;static latestTextureSlotRequests=new WeakMap;static textureSlotRequestId=0;static trackCurrentMaterialTextureSlots(t){if(t.uniforms&&(t.isRawShaderMaterial||t.isShaderMaterial===!0)){const e=t;for(const r of Object.keys(e.uniforms)){const s=e.uniforms[r].value;s?.isTexture===!0&&this.ensureTrackedTextureSlot(t,r,s)}return}for(const e of Object.keys(t)){const r=t[e];r?.isTexture===!0&&this.ensureTrackedTextureSlot(t,e,r)}}static getPendingTextureSlotRequest(t,e){return this.pendingTextureSlotRequests.get(t)?.get(e)}static nextTextureSlotRequestId(t,e,r,s){let o=this.latestTextureSlotRequests.get(t);o||(o=new Map,this.latestTextureSlotRequests.set(t,o));const i=++this.textureSlotRequestId;return o.set(e,{id:i,level:r,force:s}),i}static getLatestTextureSlotRequest(t,e){return this.latestTextureSlotRequests.get(t)?.get(e)}static shouldApplyStaleTextureSlotLOD(t,e,r,s){if(!t||!e)return!1;const o=this.getLatestTextureSlotRequest(t,e),i=this.getMaterialTextureSlot(t,e),a=this.getAssignedLODInformation(i)?.level??1/0;return r>=a?!1:s?o?r>=o.level:!1:!0}static shouldApplyStaleMeshLOD(t,e){const r=t["LOD:requested level"];if(typeof r!="number")return!1;const s=this.getAssignedLODInformation(t.geometry)?.level??1/0;return e<s&&e>=r}static setPendingTextureSlotRequest(t,e,r,s,o,i){let a=this.pendingTextureSlotRequests.get(t);a||(a=new Map,this.pendingTextureSlotRequests.set(t,a));const l={level:r,force:s,id:o,promise:i};a.set(e,l),i.finally(()=>{a.get(e)?.id===o&&a.delete(e)})}static getMaterialTextureSlot(t,e){const r=t.uniforms?.[e];if(r?.value?.isTexture===!0)return r.value;const s=t[e];return s?.isTexture===!0?s:null}static setMaterialTextureSlot(t,e,r){const s=t.uniforms?.[e];if(s?.value?.isTexture===!0){s.value=r,t.uniformsNeedUpdate=!0;return}t[e]=r}static assignTrackedTextureSlot(t,e,r){let s=this.trackedTextureSlots.get(t);s||(s=new Map,this.trackedTextureSlots.set(t,s));const o=this.getMaterialTextureSlot(t,e);let i=s.get(e);!i&&o?i=this.ensureTrackedTextureSlot(t,e,o):i&&o&&i!==o&&(this.releaseTrackedTextureSlot(t,e,i),i=this.ensureTrackedTextureSlot(t,e,o)),!(i===r&&o===r)&&(i&&i!==r&&this.releaseTrackedTextureSlot(t,e,i),i!==r&&(this.trackTextureUsage(r),s.set(e,r)),o!==r&&this.setMaterialTextureSlot(t,e,r))}static ensureTrackedTextureSlot(t,e,r){let s=this.trackedTextureSlots.get(t);s||(s=new Map,this.trackedTextureSlots.set(t,s));const o=s.get(e);return o===r?o:(o&&this.releaseTrackedTextureSlot(t,e,o),this.trackTextureUsage(r),s.set(e,r),r)}static releaseTrackedTextureSlot(t,e,r){const s=this.trackedTextureSlots.get(t);if(s?.get(e)===r&&s.delete(e),this.untrackTextureUsage(r)&&(g||oe)){const o=this.getAssignedLODInformation(r);console.log(`[gltf-progressive] Disposed old texture LOD ${o?.level??"?"} for ${t.name||t.type}.${e}`,r.uuid)}}parser;url;constructor(t){const e=t.options.path;g&&console.log("Progressive extension registered for",e),this.parser=t,this.url=e}_isLoadingMesh;loadMesh=t=>{if(this._isLoadingMesh)return null;const e=this.parser.json.meshes[t]?.extensions?.[B];return e?(this._isLoadingMesh=!0,this.parser.getDependency("mesh",t).then(r=>(this._isLoadingMesh=!1,r&&f.registerMesh(this.url,e.guid,r,e.lods?.length,0,e),r))):null};afterRoot(t){return g&&console.log("AFTER",this.url,t),this.parser.json.textures?.forEach((e,r)=>{if(e?.extensions){const s=e?.extensions[B];if(s){if(!s.lods){g&&console.warn("Texture has no LODs",s);return}let o=!1;for(const i of this.parser.associations.keys())i.isTexture===!0&&this.parser.associations.get(i)?.textures===r&&(o=!0,f.registerTexture(this.url,i,s.lods?.length,r,s));o||this.parser.getDependency("texture",r).then(i=>{i&&f.registerTexture(this.url,i,s.lods?.length,r,s)})}}}),this.parser.json.meshes?.forEach((e,r)=>{if(e?.extensions){const s=e?.extensions[B];if(s&&s.lods){for(const o of this.parser.associations.keys())if(o.isMesh){const i=this.parser.associations.get(o);i?.meshes===r&&f.registerMesh(this.url,s.guid,o,s.lods.length,i.primitives,s)}}}}),null}static registerTexture=(t,e,r,s,o)=>{if(!e){g&&console.error("!! gltf-progressive: Called register texture without texture");return}if(g){const{width:a,height:l}=ye(e);console.log(`> gltf-progressive: register texture[${s}] "${e.name||e.uuid}", Current: ${a}x${l}, Max: ${o.lods[0]?.width}x${o.lods[0]?.height}, uuid: ${e.uuid}`,o,e)}e.source&&(e.source[be]=o);const i=o.guid;f.assignLODInformation(t,e,i,r,s),f.lodInfos.set(i,o),f.lowresCache.set(i,new WeakRef(e))};static registerMesh=(t,e,r,s,o,i)=>{const a=r.geometry;if(!a){g&&console.warn("gltf-progressive: Register mesh without geometry");return}a.userData||(a.userData={}),g&&console.log("> Progressive: register mesh "+r.name,{index:o,uuid:r.uuid},i,r),f.assignLODInformation(t,a,e,s,o),f.lodInfos.set(e,i);let l=f.lowresCache.get(e)?.deref();l?l.push(r.geometry):l=[r.geometry],f.lowresCache.set(e,new WeakRef(l)),s>0&&!Y(r)&&St(r,a);for(const u of $)u.onRegisteredNewMesh?.(r,i)};static dispose(t){if(t){this.lodInfos.delete(t);const e=this.lowresCache.get(t);if(e){const r=e.deref();if(r){if(r.isTexture){const s=r;this.textureRefCounts.delete(s.uuid),s.dispose()}else if(Array.isArray(r))for(const s of r)s.dispose()}this.lowresCache.delete(t)}for(const[r,s]of this.cache)r.includes(t)&&(this._disposeCacheEntry(s),this.cache.delete(r))}else{this.lodInfos.clear();for(const[,e]of this.lowresCache){const r=e.deref();if(r){if(r.isTexture){const s=r;this.textureRefCounts.delete(s.uuid),s.dispose()}else if(Array.isArray(r))for(const s of r)s.dispose()}}this.lowresCache.clear();for(const[,e]of this.cache)this._disposeCacheEntry(e);this.cache.clear(),this.textureRefCounts.clear(),this.trackedTextureSlots=new WeakMap,this.pendingTextureSlotRequests=new WeakMap,this.latestTextureSlotRequests=new WeakMap,this.textureSlotRequestId=0}}static _disposeCacheEntry(t){if(t instanceof WeakRef){const e=t.deref();e&&(e.isTexture&&this.textureRefCounts.delete(e.uuid),e.dispose())}else t.then(e=>{if(e)if(Array.isArray(e))for(const r of e)r.dispose();else e.isTexture&&this.textureRefCounts.delete(e.uuid),e.dispose()}).catch(()=>{})}static lodInfos=new Map;static cache=new Map;static lowresCache=new Map;static textureRefCounts=new Map;static _resourceRegistry=new FinalizationRegistry(t=>{const e=f.cache.get(t);(g||oe)&&console.debug(`[gltf-progressive] Memory: Resource GC'd
2
- ${t}`),e instanceof WeakRef&&(e.deref()||(f.cache.delete(t),(g||oe)&&console.log("[gltf-progressive] \u21AA Cache entry deleted (GC)")))});static trackTextureUsage(t){const e=t.uuid,r=this.textureRefCounts.get(e)||0;this.textureRefCounts.set(e,r+1),g==="verbose"&&console.log(`[gltf-progressive] Track texture ${e}, refCount: ${r} \u2192 ${r+1}`)}static untrackTextureUsage(t){const e=t.uuid,r=this.textureRefCounts.get(e);if(!r)return(g==="verbose"||oe)&&o("[gltf-progressive] Memory: Untrack untracked texture (dispose immediately)",0),t.dispose(),!0;const s=r-1;if(s<=0)return this.textureRefCounts.delete(e),(g||oe)&&o("[gltf-progressive] Memory: Dispose texture",s),t.dispose(),!0;return this.textureRefCounts.set(e,s),g==="verbose"&&o("[gltf-progressive] Memory: Untrack texture",s),!1;function o(i,a){let{width:l,height:u}=ye(t);const c=l&&u?`${l}x${u}`:"N/A";let d="N/A";l&&u&&(d=`~${(Dt(t)/(1024*1024)).toFixed(2)} MB`),console.log(`${i} \u2014 ${t.name} ${c} (${d}), refCount: ${r} \u2192 ${a}
3
- ${e}`)}}static workers=[];static _workersIndex=0;static async getOrLoadLOD(t,e,r){const s=g=="verbose",o=this.getAssignedLODInformation(t);if(!o)return g&&console.warn(`[gltf-progressive] No LOD information found: ${t.name}, uuid: ${t.uuid}, type: ${t.type}`,t),null;const i=o?.key;let a;if(t.isTexture===!0){const l=t;l.source&&l.source[be]&&(a=l.source[be])}if(a||(a=f.lodInfos.get(i)),!a)g&&console.warn(`Can not load LOD ${e}: no LOD info found for "${i}" ${t.name}`,t.type,f.lodInfos);else{if(e>0){let c=!1;const d=Array.isArray(a.lods);if(d&&e>=a.lods.length?c=!0:d||(c=!0),c){const p=this.lowresCache.get(i);if(p){const m=p.deref();if(m)return m;this.lowresCache.delete(i),g&&console.log(`[gltf-progressive] Lowres cache entry was GC'd: ${i}`)}return null}}const l=Array.isArray(a.lods)?a.lods[e]?.path:a.lods;if(!l)return g&&!a["missing:uri"]&&(a["missing:uri"]=!0,console.warn("Missing uri for progressive asset for LOD "+e,a)),null;const u=bt(o.url,l);if(u.endsWith(".glb")||u.endsWith(".gltf")){if(!a.guid)return console.warn("missing pointer for glb/gltf texture",a),null;const c=u+"_"+a.guid,d=await this.tryResolveLODCacheEntry(this.cache.get(c),c,u,t,e,s);if(d.found)return d.value;if(r?.isCurrent?.()===!1)return s&&console.log(`Skipping stale LOD ${e} request before queue: ${u}`),null;const p=await this.queue.slot(u);if(r?.isCurrent?.()===!1)return s&&console.log(`Skipping stale LOD ${e} request after queue: ${u}`),null;const m=await this.tryResolveLODCacheEntry(this.cache.get(c),c,u,t,e,s);if(m.found)return m.value;if(!p.use)return g&&console.log(`LOD ${e} was aborted: ${u}`),null;const w=a,h=new Promise(async(D,j)=>{if(It){const b=await(await Ct({})).load(u);if(b.textures.length>0)for(const x of b.textures){let v=x.texture;return f.assignLODInformation(o.url,v,i,e,void 0),t instanceof I&&(v=this.copySettings(t,v)),v&&(v.guid=w.guid),D(v)}if(b.geometries.length>0){const x=new Array;for(const v of b.geometries){const R=v.geometry;f.assignLODInformation(o.url,R,i,e,v.primitiveIndex),x.push(R)}return D(x)}return D(null)}const E=new he;ge(E),g&&(await new Promise(b=>setTimeout(b,1e3)),s&&console.warn("Start loading (delayed) "+u,w.guid));let L=u;if(w&&Array.isArray(w.lods)){const b=w.lods[e];b.hash&&(L+="?v="+b.hash)}const y=await E.loadAsync(L).catch(b=>(console.error(`Error loading LOD ${e} from ${u}
4
- `,b),D(null)));if(!y)return D(null);const T=y.parser;s&&console.log("Loading finished "+u,w.guid);let S=0;if(y.parser.json.textures){let b=!1;for(const x of y.parser.json.textures){if(x?.extensions){const v=x?.extensions[B];if(v?.guid&&v.guid===w.guid){b=!0;break}}S++}if(b){let x=await T.getDependency("texture",S);return x&&f.assignLODInformation(o.url,x,i,e,void 0),s&&console.log('change "'+t.name+'" \u2192 "'+x.name+'"',u,S,x,c),t instanceof I&&(x=this.copySettings(t,x)),x&&(x.guid=w.guid),D(x)}else g&&console.warn("Could not find texture with guid",w.guid,y.parser.json)}if(S=0,y.parser.json.meshes){let b=!1;for(const x of y.parser.json.meshes){if(x?.extensions){const v=x?.extensions[B];if(v?.guid&&v.guid===w.guid){b=!0;break}}S++}if(b){const x=await T.getDependency("mesh",S);if(s&&console.log(`Loaded Mesh "${x.name}"`,u,S,x,c),x.isMesh===!0){const v=x.geometry;return f.assignLODInformation(o.url,v,i,e,0),D(v)}else{const v=new Array;for(let R=0;R<x.children.length;R++){const F=x.children[R];if(F.isMesh===!0){const N=F.geometry;f.assignLODInformation(o.url,N,i,e,R),v.push(N)}}return D(v)}}else g&&console.warn("Could not find mesh with guid",w.guid,y.parser.json)}return D(null)});this.cache.set(c,h),p.use(h);const M=await h;return M!=null?M instanceof I?(this.cache.set(c,new WeakRef(M)),f._resourceRegistry.register(M,c)):Array.isArray(M)?this.cache.set(c,Promise.resolve(M)):this.cache.set(c,Promise.resolve(M)):this.cache.set(c,Promise.resolve(null)),M}else if(t instanceof I){if(r?.isCurrent?.()===!1)return s&&console.log(`Skipping stale texture LOD ${e} request: ${u}`),null;s&&console.log("Load texture from uri: "+u);const c=await new ut().loadAsync(u);return r?.isCurrent?.()===!1?(c?.dispose(),null):(c?(c.guid=a.guid,c.flipY=!1,c.needsUpdate=!0,c.colorSpace=t.colorSpace,s&&console.log(a,c)):g&&console.warn("failed loading",u),c)}}return null}static async tryResolveLODCacheEntry(t,e,r,s,o,i){if(t===void 0)return{found:!1};if(i&&console.log(`LOD ${o} was already loading/loaded: ${e}`),t instanceof WeakRef){const u=t.deref();if(u){let c=u,d=!1;if(c instanceof I&&s instanceof I?Ee(c.image)||xe(c)?c=this.copySettings(s,c):d=!0:c instanceof z&&s instanceof z&&(c.attributes.position?.array||(d=!0)),!d)return{found:!0,value:c}}return this.cache.delete(e),g&&console.log(`[gltf-progressive] Re-loading GC'd/disposed resource: ${e}`),{found:!1}}let a=await t.catch(u=>(console.error(`Error loading LOD ${o} from ${r}
5
- `,u),null)),l=!1;return a==null||(a instanceof I&&s instanceof I?Ee(a.image)||xe(a)?a=this.copySettings(s,a):(l=!0,this.cache.delete(e)):a instanceof z&&s instanceof z&&(a.attributes.position?.array||(l=!0,this.cache.delete(e)))),l?{found:!1}:{found:!0,value:a}}static _queue;static get queue(){return this._queue??=new Mt(ve()?20:50,{debug:g!=!1})}static assignLODInformation(t,e,r,s,o){if(!e)return;e.userData||(e.userData={});const i=new Et(t,r,s,o);e.userData.LODS=i,"source"in e&&typeof e.source=="object"&&(e.source.LODS=i)}static getAssignedLODInformation(t){return t?t.userData?.LODS?t.userData.LODS:"source"in t&&t.source?.LODS?t.source.LODS:null:null}static copySettings(t,e){return e?(g==="verbose"&&console.debug(`Copy texture settings
6
- `,t.uuid,`
7
- `,e.uuid),e=e.clone(),e.offset=t.offset,e.repeat=t.repeat,e.colorSpace=t.colorSpace,e.magFilter=t.magFilter,e.minFilter=t.minFilter,e.wrapS=t.wrapS,e.wrapT=t.wrapT,e.flipY=t.flipY,e.anisotropy=t.anisotropy,e.mipmaps||(e.generateMipmaps=t.generateMipmaps),e):t}}class Et{url;key;level;index;constructor(t,e,r,s){this.url=t,this.key=e,this.level=r,s!=null&&(this.index=s)}}class ue{static addPromise=(t,e,r,s)=>{s.forEach(o=>{o.add(t,e,r)})};ready;get awaitedCount(){return this._addedCount}get resolvedCount(){return this._resolvedCount}get currentlyAwaiting(){return this._awaiting.length}_resolve;_signal;_frame_start;_frames_to_capture;_resolved=!1;_addedCount=0;_resolvedCount=0;_awaiting=[];_maxPromisesPerObject=1;constructor(t,e){const r=Math.max(e.frames??2,2);this._frame_start=e.waitForFirstCapture?void 0:t,this._frames_to_capture=r,this.ready=new Promise(s=>{this._resolve=s}),this.ready.finally(()=>{this._resolved=!0,this._awaiting.length=0}),this._signal=e.signal,this._signal?.addEventListener("abort",()=>{this.resolveNow()}),this._maxPromisesPerObject=Math.max(1,e.maxPromisesPerObject??1)}_currentFrame=0;update(t){this._currentFrame=t,this._frame_start===void 0&&this._addedCount>0&&(this._frame_start=t),(this._signal?.aborted||this._awaiting.length===0&&this._frame_start!==void 0&&t>this._frame_start+this._frames_to_capture)&&this.resolveNow()}_seen=new WeakMap;add(t,e,r){if(this._resolved){g&&console.warn("PromiseGroup: Trying to add a promise to a resolved group, ignoring.");return}if(!(this._frame_start!==void 0&&this._currentFrame>this._frame_start+this._frames_to_capture)){if(this._maxPromisesPerObject>=1)if(this._seen.has(e)){const s=this._seen.get(e);if(s>=this._maxPromisesPerObject){g&&console.warn("PromiseGroup: Already awaiting object ignoring new promise for it.");return}this._seen.set(e,s+1)}else this._seen.set(e,1);this._awaiting.push(r),this._addedCount++,r.finally(()=>{this._resolvedCount++,this._awaiting.splice(this._awaiting.indexOf(r),1)})}}resolveNow(){this._resolved||this._resolve?.({awaited_count:this._addedCount,resolved_count:this._resolvedCount,cancelled:this._signal?.aborted??!1})}}const A=q("debugprogressive"),$t=A==="colors",Bt=q("noprogressive"),Me=Symbol("Needle:LODSManager"),De=Symbol("Needle:LODState"),U=Symbol("Needle:CurrentLOD"),Ge=ct,_={mesh_lod:-1,texture_lod:-1},Gt=new Re,qe=[3526751,11065402,15978811,15897394,15749691,11032304,4827122,3332036,16739229,7306743,14053330,3516499,12035359,14703919,3963096,42662,14100029,8344319,4633680,16229681,3120096,12076434,9083434,2060171,15751837,10182117,48121,62932,16704576,15817653,5083278,5592405];function qt(){const n=Ge.Timer||Ge.Clock;return new n}const _e=new ie,W=new ie,Ue=new ie,Ut=new k,Wt=new k,jt=new ke,G=new k,Q=new k,J=new k,Z=new k;function Ft(n,t){const e=n.min,r=n.max,s=(e.x+r.x)*.5,o=(e.y+r.y)*.5;return G.set(s,o,e.z).applyMatrix4(t).z<0}function We(n){const{geometry:t,matrixWorld:e,camera:r,projectionScreenMatrix:s,desiredDensity:o,canvasHeight:i=0,currentLevel:a=-1,xrEnabled:l=!1,debugDrawLine:u,warnMissingPrimitiveDensities:c=!1}=n,d=f.getMeshLODExtension(t)?.lods,p=f.getPrimitiveIndex(t),m=n.target??{level:a,primitiveIndex:p,screenCoverage:0,screenspaceVolume:new k,centrality:1};m.level=a,m.primitiveIndex=p,m.screenCoverage=0,m.screenspaceVolume.set(0,0,0),m.centrality=1;let w=n.boundingBox??t.boundingBox;if(w||(t.computeBoundingBox(),w=t.boundingBox),!w)return m;if(_e.copy(w).applyMatrix4(e),r.isPerspectiveCamera&&Ft(_e,s))return m.level=0,m.screenCoverage=1/0,m.screenspaceVolume.set(1/0,1/0,1/0),m;if(W.copy(_e).applyMatrix4(s),l&&r.isPerspectiveCamera&&r.fov>70){const L=W.min,y=W.max;let T=L.x,S=L.y,b=y.x,x=y.y;const v=2,R=1.5,F=(L.x+y.x)*.5,N=(L.y+y.y)*.5;T=(T-F)*v+F,S=(S-N)*v+N,b=(b-F)*v+F,x=(x-N)*v+N;const Ke=T<0&&b>0?0:Math.min(Math.abs(L.x),Math.abs(y.x)),Ye=S<0&&x>0?0:Math.min(Math.abs(L.y),Math.abs(y.y)),de=Math.max(Ke,Ye);m.centrality=(R-de)*(R-de)*(R-de)}const h=W.getSize(Ut);h.multiplyScalar(.5),globalThis.screen?.availHeight>0&&i>0&&h.multiplyScalar(i/globalThis.screen.availHeight),r.isPerspectiveCamera&&(h.x*=r.aspect),Ue.copy(w).applyMatrix4(e).applyMatrix4(r.matrixWorldInverse);const M=Ue.getSize(Wt),D=Math.max(h.x,h.y),j=Math.max(M.x,M.y);D!==0&&j!==0&&(h.z=M.z/j*D);const E=Math.max(h.x,h.y,h.z)*m.centrality;if(m.screenCoverage=E,m.screenspaceVolume.copy(h),E<=0)return m;if(u){const L=jt.copy(s);L.invert(),G.copy(W.min),Q.copy(W.max),Q.x=G.x,J.copy(W.max),J.y=G.y,Z.copy(W.max);const y=(G.z+Z.z)*.5;G.z=Q.z=J.z=Z.z=y,G.applyMatrix4(L),Q.applyMatrix4(L),J.applyMatrix4(L),Z.applyMatrix4(L),u(G,Q,255),u(G,J,255),u(Q,Z,255),u(J,Z,255)}if(d?.length)for(let L=0;L<d.length;L++){const y=d[L],T=y.densities?.[p]||y.density||1e-5;if(p>0&&c&&$e()&&!y.densities&&!globalThis["NEEDLE:MISSING_LOD_PRIMITIVE_DENSITIES"]&&(globalThis["NEEDLE:MISSING_LOD_PRIMITIVE_DENSITIES"]=!0,console.warn("[Needle Progressive] Detected usage of mesh without primitive densities. This might cause incorrect LOD level selection: Consider re-optimizing your model by updating your Needle Integration, Needle glTF Pipeline or running optimization again on Needle Cloud.")),T/E<o){m.level=L;break}}return m}let ne=class K{static debugDrawLine;static getObjectLODState(t){return t[De]}static addPlugin(t){$.push(t)}static removePlugin(t){const e=$.indexOf(t);e>=0&&$.splice(e,1)}static getPlugins(){return $}static get(t,e){if(t[Me])return console.debug("[gltf-progressive] LODsManager already exists for this renderer"),t[Me];const r=new K(t,{engine:"unknown",...e});return t[Me]=r,r}renderer;context;projectionScreenMatrix=new ke;get plugins(){return $}overrideLodLevel=void 0;targetTriangleDensity=2e5;skinnedMeshAutoUpdateBoundsInterval=30;updateInterval="auto";#e=1;pause=!1;manual=!1;_newPromiseGroups=[];_promiseGroupIds=0;awaitLoading(t){const e=this._promiseGroupIds++,r=new ue(this.#s,{...t});this._newPromiseGroups.push(r);const s=performance.now();return r.ready.finally(()=>{const o=this._newPromiseGroups.indexOf(r);o>=0&&(this._newPromiseGroups.splice(o,1),$e()&&performance.measure("LODsManager:awaitLoading",{start:s,detail:{id:e,name:t?.name,awaited:r.awaitedCount,resolved:r.resolvedCount}}))}),r.ready}trackLoadingPromise(t,e,r){return ue.addPromise(t,e,r,this._newPromiseGroups),r}_postprocessPromiseGroups(){if(this._newPromiseGroups.length!==0)for(let t=this._newPromiseGroups.length-1;t>=0;t--)this._newPromiseGroups[t].update(this.#s)}_lodchangedlisteners=[];addEventListener(t,e){return t==="changed"?(this._lodchangedlisteners.push(e),()=>{this.removeEventListener(t,e)}):()=>{}}removeEventListener(t,e){let r=!1;if(t==="changed"){const s=this._lodchangedlisteners.indexOf(e);s>=0&&(this._lodchangedlisteners.splice(s,1),r=!0)}return r}constructor(t,e){this.renderer=t,this.context={...e}}#t;#s=0;#o=0;#i=0;#r=0;#n=qt();_fpsBuffer=[60,60,60,60,60];enable(){if(this.#t)return;console.debug("[gltf-progressive] Enabling LODsManager for renderer");let t=0;this.#t=this.renderer.render;const e=this;ae(this.renderer),this.renderer.render=function(r,s){const o=e.renderer.getRenderTarget();(o==null||"isXRRenderTarget"in o&&o.isXRRenderTarget)&&(t=0,e.#s+=1,e.#n.update?.(),e.#o=Math.max(e.#n.getDelta(),1/1e3),e.#i+=e.#o,e._fpsBuffer.shift(),e._fpsBuffer.push(1/e.#o),e.#r=e._fpsBuffer.reduce((a,l)=>a+l)/e._fpsBuffer.length,A&&e.#s%200===0&&console.log("FPS",Math.round(e.#r),"Interval:",e.#e));const i=t++;e.#t.call(this,r,s),e.onAfterRender(r,s,i)}}disable(){this.#t&&(console.debug("[gltf-progressive] Disabling LODsManager for renderer"),this.renderer.render=this.#t,this.#t=void 0)}update(t,e){this.internalUpdate(t,e)}onAfterRender(t,e,r){if(this.pause)return;const s=this.getRenderList(t,e,r);if(!s)return;const o=s.opaque;let i=!0;if(o.length===1){const a=o[0].material;(a.name==="EffectMaterial"||a.name==="CopyShader")&&(i=!1)}if((e.parent&&e.parent.type==="CubeCamera"||r>=1&&e.type==="OrthographicCamera")&&(i=!1),i){if(Bt||(this.updateInterval==="auto"?this.#r<40&&this.#e<10?(this.#e+=1,A&&console.warn("\u2193 Reducing LOD updates",this.#e,this.#r.toFixed(0))):this.#r>=60&&this.#e>1&&(this.#e-=1,A&&console.warn("\u2191 Increasing LOD updates",this.#e,this.#r.toFixed(0))):this.#e=this.updateInterval,this.#e>0&&this.#s%this.#e!=0))return;this.internalUpdate(t,e),this._postprocessPromiseGroups()}}internalUpdate(t,e){const r=this.getRenderList(t,e,0);if(!r)return;const s=r.opaque;this.projectionScreenMatrix.multiplyMatrices(e.projectionMatrix,e.matrixWorldInverse);const o=this.targetTriangleDensity;for(const l of s){if(l.material&&(l.geometry?.type==="BoxGeometry"||l.geometry?.type==="BufferGeometry")&&(l.material.name==="SphericalGaussianBlur"||l.material.name=="BackgroundCubeMaterial"||l.material.name==="CubemapFromEquirect"||l.material.name==="EquirectangularToCubeUV")){A&&(l.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]||(l.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]=!0,console.warn("Ignoring skybox or BLIT object",l,l.material.name,l.material.type)));continue}switch(l.material.type){case"LineBasicMaterial":case"LineDashedMaterial":case"PointsMaterial":case"ShadowMaterial":case"MeshDistanceMaterial":case"MeshDepthMaterial":continue}const u=l.object;(u instanceof V||u.isMesh)&&this.updateLODs(t,e,u,o)}const i=r.transparent;for(const l of i){const u=l.object;(u instanceof V||u.isMesh)&&this.updateLODs(t,e,u,o)}const a=r.transmissive;for(const l of a){const u=l.object;(u instanceof V||u.isMesh)&&this.updateLODs(t,e,u,o)}}getRenderList(t,e,r){const s=this.renderer;let o=null;if(s.isWebGPURenderer===!0){const i=s._renderLists;if(!i)return null;o=i.get(t,e)}else if(s.isWebGLRenderer===!0){const i=s.renderLists;if(!i)return null;o=i.get(t,r)}return o?{opaque:o.opaque||[],transparent:o.transparent||[],transmissive:o.transmissive||o.transparentDoublePass||[],transparentDoublePass:o.transparentDoublePass||[]}:null}updateLODs(t,e,r,s){r.userData||(r.userData={});let o=r[De];if(o||(o=new Nt,r[De]=o),o.frames++<2)return;for(const a of $)a.onBeforeUpdateLOD?.(this.renderer,t,e,r);const i=this.overrideLodLevel!==void 0?this.overrideLodLevel:X;i>=0?(_.mesh_lod=i,_.texture_lod=i):(this.calculateLodLevel(e,r,o,s,_),_.mesh_lod=Math.round(_.mesh_lod),_.texture_lod=Math.round(_.texture_lod)),_.mesh_lod>=0&&this.loadProgressiveMeshes(r,_.mesh_lod),r.material&&_.texture_lod>=0&&this.loadProgressiveTextures(r.material,_.texture_lod,i),g&&r.material&&!r.isGizmo&&Be(r.material),$t&&r.material&&!r.isGizmo&&!r.isBatchedMesh&&je(r.material,_.mesh_lod);for(const a of $)a.onAfterUpdatedLOD?.(this.renderer,t,e,r,_);o.lastLodLevel_Mesh=_.mesh_lod,o.lastLodLevel_Texture=_.texture_lod}loadProgressiveTextures(t,e,r){if(!t)return;if(Array.isArray(t)){for(const i of t)this.loadProgressiveTextures(i,e,r);return}let s=!1;(t[U]===void 0||e<t[U])&&(s=!0);const o=r!==void 0&&r>=0;if(o&&(s=t[U]!=r,e=r),s){t[U]=e;const i=o?{force:!0}:void 0,a=f.assignTextureLOD(t,e,i).then(l=>{this._lodchangedlisteners.forEach(u=>u({type:"texture",level:e,object:t}))});ue.addPromise("texture",t,a,this._newPromiseGroups)}}loadProgressiveMeshes(t,e){if(!t)return Promise.resolve(null);let r=t[U]!==e;const s=t["DEBUG:LOD"];if(s!=null&&(r=t[U]!=s,e=s),r){t[U]=e;const o=t.geometry,i=f.assignMeshLOD(t,e).then(a=>(a&&t[U]==e&&o!=t.geometry&&this._lodchangedlisteners.forEach(l=>l({type:"mesh",level:e,object:t})),a));return ue.addPromise("mesh",t,i,this._newPromiseGroups),i}return Promise.resolve(null)}_sphere=new Oe;_tempWorldPosition=new k;static skinnedMeshBoundsFrameOffsetCounter=0;static $skinnedMeshBoundsOffset=Symbol("gltf-progressive-skinnedMeshBoundsOffset");calculateLodLevel(t,e,r,s,o){if(!e){o.mesh_lod=-1,o.texture_lod=-1;return}if(!t){o.mesh_lod=-1,o.texture_lod=-1;return}let i=10+1,a=!1;if(A&&e["DEBUG:LOD"]!=null)return e["DEBUG:LOD"];const l=f.getMeshLODExtension(e.geometry)?.lods,u=f.getPrimitiveIndex(e.geometry),c=l&&l.length>0,d=f.getMaterialMinMaxLODsCount(e.material),p=d.min_count!==1/0&&d.min_count>=0&&d.max_count>=0;if(!c&&!p){o.mesh_lod=0,o.texture_lod=0;return}c||(a=!0,i=0);const m=this.renderer.domElement.clientHeight||this.renderer.domElement.height;let w=e.geometry.boundingBox;if(e.type==="SkinnedMesh"){const h=e;if(!h.boundingBox)h.computeBoundingBox();else if(this.skinnedMeshAutoUpdateBoundsInterval>0){if(!h[K.$skinnedMeshBoundsOffset]){const D=K.skinnedMeshBoundsFrameOffsetCounter++;h[K.$skinnedMeshBoundsOffset]=D}const M=h[K.$skinnedMeshBoundsOffset];if((r.frames+M)%this.skinnedMeshAutoUpdateBoundsInterval===0){const D=Y(h),j=h.geometry;D&&(h.geometry=D),h.computeBoundingBox(),h.geometry=j}}w=h.boundingBox}if(w){if(e.geometry.attributes.color&&e.geometry.attributes.color.count<100&&e.geometry.boundingSphere){this._sphere.copy(e.geometry.boundingSphere),this._sphere.applyMatrix4(e.matrixWorld);const M=t.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(M)){o.mesh_lod=0,o.texture_lod=0;return}}const h=We({geometry:e.geometry,matrixWorld:e.matrixWorld,camera:t,projectionScreenMatrix:this.projectionScreenMatrix,desiredDensity:s,canvasHeight:m,currentLevel:r.lastLodLevel_Mesh,boundingBox:w,xrEnabled:this.renderer.xr.enabled,debugDrawLine:A?K.debugDrawLine:void 0,warnMissingPrimitiveDensities:!0});if(r.lastCentrality=h.centrality,r.lastScreenCoverage=h.screenCoverage,r.lastScreenspaceVolume.copy(h.screenspaceVolume),h.screenCoverage===1/0){o.mesh_lod=0,o.texture_lod=0;return}h.level>=0&&h.level<i&&(i=h.level,a=!0)}if(a?o.mesh_lod=i:o.mesh_lod=r.lastLodLevel_Mesh,A&&o.mesh_lod!=r.lastLodLevel_Mesh){const h=l?.[o.mesh_lod];h&&console.log(`Mesh LOD changed: ${r.lastLodLevel_Mesh} \u2192 ${o.mesh_lod} (density: ${h.densities?.[u].toFixed(0)}) | ${e.name}`)}if(p){const h="saveData"in globalThis.navigator&&globalThis.navigator.saveData===!0;if(r.lastLodLevel_Texture<0){if(o.texture_lod=d.max_count-1,A){const M=d.lods[d.max_count-1];A&&console.log(`First Texture LOD ${o.texture_lod} (${M.max_height}px) - ${e.name}`)}}else{const M=r.lastScreenspaceVolume.x+r.lastScreenspaceVolume.y+r.lastScreenspaceVolume.z;let D=r.lastScreenCoverage*4;this.context?.engine==="model-viewer"&&(D*=1.5);const j=this.renderer.getPixelRatio?.()||globalThis.devicePixelRatio||1,E=m/j*D;let L=!1;for(let y=d.lods.length-1;y>=0;y--){const T=d.lods[y];if(!(h&&T.max_height>=2048)&&!(ve()&&T.max_height>4096)&&(T.max_height>E||!L&&y===0)){if(L=!0,o.texture_lod=y,A&&o.texture_lod<r.lastLodLevel_Texture){const S=T.max_height;console.log(`Texture LOD changed: ${r.lastLodLevel_Texture} \u2192 ${o.texture_lod} = ${S}px
8
- Screensize: ${E.toFixed(0)}px, Coverage: ${(100*r.lastScreenCoverage).toFixed(2)}%, Volume ${M.toFixed(1)}
9
- ${e.name}`)}break}}}}else o.texture_lod=0}};class Nt{frames=0;lastLodLevel_Mesh=-1;lastLodLevel_Texture=-1;lastScreenCoverage=0;lastScreenspaceVolume=new k;lastCentrality=0}function je(n,t){if(!(t<0)){if(Array.isArray(n)){for(const e of n)je(e,t);return}"color"in n&&n.color instanceof Re&&(n.color.copy(Fe(t,Gt)),n.needsUpdate=!0)}}function Fe(n,t){const e=Math.max(0,Math.min(qe.length-1,Math.floor(n)));return t.setHex(qe[e])}const Ne=Symbol("NEEDLE_mesh_lod"),ce=Symbol("NEEDLE_texture_lod");let Te=null;function ze(){const n=zt();n&&(n.mapURLs(function(t){return Ve(),t}),Ve(),Te?.disconnect(),Te=new MutationObserver(t=>{t.forEach(e=>{e.addedNodes.forEach(r=>{r instanceof HTMLElement&&r.tagName.toLowerCase()==="model-viewer"&&Xe(r)})})}),Te.observe(document,{childList:!0,subtree:!0}))}function zt(){return typeof customElements>"u"?null:customElements.get("model-viewer")||(customElements.whenDefined("model-viewer").then(()=>{console.debug("[gltf-progressive] model-viewer defined"),ze()}),null)}function Ve(){typeof document>"u"||document.querySelectorAll("model-viewer").forEach(n=>{Xe(n)})}const He=new WeakSet;let Vt=0;function Xe(n){if(!n||He.has(n))return null;He.add(n),console.debug("[gltf-progressive] found new model-viewer..."+ ++Vt+`
10
- `,n.getAttribute("src"));let t=null,e=null,r=null;for(let s=n;s!=null;s=Object.getPrototypeOf(s)){const o=Object.getOwnPropertySymbols(s),i=o.find(u=>u.toString()=="Symbol(renderer)"),a=o.find(u=>u.toString()=="Symbol(scene)"),l=o.find(u=>u.toString()=="Symbol(needsRender)");!t&&i!=null&&(t=n[i].threeRenderer),!e&&a!=null&&(e=n[a]),!r&&l!=null&&(r=n[l])}if(t&&e){let s=function(){if(r){let i=0;const a=setInterval(()=>{if(i++>5){clearInterval(a);return}r?.call(n)},300)}};console.debug("[gltf-progressive] setup model-viewer");const o=ne.get(t,{engine:"model-viewer"});return ne.addPlugin(new Ht),o.enable(),o.addEventListener("changed",()=>{r?.call(n)}),n.addEventListener("model-visibility",i=>{i.detail.visible&&r?.call(n)}),n.addEventListener("load",()=>{s()}),()=>{o.disable()}}return null}class Ht{_didWarnAboutMissingUrl=!1;onBeforeUpdateLOD(t,e,r,s){this.tryParseMeshLOD(e,s),this.tryParseTextureLOD(e,s)}getUrl(t){if(!t)return null;let e=t.getAttribute("src");return e||(e=t.src),e||(this._didWarnAboutMissingUrl||console.warn("No url found in modelviewer",t),this._didWarnAboutMissingUrl=!0),e}tryGetCurrentGLTF(t){return t._currentGLTF}tryGetCurrentModelViewer(t){return t.element}tryParseTextureLOD(t,e){if(e[ce]==!0)return;e[ce]=!0;const r=this.tryGetCurrentGLTF(t),s=this.tryGetCurrentModelViewer(t),o=this.getUrl(s);if(o&&r&&e.material){let i=function(l){if(l[ce]==!0)return;l[ce]=!0,l.userData&&(l.userData.LOD=-1);const u=Object.keys(l);for(let c=0;c<u.length;c++){const d=u[c],p=l[d];if(p?.isTexture===!0){const m=p.userData?.associations?.textures;if(m==null)continue;const w=r.parser.json.textures[m];if(!w){console.warn("Texture data not found for texture index "+m);continue}if(w?.extensions?.[B]){const h=w.extensions[B];h&&o&&f.registerTexture(o,p,h.lods.length,m,h)}}}};const a=e.material;if(Array.isArray(a))for(const l of a)i(l);else i(a)}}tryParseMeshLOD(t,e){if(e[Ne]==!0)return;e[Ne]=!0;const r=this.tryGetCurrentModelViewer(t),s=this.getUrl(r);if(!s)return;const o=e.userData?.gltfExtensions?.[B];if(o&&s){const i=e.uuid;f.registerMesh(s,i,e,0,o.lods.length,o)}}}function Xt(...n){let t,e,r,s;switch(n.length){case 2:[r,e]=n,s={};break;case 3:[r,e,s]=n;break;case 4:[t,e,r,s]=n;break;default:throw new Error("Invalid arguments")}ae(e),ge(r),pe(r,{progressive:!0,...s?.hints}),r.register(i=>new f(i));const o=ne.get(e);return s?.enableLODsManager!==!1&&o.enable(),o}if(ze(),!Tt){const n={gltfProgressive:{useNeedleProgressive:Xt,LODsManager:ne,configureLoader:pe,getRaycastMesh:Y,useRaycastMeshes:Ot}};if(!globalThis.Needle)globalThis.Needle=n;else for(const t in n)globalThis.Needle[t]=n[t]}export{ne as LODsManager,f as NEEDLE_progressive,ge as addDracoAndKTX2Loaders,We as calculateMeshLODLevel,pe as configureLoader,ae as createLoaders,Fe as getLODColor,Y as getRaycastMesh,Ce as setDracoDecoderLocation,Ae as setKTX2TranscoderLocation};