@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.
- package/CHANGELOG.md +5 -0
- package/SKILL.md +1 -1
- package/dist/gltf-progressive-BnLTsXZw.umd.cjs +10 -0
- package/dist/gltf-progressive-D6RT9IkB.min.js +10 -0
- package/dist/{gltf-progressive-B4DQEeBz.js → gltf-progressive-DaeP5KBr.js} +527 -514
- package/dist/{needle-engine.bundle-DdH96IqH.umd.cjs → needle-engine.bundle-B_ZctaDt.umd.cjs} +146 -146
- package/dist/{needle-engine.bundle-BmdWLmLa.min.js → needle-engine.bundle-DR967VjU.min.js} +145 -145
- package/dist/{needle-engine.bundle-9m-VdGg2.js → needle-engine.bundle-DTvJ1zCJ.js} +7302 -7153
- package/dist/needle-engine.d.ts +23 -15
- package/dist/needle-engine.js +517 -518
- package/dist/needle-engine.min.js +1 -1
- package/dist/needle-engine.umd.cjs +1 -1
- package/lib/engine/api.d.ts +4 -3
- package/lib/engine/api.js +3 -9
- package/lib/engine/api.js.map +1 -1
- package/lib/engine/engine_addressables.d.ts +32 -1
- package/lib/engine/engine_addressables.js +71 -9
- package/lib/engine/engine_addressables.js.map +1 -1
- package/lib/engine/engine_application.d.ts +2 -0
- package/lib/engine/engine_application.js +7 -3
- package/lib/engine/engine_application.js.map +1 -1
- package/lib/engine/engine_context.js +25 -1
- package/lib/engine/engine_context.js.map +1 -1
- package/lib/engine/engine_init.js +2 -2
- package/lib/engine/engine_input.js +1 -1
- package/lib/engine/engine_input.js.map +1 -1
- package/lib/engine/engine_license.d.ts +7 -7
- package/lib/engine/engine_license.js +121 -78
- package/lib/engine/engine_license.js.map +1 -1
- package/lib/engine/engine_lifecycle_functions_internal.js +20 -2
- package/lib/engine/engine_lifecycle_functions_internal.js.map +1 -1
- package/lib/engine/engine_networking_blob.js +3 -3
- package/lib/engine/engine_networking_blob.js.map +1 -1
- package/lib/engine/engine_networking_streams.d.ts +3 -0
- package/lib/engine/engine_networking_streams.js +64 -17
- package/lib/engine/engine_networking_streams.js.map +1 -1
- package/lib/engine/engine_utils_qrcode.js +2 -2
- package/lib/engine/engine_utils_qrcode.js.map +1 -1
- package/lib/engine/physics/workers/mesh-bvh/GenerateMeshBVHWorker.js +5 -0
- package/lib/engine/physics/workers/mesh-bvh/GenerateMeshBVHWorker.js.map +1 -1
- package/lib/engine/webcomponents/fonts.d.ts +1 -1
- package/lib/engine/webcomponents/fonts.js +16 -1
- package/lib/engine/webcomponents/fonts.js.map +1 -1
- package/lib/engine/webcomponents/icons.js +8 -1
- package/lib/engine/webcomponents/icons.js.map +1 -1
- package/lib/engine/webcomponents/needle menu/needle-menu-spatial.js +2 -2
- package/lib/engine/webcomponents/needle menu/needle-menu-spatial.js.map +1 -1
- package/lib/engine/webcomponents/needle menu/needle-menu.d.ts +5 -0
- package/lib/engine/webcomponents/needle menu/needle-menu.js +34 -16
- package/lib/engine/webcomponents/needle menu/needle-menu.js.map +1 -1
- package/lib/engine/webcomponents/needle-engine.d.ts +1 -0
- package/lib/engine/webcomponents/needle-engine.js +18 -6
- package/lib/engine/webcomponents/needle-engine.js.map +1 -1
- package/lib/engine/webcomponents/needle-engine.loading.d.ts +2 -0
- package/lib/engine/webcomponents/needle-engine.loading.js +12 -2
- package/lib/engine/webcomponents/needle-engine.loading.js.map +1 -1
- package/lib/engine/xr/TempXRContext.js +2 -2
- package/lib/engine/xr/TempXRContext.js.map +1 -1
- package/lib/engine-components/export/usdz/USDZExporter.js +4 -4
- package/lib/engine-components/export/usdz/USDZExporter.js.map +1 -1
- package/package.json +2 -2
- package/plugins/common/license.js +4 -4
- package/plugins/vite/license.js +4 -4
- package/src/engine/api.ts +5 -3
- package/src/engine/engine_addressables.ts +75 -9
- package/src/engine/engine_application.ts +7 -2
- package/src/engine/engine_context.ts +25 -1
- package/src/engine/engine_init.ts +2 -2
- package/src/engine/engine_input.ts +2 -2
- package/src/engine/engine_license.ts +113 -75
- package/src/engine/engine_lifecycle_functions_internal.ts +21 -2
- package/src/engine/engine_networking_blob.ts +3 -3
- package/src/engine/engine_networking_streams.ts +61 -17
- package/src/engine/engine_utils_qrcode.ts +2 -2
- package/src/engine/physics/workers/mesh-bvh/GenerateMeshBVHWorker.js +8 -1
- package/src/engine/webcomponents/fonts.ts +16 -1
- package/src/engine/webcomponents/icons.ts +8 -1
- package/src/engine/webcomponents/needle menu/needle-menu-spatial.ts +2 -2
- package/src/engine/webcomponents/needle menu/needle-menu.ts +33 -15
- package/src/engine/webcomponents/needle-engine.loading.ts +19 -7
- package/src/engine/webcomponents/needle-engine.ts +16 -6
- package/src/engine/xr/TempXRContext.ts +2 -2
- package/src/engine-components/export/usdz/USDZExporter.ts +4 -4
- package/dist/gltf-progressive-CdjpU5eP.min.js +0 -10
- 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 =
|
|
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 {
|
|
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 &&
|
|
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 {
|
|
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(() =>
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
if (
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
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 (
|
|
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 (
|
|
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 {
|
|
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 =
|
|
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 =
|
|
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() &&
|
|
408
|
+
// if (!isDevEnvironment() && _$zwcLl) {
|
|
397
409
|
// if (debugLicense) console.log("Waiting for runtime license check");
|
|
398
|
-
// await
|
|
399
|
-
// commercialLicense =
|
|
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 {
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
1209
|
-
|
|
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
|
-
}
|
|
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 {
|
|
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 (
|
|
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 {
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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};
|