@needle-tools/engine 4.16.4-next.90e872e → 4.16.4-next.d7334fa
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/dist/gltf-progressive-BJ9OrddA.js +28272 -0
- package/dist/gltf-progressive-Ck_bXBr_.umd.cjs +4022 -0
- package/dist/gltf-progressive-CqZYu6Hx.min.js +4022 -0
- package/dist/loader.worker-CCrD-Ycm.js +3 -0
- package/dist/{materialx-CT8J50pg.js → materialx-CEJOMiVq.js} +6 -7
- package/dist/{materialx-BrPdNmQT.umd.cjs → materialx-CN_PDqtU.umd.cjs} +1 -1
- package/dist/{materialx-D_ofN96q.min.js → materialx-DZYSWLvT.min.js} +1 -1
- package/dist/{needle-engine.bundle-dWi9rwQU.umd.cjs → needle-engine.bundle-7jhrheqM.umd.cjs} +159 -159
- package/dist/{needle-engine.bundle-j_dgrIWR.min.js → needle-engine.bundle-B1vy0_5d.min.js} +137 -137
- package/dist/{needle-engine.bundle-CTDEd5SB.js → needle-engine.bundle-BVUJloKV.js} +4192 -4185
- package/dist/needle-engine.d.ts +59 -53
- package/dist/needle-engine.js +3 -3
- package/dist/needle-engine.min.js +1 -1
- package/dist/needle-engine.umd.cjs +1 -1
- package/dist/three-examples.js +3294 -4643
- package/dist/three-examples.min.js +13 -47
- package/dist/three-examples.umd.cjs +12 -46
- package/dist/three.js +24892 -15999
- package/dist/three.min.js +214 -214
- package/dist/three.umd.cjs +208 -208
- package/dist/{vendor-DZ45lcA8.min.js → vendor-BPp9F5vR.min.js} +19 -19
- package/dist/{vendor-BsRxp-FT.js → vendor-CQMI3jTS.js} +862 -901
- package/dist/{vendor-BwxpsdCm.umd.cjs → vendor-CipoooTV.umd.cjs} +20 -20
- package/lib/engine/engine_feature_flags.d.ts +3 -3
- package/lib/engine/engine_feature_flags.js +3 -5
- package/lib/engine/engine_feature_flags.js.map +1 -1
- package/lib/engine/engine_lods.d.ts +9 -1
- package/lib/engine/engine_lods.js +9 -1
- package/lib/engine/engine_lods.js.map +1 -1
- package/lib/engine/engine_mainloop_utils.js +49 -60
- package/lib/engine/engine_mainloop_utils.js.map +1 -1
- package/lib/engine/engine_modules.d.ts +26 -30
- package/lib/engine/engine_modules.js +32 -103
- package/lib/engine/engine_modules.js.map +1 -1
- package/lib/engine/engine_three_utils.js +7 -13
- package/lib/engine/engine_three_utils.js.map +1 -1
- package/lib/engine/webcomponents/needle-engine.d.ts +6 -2
- package/lib/engine/webcomponents/needle-engine.js +56 -41
- package/lib/engine/webcomponents/needle-engine.js.map +1 -1
- package/package.json +3 -2
- package/plugins/vite/alias.d.ts +1 -1
- package/plugins/vite/alias.js +87 -82
- package/plugins/vite/build-pipeline.js +4 -3
- package/plugins/vite/dependencies.js +7 -5
- package/plugins/vite/drop.js +6 -8
- package/plugins/vite/editor-connection.js +2 -1
- package/plugins/vite/index.js +12 -0
- package/plugins/vite/poster.js +2 -1
- package/plugins/vite/reload.js +7 -2
- package/src/engine/engine_feature_flags.ts +3 -7
- package/src/engine/engine_lods.ts +10 -2
- package/src/engine/engine_mainloop_utils.ts +54 -62
- package/src/engine/engine_modules.ts +39 -101
- package/src/engine/engine_three_utils.ts +7 -15
- package/src/engine/webcomponents/needle-engine.ts +62 -47
- package/dist/gltf-progressive-DQa78GTA.min.js +0 -10
- package/dist/gltf-progressive-LOFTyzy4.umd.cjs +0 -10
- package/dist/gltf-progressive-_wvokUUu.js +0 -1528
- package/dist/loader.worker-BqODMeeW.js +0 -23
|
@@ -1,108 +1,46 @@
|
|
|
1
1
|
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
* If a module is already loaded it's also available in the `MODULE` variable.
|
|
8
|
-
*/
|
|
9
|
-
export namespace MODULES {
|
|
10
|
-
|
|
11
|
-
export namespace MaterialX {
|
|
12
|
-
export type TYPE = typeof import("@needle-tools/materialx");
|
|
13
|
-
export let MODULE: TYPE;
|
|
14
|
-
export let MAYBEMODULE: TYPE | null = null;
|
|
15
|
-
|
|
16
|
-
const callbacks: Array<(module: TYPE) => void> = [];
|
|
17
|
-
/** Wait for the module to be loaded (doesn't trigger a load) */
|
|
18
|
-
export function ready(): Promise<TYPE> {
|
|
19
|
-
if (MODULE) return Promise.resolve(MODULE);
|
|
20
|
-
return new Promise((resolve) => { callbacks.push(resolve); });
|
|
21
|
-
};
|
|
22
|
-
/** Load the module */
|
|
23
|
-
export async function load(): Promise<TYPE> {
|
|
24
|
-
if (MODULE) return MODULE;
|
|
25
|
-
const module = await import("@needle-tools/materialx");
|
|
26
|
-
MODULE = module;
|
|
27
|
-
MAYBEMODULE = module;
|
|
28
|
-
callbacks.forEach((callback) => callback(module));
|
|
29
|
-
callbacks.length = 0;
|
|
30
|
-
return module;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export namespace RAPIER_PHYSICS {
|
|
36
|
-
export type TYPE = typeof import("@dimforge/rapier3d-compat");
|
|
37
|
-
export let MODULE: TYPE;
|
|
38
|
-
export let MAYBEMODULE: TYPE | null = null;
|
|
39
|
-
|
|
40
|
-
const callbacks: Array<(module: TYPE) => void> = [];
|
|
41
|
-
/** Wait for the module to be loaded (doesn't trigger a load) */
|
|
42
|
-
export function ready(): Promise<TYPE> {
|
|
43
|
-
if (MODULE) return Promise.resolve(MODULE);
|
|
44
|
-
return new Promise((resolve) => { callbacks.push(resolve); });
|
|
45
|
-
};
|
|
46
|
-
/** Load the module */
|
|
47
|
-
export async function load(): Promise<TYPE> {
|
|
48
|
-
if (MODULE) return MODULE;
|
|
49
|
-
const module = await import("@dimforge/rapier3d-compat");
|
|
50
|
-
MODULE = module;
|
|
51
|
-
MAYBEMODULE = module;
|
|
52
|
-
callbacks.forEach((callback) => callback(module));
|
|
53
|
-
callbacks.length = 0;
|
|
54
|
-
return module;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export namespace POSTPROCESSING {
|
|
60
|
-
|
|
61
|
-
export type TYPE = typeof import("postprocessing");
|
|
62
|
-
export let MODULE: TYPE;
|
|
63
|
-
export let MAYBEMODULE: TYPE | null = null;
|
|
64
|
-
|
|
65
|
-
const callbacks: Array<(module: TYPE) => void> = [];
|
|
2
|
+
function createModule<T>(loader: () => Promise<T>) {
|
|
3
|
+
const callbacks: Array<(module: T) => void> = [];
|
|
4
|
+
const mod = {
|
|
5
|
+
MODULE: undefined as unknown as T,
|
|
6
|
+
MAYBEMODULE: null as T | null,
|
|
66
7
|
/** Wait for the module to be loaded (doesn't trigger a load) */
|
|
67
|
-
|
|
68
|
-
if (MODULE) return Promise.resolve(MODULE);
|
|
69
|
-
return new Promise(
|
|
70
|
-
}
|
|
71
|
-
|
|
8
|
+
ready(): Promise<T> {
|
|
9
|
+
if (mod.MODULE) return Promise.resolve(mod.MODULE);
|
|
10
|
+
return new Promise(resolve => { callbacks.push(resolve); });
|
|
11
|
+
},
|
|
72
12
|
/** Load the module */
|
|
73
|
-
|
|
74
|
-
if (MODULE) return MODULE;
|
|
75
|
-
const module = await
|
|
76
|
-
MODULE = module;
|
|
77
|
-
MAYBEMODULE = module;
|
|
78
|
-
callbacks
|
|
13
|
+
async load(): Promise<T> {
|
|
14
|
+
if (mod.MODULE) return mod.MODULE;
|
|
15
|
+
const module = await loader();
|
|
16
|
+
mod.MODULE = module;
|
|
17
|
+
mod.MAYBEMODULE = module;
|
|
18
|
+
for (const cb of callbacks) cb(module);
|
|
79
19
|
callbacks.length = 0;
|
|
80
20
|
return module;
|
|
81
21
|
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
export type TYPE = typeof import("n8ao");
|
|
87
|
-
export let MODULE: TYPE;
|
|
88
|
-
export let MAYBEMODULE: TYPE | null = null;
|
|
89
|
-
|
|
90
|
-
const callbacks: Array<(module: TYPE) => void> = [];
|
|
91
|
-
/** Wait for the module to be loaded (doesn't trigger a load) */
|
|
92
|
-
export function ready(): Promise<TYPE> {
|
|
93
|
-
if (MODULE) return Promise.resolve(MODULE);
|
|
94
|
-
return new Promise((resolve) => { callbacks.push(resolve); });
|
|
95
|
-
};
|
|
22
|
+
};
|
|
23
|
+
return mod;
|
|
24
|
+
}
|
|
96
25
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
26
|
+
/**
|
|
27
|
+
* External dependencies that are loaded on demand either by the engine automatically when needed or they can be loaded manually by calling the `load` function.
|
|
28
|
+
*
|
|
29
|
+
* Use the `ready` function to wait for the module to be loaded if you do not wand to trigger a load.
|
|
30
|
+
*
|
|
31
|
+
* If a module is already loaded it's also available in the `MODULE` variable.
|
|
32
|
+
*/
|
|
33
|
+
export const MODULES = {
|
|
34
|
+
MaterialX: createModule<typeof import("@needle-tools/materialx")>(
|
|
35
|
+
() => import("@needle-tools/materialx")
|
|
36
|
+
),
|
|
37
|
+
RAPIER_PHYSICS: createModule<typeof import("@dimforge/rapier3d-compat")>(
|
|
38
|
+
() => import("@dimforge/rapier3d-compat")
|
|
39
|
+
),
|
|
40
|
+
POSTPROCESSING: createModule<typeof import("postprocessing")>(
|
|
41
|
+
() => import("postprocessing")
|
|
42
|
+
),
|
|
43
|
+
POSTPROCESSING_AO: createModule<typeof import("n8ao")>(
|
|
44
|
+
() => import("n8ao")
|
|
45
|
+
),
|
|
46
|
+
};
|
|
@@ -586,17 +586,12 @@ export class Graphics {
|
|
|
586
586
|
this.scene.children.length = 0;
|
|
587
587
|
this.scene.add(mesh);
|
|
588
588
|
|
|
589
|
-
// Save state
|
|
590
589
|
const renderTarget = renderer.getRenderTarget();
|
|
591
|
-
const gl = renderer.getContext();
|
|
592
|
-
const depthTestEnabled = gl.getParameter(gl.DEPTH_TEST);
|
|
593
|
-
const depthWriteMask = gl.getParameter(gl.DEPTH_WRITEMASK);
|
|
594
|
-
const depthFunc = gl.getParameter(gl.DEPTH_FUNC);
|
|
595
|
-
|
|
596
590
|
|
|
597
591
|
// Set state
|
|
598
|
-
|
|
599
|
-
|
|
592
|
+
const gl = renderer.getContext();
|
|
593
|
+
if (depthTest) gl.enable(gl.DEPTH_TEST);
|
|
594
|
+
else gl.disable(gl.DEPTH_TEST);
|
|
600
595
|
renderer.state.buffers.depth.setMask(depthWrite);
|
|
601
596
|
|
|
602
597
|
renderer.setClearColor(new Color(0, 0, 0), 0);
|
|
@@ -608,13 +603,10 @@ export class Graphics {
|
|
|
608
603
|
renderer.clear();
|
|
609
604
|
renderer.render(this.scene, this.perspectiveCam);
|
|
610
605
|
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
depthBuffer.setTest(depthTestEnabled);
|
|
616
|
-
depthBuffer.setMask(depthWriteMask);
|
|
617
|
-
depthBuffer.setFunc(depthFunc);
|
|
606
|
+
// Restore defaults (not using gl.getParameter to save/restore — it causes a synchronous GPU-CPU stall)
|
|
607
|
+
renderer.setRenderTarget(renderTarget);
|
|
608
|
+
gl.enable(gl.DEPTH_TEST);
|
|
609
|
+
renderer.state.buffers.depth.setMask(true);
|
|
618
610
|
}
|
|
619
611
|
|
|
620
612
|
/**
|
|
@@ -168,7 +168,7 @@ export class NeedleEngineWebComponent extends HTMLElement implements INeedleEngi
|
|
|
168
168
|
*/
|
|
169
169
|
public get context() { return this._context; }
|
|
170
170
|
|
|
171
|
-
private _context
|
|
171
|
+
private _context?: Context;
|
|
172
172
|
private _overlay_ar!: AROverlayHandler;
|
|
173
173
|
private _loadingProgress01: number = 0;
|
|
174
174
|
private _loadingView?: ILoadingViewHandler;
|
|
@@ -298,12 +298,10 @@ export class NeedleEngineWebComponent extends HTMLElement implements INeedleEngi
|
|
|
298
298
|
|
|
299
299
|
|
|
300
300
|
this._overlay_ar = new AROverlayHandler();
|
|
301
|
-
this.
|
|
301
|
+
this.getOrCreateContext();
|
|
302
302
|
this.addEventListener("xr-session-started", this.onXRSessionStarted);
|
|
303
303
|
this.onSetupDesktop();
|
|
304
304
|
|
|
305
|
-
|
|
306
|
-
|
|
307
305
|
if (!this.getAttribute("src")) {
|
|
308
306
|
const global = (globalThis as any)["needle:codegen_files"] as unknown as string;
|
|
309
307
|
if (debug) console.log("src is null, trying to load from globalThis[\"needle:codegen_files\"]", global);
|
|
@@ -418,18 +416,23 @@ export class NeedleEngineWebComponent extends HTMLElement implements INeedleEngi
|
|
|
418
416
|
case "focus-rect":
|
|
419
417
|
{
|
|
420
418
|
const focus_rect = this.getAttribute("focus-rect") as HTMLElement | string | null;
|
|
421
|
-
if (focus_rect
|
|
419
|
+
if (focus_rect) {
|
|
420
|
+
const context = this.getOrCreateContext();
|
|
421
|
+
|
|
422
422
|
if (focus_rect === null) {
|
|
423
|
-
|
|
423
|
+
context.setCameraFocusRect(null);
|
|
424
424
|
}
|
|
425
425
|
else if (typeof focus_rect === "string" && focus_rect.length > 0) {
|
|
426
426
|
const element = document.querySelector(focus_rect);
|
|
427
|
-
|
|
427
|
+
if (!element) console.warn(`No element found for focus-rect selector: ${focus_rect}`);
|
|
428
|
+
context.setCameraFocusRect(element instanceof HTMLElement ? element : null);
|
|
428
429
|
}
|
|
429
430
|
else if (focus_rect instanceof HTMLElement) {
|
|
430
|
-
|
|
431
|
+
context.setCameraFocusRect(focus_rect);
|
|
432
|
+
}
|
|
433
|
+
else {
|
|
434
|
+
console.warn("Invalid focus-rect value. Expected a CSS selector string or an HTMLElement.", focus_rect);
|
|
431
435
|
}
|
|
432
|
-
|
|
433
436
|
}
|
|
434
437
|
}
|
|
435
438
|
break;
|
|
@@ -447,19 +450,22 @@ export class NeedleEngineWebComponent extends HTMLElement implements INeedleEngi
|
|
|
447
450
|
private _lastSourceFiles: Array<string> | null = null;
|
|
448
451
|
private _createContextPromise: Promise<boolean> | null = null;
|
|
449
452
|
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
453
|
+
/**
|
|
454
|
+
* Check if we have a context. If not a new one is created.
|
|
455
|
+
*/
|
|
456
|
+
private getOrCreateContext() {
|
|
454
457
|
if (!this._context) {
|
|
455
458
|
if (debug) console.warn("Create new context");
|
|
456
459
|
this._context = new Context({ domElement: this });
|
|
457
460
|
}
|
|
461
|
+
return this._context;
|
|
462
|
+
}
|
|
458
463
|
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
464
|
+
private async onLoad() {
|
|
465
|
+
|
|
466
|
+
if (!this.isConnected) return;
|
|
467
|
+
|
|
468
|
+
const context = this.getOrCreateContext();
|
|
463
469
|
|
|
464
470
|
const filesToLoad = this.getSourceFiles();
|
|
465
471
|
if (!this.checkIfSourceHasChanged(filesToLoad, this._lastSourceFiles)) {
|
|
@@ -477,7 +483,7 @@ export class NeedleEngineWebComponent extends HTMLElement implements INeedleEngi
|
|
|
477
483
|
|
|
478
484
|
if (filesToLoad === null || filesToLoad === undefined || filesToLoad.length <= 0) {
|
|
479
485
|
if (debug) console.warn("Clear scene", filesToLoad);
|
|
480
|
-
|
|
486
|
+
context.clear();
|
|
481
487
|
if (loadId !== this._loadId) return;
|
|
482
488
|
}
|
|
483
489
|
|
|
@@ -491,7 +497,7 @@ export class NeedleEngineWebComponent extends HTMLElement implements INeedleEngi
|
|
|
491
497
|
this.ensureLoadStartIsRegistered();
|
|
492
498
|
let useDefaultLoading = this.dispatchEvent(new CustomEvent("loadstart", {
|
|
493
499
|
detail: {
|
|
494
|
-
context:
|
|
500
|
+
context: context,
|
|
495
501
|
alias: alias
|
|
496
502
|
},
|
|
497
503
|
cancelable: true
|
|
@@ -581,9 +587,9 @@ export class NeedleEngineWebComponent extends HTMLElement implements INeedleEngi
|
|
|
581
587
|
|
|
582
588
|
const currentHash = this.getAttribute("hash");
|
|
583
589
|
if (currentHash !== null && currentHash !== undefined)
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
this._createContextPromise =
|
|
590
|
+
context.hash = currentHash;
|
|
591
|
+
context.alias = alias;
|
|
592
|
+
this._createContextPromise = context.create(args);
|
|
587
593
|
const success = await this._createContextPromise;
|
|
588
594
|
this.applyAttributes();
|
|
589
595
|
|
|
@@ -615,42 +621,45 @@ export class NeedleEngineWebComponent extends HTMLElement implements INeedleEngi
|
|
|
615
621
|
|
|
616
622
|
// #region applyAttributes
|
|
617
623
|
private applyAttributes() {
|
|
624
|
+
|
|
625
|
+
const context = this.getOrCreateContext();
|
|
626
|
+
|
|
618
627
|
// set tonemapping if configured
|
|
619
|
-
if (
|
|
628
|
+
if (context.renderer) {
|
|
620
629
|
const threeTonemapping = nameToThreeTonemapping(this.toneMapping);
|
|
621
630
|
if (threeTonemapping !== undefined) {
|
|
622
|
-
|
|
631
|
+
context.renderer.toneMapping = threeTonemapping;
|
|
623
632
|
}
|
|
624
633
|
const exposure = this.getAttribute("tone-mapping-exposure");
|
|
625
634
|
if (exposure !== null && exposure !== undefined) {
|
|
626
635
|
const value = parseFloat(exposure);
|
|
627
636
|
if (!isNaN(value))
|
|
628
|
-
|
|
637
|
+
context.renderer.toneMappingExposure = value;
|
|
629
638
|
}
|
|
630
639
|
}
|
|
631
640
|
|
|
632
641
|
const backgroundBlurriness = this.getAttribute("background-blurriness");
|
|
633
642
|
if (backgroundBlurriness !== null && backgroundBlurriness !== undefined) {
|
|
634
643
|
const value = parseFloat(backgroundBlurriness);
|
|
635
|
-
if (!isNaN(value)
|
|
636
|
-
|
|
644
|
+
if (!isNaN(value)) {
|
|
645
|
+
context.scene.backgroundBlurriness = value;
|
|
637
646
|
}
|
|
638
647
|
}
|
|
639
648
|
|
|
640
649
|
const environmentIntensity = this.getAttribute("environment-intensity");
|
|
641
|
-
if (environmentIntensity != undefined
|
|
650
|
+
if (environmentIntensity != undefined) {
|
|
642
651
|
const value = parseFloat(environmentIntensity);
|
|
643
|
-
if (!isNaN(value)
|
|
644
|
-
|
|
652
|
+
if (!isNaN(value))
|
|
653
|
+
context.scene.environmentIntensity = value;
|
|
645
654
|
}
|
|
646
655
|
|
|
647
656
|
const backgroundColor = this.getAttribute("background-color");
|
|
648
|
-
if (
|
|
657
|
+
if (context.renderer) {
|
|
649
658
|
if (typeof backgroundColor === "string" && backgroundColor.length > 0) {
|
|
650
659
|
const rgbaColor = RGBAColor.fromColorRepresentation(backgroundColor);
|
|
651
660
|
if (debug) console.debug("<needle-engine> background-color changed, str:", backgroundColor, "→", rgbaColor)
|
|
652
|
-
|
|
653
|
-
|
|
661
|
+
context.renderer.setClearColor(rgbaColor, rgbaColor.alpha);
|
|
662
|
+
context.scene.background = null;
|
|
654
663
|
}
|
|
655
664
|
// HACK: if we set background-color to a color and then back to null we want the background-image attribute to re-apply
|
|
656
665
|
else if (this.getAttribute("background-image")) {
|
|
@@ -660,19 +669,21 @@ export class NeedleEngineWebComponent extends HTMLElement implements INeedleEngi
|
|
|
660
669
|
}
|
|
661
670
|
|
|
662
671
|
private onXRSessionStarted = () => {
|
|
663
|
-
const
|
|
672
|
+
const context = this.getOrCreateContext();
|
|
673
|
+
|
|
674
|
+
const xrSessionMode = context.xrSessionMode;
|
|
664
675
|
if (xrSessionMode === "immersive-ar")
|
|
665
|
-
this.onEnterAR(
|
|
676
|
+
this.onEnterAR(context.xrSession!);
|
|
666
677
|
else if (xrSessionMode === "immersive-vr")
|
|
667
|
-
this.onEnterVR(
|
|
678
|
+
this.onEnterVR(context.xrSession!);
|
|
668
679
|
|
|
669
680
|
// handle session end:
|
|
670
|
-
|
|
671
|
-
this.dispatchEvent(new CustomEvent("xr-session-ended", { detail: { session:
|
|
681
|
+
context.xrSession?.addEventListener("end", () => {
|
|
682
|
+
this.dispatchEvent(new CustomEvent("xr-session-ended", { detail: { session: context.xrSession, context: this._context, sessionMode: xrSessionMode } }));
|
|
672
683
|
if (xrSessionMode === "immersive-ar")
|
|
673
|
-
this.onExitAR(
|
|
684
|
+
this.onExitAR(context.xrSession!);
|
|
674
685
|
else if (xrSessionMode === "immersive-vr")
|
|
675
|
-
this.onExitVR(
|
|
686
|
+
this.onExitVR(context.xrSession!);
|
|
676
687
|
});
|
|
677
688
|
};
|
|
678
689
|
|
|
@@ -741,7 +752,7 @@ export class NeedleEngineWebComponent extends HTMLElement implements INeedleEngi
|
|
|
741
752
|
if (typeof fn === "function") {
|
|
742
753
|
this._previouslyRegisteredMap.set(eventName, fn);
|
|
743
754
|
// @ts-ignore // not sure how to type this properly
|
|
744
|
-
this.addEventListener(eventName, evt => fn?.call(globalThis, this.
|
|
755
|
+
this.addEventListener(eventName, evt => fn?.call(globalThis, this.getOrCreateContext(), evt));
|
|
745
756
|
}
|
|
746
757
|
}
|
|
747
758
|
catch (err) {
|
|
@@ -783,35 +794,39 @@ export class NeedleEngineWebComponent extends HTMLElement implements INeedleEngi
|
|
|
783
794
|
* @internal
|
|
784
795
|
*/
|
|
785
796
|
onEnterAR(session: XRSession) {
|
|
797
|
+
const context = this.getOrCreateContext();
|
|
786
798
|
this.onSetupAR();
|
|
787
799
|
const overlayContainer = this.getAROverlayContainer();
|
|
788
|
-
this._overlay_ar.onBegin(
|
|
789
|
-
this.dispatchEvent(new CustomEvent("enter-ar", { detail: { session: session, context:
|
|
800
|
+
this._overlay_ar.onBegin(context, overlayContainer, session);
|
|
801
|
+
this.dispatchEvent(new CustomEvent("enter-ar", { detail: { session: session, context: context, htmlContainer: this._overlay_ar?.ARContainer } }));
|
|
790
802
|
}
|
|
791
803
|
|
|
792
804
|
/**
|
|
793
805
|
* @internal
|
|
794
806
|
*/
|
|
795
807
|
onExitAR(session: XRSession) {
|
|
796
|
-
this.
|
|
808
|
+
const context = this.getOrCreateContext();
|
|
809
|
+
this._overlay_ar.onEnd(context);
|
|
797
810
|
this.onSetupDesktop();
|
|
798
|
-
this.dispatchEvent(new CustomEvent("exit-ar", { detail: { session: session, context:
|
|
811
|
+
this.dispatchEvent(new CustomEvent("exit-ar", { detail: { session: session, context: context, htmlContainer: this._overlay_ar?.ARContainer } }));
|
|
799
812
|
}
|
|
800
813
|
|
|
801
814
|
/**
|
|
802
815
|
* @internal
|
|
803
816
|
*/
|
|
804
817
|
onEnterVR(session: XRSession) {
|
|
818
|
+
const context = this.getOrCreateContext();
|
|
805
819
|
this.onSetupVR();
|
|
806
|
-
this.dispatchEvent(new CustomEvent("enter-vr", { detail: { session: session, context:
|
|
820
|
+
this.dispatchEvent(new CustomEvent("enter-vr", { detail: { session: session, context: context } }));
|
|
807
821
|
}
|
|
808
822
|
|
|
809
823
|
/**
|
|
810
824
|
* @internal
|
|
811
825
|
*/
|
|
812
826
|
onExitVR(session: XRSession) {
|
|
827
|
+
const context = this.getOrCreateContext();
|
|
813
828
|
this.onSetupDesktop();
|
|
814
|
-
this.dispatchEvent(new CustomEvent("exit-vr", { detail: { session: session, context:
|
|
829
|
+
this.dispatchEvent(new CustomEvent("exit-vr", { detail: { session: session, context: context } }));
|
|
815
830
|
}
|
|
816
831
|
|
|
817
832
|
private onSetupAR() {
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import{BufferGeometry as W,Mesh as F,Box3 as ae,Vector3 as A,Sphere as Le,CompressedTexture as Be,Texture as $,Matrix3 as Ge,InterleavedBuffer as je,InterleavedBufferAttribute as Ne,BufferAttribute as Ue,TextureLoader as We,Matrix4 as _e,Clock as Fe,MeshStandardMaterial as ze}from"./three.min.js";import{DRACOLoader as qe,KTX2Loader as Ve,MeshoptDecoder as Xe,GLTFLoader as le}from"./three-examples.min.js";const He="";globalThis.GLTF_PROGRESSIVE_VERSION=He,console.debug("[gltf-progressive] version -");let k="https://www.gstatic.com/draco/versioned/decoders/1.5.7/",z="https://cdn.needle.tools/static/three/0.179.1/basis2/";const Ke=k,Ye=z,be=new URL(k+"draco_decoder.js");be.searchParams.append("range","true"),fetch(be,{method:"GET",headers:{Range:"bytes=0-1"}}).catch(i=>{console.debug(`Failed to fetch remote Draco decoder from ${k} (offline: ${typeof navigator<"u"?navigator.onLine:"unknown"})`),k===Ke&&Me("./include/draco/"),z===Ye&&De("./include/ktx2/")}).finally(()=>{Oe()});const Qe=()=>({dracoDecoderPath:k,ktx2TranscoderPath:z});function Me(i){k=i,T&&T[ce]!=k?(console.debug("Updating Draco decoder path to "+i),T[ce]=k,T.setDecoderPath(k),T.preload()):console.debug("Setting Draco decoder path to "+i)}function De(i){z=i,I&&I.transcoderPath!=z?(console.debug("Updating KTX2 transcoder path to "+i),I.setTranscoderPath(z),I.init()):console.debug("Setting KTX2 transcoder path to "+i)}function te(i){return Oe(),i?I.detectSupport(i):i!==null&&console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures might fail"),{dracoLoader:T,ktx2Loader:I,meshoptDecoder:re}}function ue(i){i.dracoLoader||i.setDRACOLoader(T),i.ktx2Loader||i.setKTX2Loader(I),i.meshoptDecoder||i.setMeshoptDecoder(re)}const ce=Symbol("dracoDecoderPath");let T,re,I;function Oe(){T||(T=new qe,T[ce]=k,T.setDecoderPath(k),T.setDecoderConfig({type:"js"}),T.preload()),I||(I=new Ve,I.setTranscoderPath(z),I.init()),re||(re=Xe)}const de=new WeakMap;function he(i,t){let e=de.get(i);e?e=Object.assign(e,t):e=t,de.set(i,e)}const Je=le.prototype.load;function Ze(...i){const t=de.get(this);let e=i[0];const r=new URL(e,window.location.href);if(r.hostname.endsWith("needle.tools")){const n=t?.progressive!==void 0?t.progressive:!0,s=t?.usecase?t.usecase:"default";n?this.requestHeader.Accept=`*/*;progressive=allowed;usecase=${s}`:this.requestHeader.Accept=`*/*;usecase=${s}`,e=r.toString()}return i[0]=e,Je?.call(this,...i)}le.prototype.load=Ze,j("debugprogressive");function j(i){if(typeof window>"u")return!1;const t=new URL(window.location.href).searchParams.get(i);return t==null||t==="0"||t==="false"?!1:t===""?!0:t}function et(i,t){if(t===void 0||i===void 0||t.startsWith("./")||t.startsWith("http")||t.startsWith("data:")||t.startsWith("blob:"))return t;const e=i.lastIndexOf("/");if(e>=0){const r=i.substring(0,e+1);for(;r.endsWith("/")&&t.startsWith("/");)t=t.substring(1);return r+t}return t}function Se(){return se!==void 0||(se=/iPhone|iPad|iPod|Android|IEMobile/i.test(navigator.userAgent),j("debugprogressive")&&console.log("[glTF Progressive]: isMobileDevice",se)),se}let se;function Te(){if(typeof window>"u")return!1;const i=new URL(window.location.href),t=i.hostname==="localhost"||/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(i.hostname);return i.hostname==="127.0.0.1"||t}class tt{constructor(t=100,e={}){this.maxConcurrent=t,this.debug=e.debug??!1,window.requestAnimationFrame(this.tick)}_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:n}=this._queue.shift();n({use:s=>this.add(r,s)})}}}function rt(i){const t=i.image?.width??0,e=i.image?.height??0,r=i.image?.depth??1,n=Math.floor(Math.log2(Math.max(t,e,r)))+1,s=st(i);return t*e*r*s*(1-Math.pow(.25,n))/(1-.25)}function st(i){let t=4;const e=i.format;e===1024||e===1025?t=1:e===1026||e===1027?t=2:e===1022||e===1029?t=3:(e===1023||e===1033)&&(t=4);let r=1;const n=i.type;return n===1009||n===1010?r=1:n===1011||n===1012?r=2:n===1013||n===1014||n===1015?r=4:n===1016&&(r=2),t*r}const nt=typeof window>"u"&&typeof document>"u",ge=Symbol("needle:raycast-mesh");function V(i){return i?.[ge]instanceof W?i[ge]:null}function ot(i,t){if((i.type==="Mesh"||i.type==="SkinnedMesh")&&!V(i)){const e=at(t);e.userData={isRaycastMesh:!0},i[ge]=e}}function it(i=!0){if(i){if(H)return;const t=H=F.prototype.raycast;F.prototype.raycast=function(e,r){const n=this,s=V(n);let o;s&&n.isMesh&&(o=n.geometry,n.geometry=s),t.call(this,e,r),o&&(n.geometry=o)}}else{if(!H)return;F.prototype.raycast=H,H=null}}let H=null;function at(i){const t=new W;for(const e in i.attributes)t.setAttribute(e,i.getAttribute(e));return t.setIndex(i.getIndex()),t}const N=new Array,h=j("debugprogressive");let K,q=-1;if(h){let i=function(){q+=1,q>=t&&(q=-1),console.log(`Toggle LOD level [${q}]`)},t=6;window.addEventListener("keyup",e=>{e.key==="p"&&i(),e.key==="w"&&(K=!K,console.log(`Toggle wireframe [${K}]`));const r=parseInt(e.key);!isNaN(r)&&r>=0&&(q=r,console.log(`Set LOD level to [${q}]`))})}function Pe(i){if(h&&K!==void 0)if(Array.isArray(i))for(const t of i)Pe(t);else i&&"wireframe"in i&&(i.wireframe=K===!0)}const Y=new Array;let lt=0;const ut=Se()?2:10;function ct(i){if(Y.length<ut){const e=Y.length;h&&console.warn(`[Worker] Creating new worker #${e}`);const r=ve.createWorker(i||{});return Y.push(r),r}const t=lt++%Y.length;return Y[t]}class ve{constructor(t,e){this.worker=t,this._debug=e.debug??!1,t.onmessage=r=>{const n=r.data;switch(this._debug&&console.log("[Worker] EVENT",n),n.type){case"loaded-gltf":for(const s of this._running)if(s.url===n.result.url){dt(n.result),s.resolve(n.result);const o=s.url;o.startsWith("blob:")&&URL.revokeObjectURL(o)}}},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("./loader.worker-BqODMeeW.js",import.meta.url).toString()}';`],{type:"text/javascript"})),{type:"module"});return new ve(e,t)}_running=[];_webglRenderer=null;async load(t,e){const r=Qe();let n=e?.renderer;n||(this._webglRenderer??=(async()=>{const{WebGLRenderer:l}=await import("./three.min.js").then(a=>a.THREE);return new l})(),n=await this._webglRenderer);const s=te(n).ktx2Loader.workerConfig;t instanceof URL?t=t.toString():t.startsWith("file:")?t=URL.createObjectURL(new Blob([t])):!t.startsWith("blob:")&&!t.startsWith("http:")&&!t.startsWith("https:")&&(t=new URL(t,window.location.href).toString());const o={type:"load",url:t,dracoDecoderPath:r.dracoDecoderPath,ktx2TranscoderPath:r.ktx2TranscoderPath,ktx2LoaderConfig:s};return this._debug&&console.debug("[Worker] Sending load request",o),this.worker.postMessage(o),new Promise(l=>{this._running.push({url:t.toString(),resolve:l})})}_debug=!1}function dt(i){for(const t of i.geometries){const e=t.geometry,r=new W;if(r.name=e.name||"",e.index){const n=e.index;r.setIndex(fe(n))}for(const n in e.attributes){const s=e.attributes[n],o=fe(s);r.setAttribute(n,o)}if(e.morphAttributes)for(const n in e.morphAttributes){const s=e.morphAttributes[n].map(o=>fe(o));r.morphAttributes[n]=s}if(r.morphTargetsRelative=e.morphTargetsRelative??!1,r.boundingBox=new ae,r.boundingBox.min=new A(e.boundingBox?.min.x,e.boundingBox?.min.y,e.boundingBox?.min.z),r.boundingBox.max=new A(e.boundingBox?.max.x,e.boundingBox?.max.y,e.boundingBox?.max.z),r.boundingSphere=new Le(new A(e.boundingSphere?.center.x,e.boundingSphere?.center.y,e.boundingSphere?.center.z),e.boundingSphere?.radius),e.groups)for(const n of e.groups)r.addGroup(n.start,n.count,n.materialIndex);e.userData&&(r.userData=e.userData),t.geometry=r}for(const t of i.textures){const e=t.texture;let r=null;if(e.isCompressedTexture){const n=e.mipmaps,s=e.image?.width||e.source?.data?.width||-1,o=e.image?.height||e.source?.data?.height||-1;r=new Be(n,s,o,e.format,e.type,e.mapping,e.wrapS,e.wrapT,e.magFilter,e.minFilter,e.anisotropy,e.colorSpace)}else r=new $(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 Ge(...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 i}function fe(i){let t=i;if("isInterleavedBufferAttribute"in i&&i.isInterleavedBufferAttribute){const e=i.data,r=e.array,n=new je(r,e.stride);t=new Ne(n,i.itemSize,r.byteOffset,i.normalized),t.offset=i.offset}else"isBufferAttribute"in i&&i.isBufferAttribute&&(t=new Ue(i.array,i.itemSize,i.normalized),t.usage=i.usage,t.gpuType=i.gpuType,t.updateRanges=i.updateRanges);return t}const ht=j("gltf-progressive-worker");j("gltf-progressive-reduce-mipmaps");const X=j("gltf-progressive-gc"),me=Symbol("needle-progressive-texture"),B="NEEDLE_progressive";class m{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,n="LODS:minmax",s=t[n];if(s!=null)return s;if(e||(e={min_count:1/0,max_count:0,lods:[]}),Array.isArray(t)){for(const l of t)this.getMaterialMinMaxLODsCount(l,e);return t[n]=e,e}if(h==="verbose"&&console.log("getMaterialMinMaxLODsCount",t),t.type==="ShaderMaterial"||t.type==="RawShaderMaterial"){const l=t;for(const a of Object.keys(l.uniforms)){const u=l.uniforms[a].value;u?.isTexture===!0&&o(u,e)}}else if(t.isMaterial)for(const l of Object.keys(t)){const a=t[l];a?.isTexture===!0&&o(a,e)}else h&&console.warn(`[getMaterialMinMaxLODsCount] Unsupported material type: ${t.type}`);return t[n]=e,e;function o(l,a){const u=r.getAssignedLODInformation(l);if(u){const w=r.lodInfos.get(u.key);if(w&&w.lods){a.min_count=Math.min(a.min_count,w.lods.length),a.max_count=Math.max(a.max_count,w.lods.length);for(let p=0;p<w.lods.length;p++){const y=w.lods[p];y.width&&(a.lods[p]=a.lods[p]||{min_height:1/0,max_height:0},a.lods[p].min_height=Math.min(a.lods[p].min_height,y.height),a.lods[p].max_height=Math.max(a.lods[p].max_height,y.height))}}}}}static hasLODLevelAvailable(t,e){if(Array.isArray(t)){for(const s of t)if(this.hasLODLevelAvailable(s,e))return!0;return!1}if(t.isMaterial===!0){for(const s of Object.keys(t)){const o=t[s];if(o&&o.isTexture&&this.hasLODLevelAvailable(o,e))return!0}return!1}else if(t.isGroup===!0){for(const s of t.children)if(s.isMesh===!0&&this.hasLODLevelAvailable(s,e))return!0}let r,n;if(t.isMesh?r=t.geometry:(t.isBufferGeometry||t.isTexture)&&(r=t),r&&r?.userData?.LODS){const s=r.userData.LODS;if(n=this.lodInfos.get(s.key),e===void 0)return n!=null;if(n)return Array.isArray(n.lods)?e<n.lods.length:e===0}return!1}static assignMeshLOD(t,e){if(!t)return Promise.resolve(null);if(t instanceof F||t.isMesh===!0){const r=t.geometry,n=this.getAssignedLODInformation(r);if(!n)return Promise.resolve(null);for(const s of N)s.onBeforeGetLODMesh?.(t,e);return t["LOD:requested level"]=e,m.getOrLoadLOD(r,e).then(s=>{if(Array.isArray(s)){const o=n.index||0;s=s[o]}return t["LOD:requested level"]===e&&(delete t["LOD:requested level"],s&&r!=s&&(s?.isBufferGeometry?t.geometry=s:h&&console.error("Invalid LOD geometry",s))),s}).catch(s=>(console.error("Error loading mesh LOD",t,s),null))}else h&&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){if(!t)return Promise.resolve(null);if(t.isMesh===!0){const r=t;if(Array.isArray(r.material)){const n=new Array;for(const s of r.material){const o=this.assignTextureLOD(s,e);n.push(o)}return Promise.all(n).then(s=>{const o=new Array;for(const l of s)Array.isArray(l)&&o.push(...l);return o})}else return this.assignTextureLOD(r.material,e)}if(t.isMaterial===!0){const r=t,n=[],s=new Array;if(r.uniforms&&(r.isRawShaderMaterial||r.isShaderMaterial===!0)){const o=r;for(const l of Object.keys(o.uniforms)){const a=o.uniforms[l].value;if(a?.isTexture===!0){const u=this.assignTextureLODForSlot(a,e,r,l).then(w=>(w&&o.uniforms[l].value!=w&&(o.uniforms[l].value=w,o.uniformsNeedUpdate=!0),w));n.push(u),s.push(l)}}}else for(const o of Object.keys(r)){const l=r[o];if(l?.isTexture===!0){const a=this.assignTextureLODForSlot(l,e,r,o);n.push(a),s.push(o)}}return Promise.all(n).then(o=>{const l=new Array;for(let a=0;a<o.length;a++){const u=o[a],w=s[a];u&&u.isTexture===!0?l.push({material:r,slot:w,texture:u,level:e}):l.push({material:r,slot:w,texture:null,level:e})}return l})}if(t instanceof $||t.isTexture===!0){const r=t;return this.assignTextureLODForSlot(r,e,null,null)}return Promise.resolve(null)}static assignTextureLODForSlot(t,e,r,n){return t?.isTexture!==!0?Promise.resolve(null):n==="glyphMap"?Promise.resolve(t):m.getOrLoadLOD(t,e).then(s=>{if(Array.isArray(s))return console.warn("Progressive: Got an array of textures for a texture slot, this should not happen..."),null;if(s?.isTexture===!0){if(s!=t&&r&&n){const o=r[n];if(o&&!h){const l=this.getAssignedLODInformation(o);if(l&&l?.level<e)return h==="verbose"&&console.warn("Assigned texture level is already higher: ",l.level,e,r,o,s),s&&s!==o&&((h||X)&&console.log(`[gltf-progressive] Disposing rejected lower-quality texture LOD ${e} (assigned is ${l.level})`,s.uuid),s.dispose()),null}if(this.trackTextureUsage(s),o&&o!==s&&this.untrackTextureUsage(o)&&(h||X)){const l=this.getAssignedLODInformation(o);console.log(`[gltf-progressive] Disposed old texture LOD ${l?.level??"?"} \u2192 ${e} for ${r.name||r.type}.${n}`,o.uuid)}r[n]=s}return s}else h=="verbose"&&console.warn("No LOD found for",t,e);return null}).catch(s=>(console.error("Error loading LOD",t,s),null))}parser;url;constructor(t){const e=t.options.path;h&&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&&m.registerMesh(this.url,e.guid,r,e.lods?.length,0,e),r))):null};afterRoot(t){return h&&console.log("AFTER",this.url,t),this.parser.json.textures?.forEach((e,r)=>{if(e?.extensions){const n=e?.extensions[B];if(n){if(!n.lods){h&&console.warn("Texture has no LODs",n);return}let s=!1;for(const o of this.parser.associations.keys())o.isTexture===!0&&this.parser.associations.get(o)?.textures===r&&(s=!0,m.registerTexture(this.url,o,n.lods?.length,r,n));s||this.parser.getDependency("texture",r).then(o=>{o&&m.registerTexture(this.url,o,n.lods?.length,r,n)})}}}),this.parser.json.meshes?.forEach((e,r)=>{if(e?.extensions){const n=e?.extensions[B];if(n&&n.lods){for(const s of this.parser.associations.keys())if(s.isMesh){const o=this.parser.associations.get(s);o?.meshes===r&&m.registerMesh(this.url,n.guid,s,n.lods.length,o.primitives,n)}}}}),null}static registerTexture=(t,e,r,n,s)=>{if(!e){h&&console.error("!! gltf-progressive: Called register texture without texture");return}if(h){const l=e.image?.width||e.source?.data?.width||0,a=e.image?.height||e.source?.data?.height||0;console.log(`> gltf-progressive: register texture[${n}] "${e.name||e.uuid}", Current: ${l}x${a}, Max: ${s.lods[0]?.width}x${s.lods[0]?.height}, uuid: ${e.uuid}`,s,e)}e.source&&(e.source[me]=s);const o=s.guid;m.assignLODInformation(t,e,o,r,n),m.lodInfos.set(o,s),m.lowresCache.set(o,new WeakRef(e))};static registerMesh=(t,e,r,n,s,o)=>{const l=r.geometry;if(!l){h&&console.warn("gltf-progressive: Register mesh without geometry");return}l.userData||(l.userData={}),h&&console.log("> Progressive: register mesh "+r.name,{index:s,uuid:r.uuid},o,r),m.assignLODInformation(t,l,e,n,s),m.lodInfos.set(e,o);let a=m.lowresCache.get(e)?.deref();a?a.push(r.geometry):a=[r.geometry],m.lowresCache.set(e,new WeakRef(a)),n>0&&!V(r)&&ot(r,l);for(const u of N)u.onRegisteredNewMesh?.(r,o)};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 n=r;this.textureRefCounts.delete(n.uuid),n.dispose()}else if(Array.isArray(r))for(const n of r)n.dispose()}this.lowresCache.delete(t)}for(const[r,n]of this.cache)r.includes(t)&&(this._disposeCacheEntry(n),this.cache.delete(r))}else{this.lodInfos.clear();for(const[,e]of this.lowresCache){const r=e.deref();if(r){if(r.isTexture){const n=r;this.textureRefCounts.delete(n.uuid),n.dispose()}else if(Array.isArray(r))for(const n of r)n.dispose()}}this.lowresCache.clear();for(const[,e]of this.cache)this._disposeCacheEntry(e);this.cache.clear(),this.textureRefCounts.clear()}}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=m.cache.get(t);(h||X)&&console.debug(`[gltf-progressive] Memory: Resource GC'd
|
|
2
|
-
${t}`),e instanceof WeakRef&&(e.deref()||(m.cache.delete(t),(h||X)&&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),h==="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(h==="verbose"||X)&&s("[gltf-progressive] Memory: Untrack untracked texture (dispose immediately)",0),t.dispose(),!0;const n=r-1;if(n<=0)return this.textureRefCounts.delete(e),(h||X)&&s("[gltf-progressive] Memory: Dispose texture",n),t.dispose(),!0;return this.textureRefCounts.set(e,n),h==="verbose"&&s("[gltf-progressive] Memory: Untrack texture",n),!1;function s(o,l){let a=t.image?.width||t.source?.data?.width||0,u=t.image?.height||t.source?.data?.height||0;const w=a&&u?`${a}x${u}`:"N/A";let p="N/A";a&&u&&(p=`~${(rt(t)/(1024*1024)).toFixed(2)} MB`),console.log(`${o} \u2014 ${t.name} ${w} (${p}), refCount: ${r} \u2192 ${l}
|
|
3
|
-
${e}`)}}static workers=[];static _workersIndex=0;static async getOrLoadLOD(t,e){const r=h=="verbose",n=this.getAssignedLODInformation(t);if(!n)return h&&console.warn(`[gltf-progressive] No LOD information found: ${t.name}, uuid: ${t.uuid}, type: ${t.type}`,t),null;const s=n?.key;let o;if(t.isTexture===!0){const l=t;l.source&&l.source[me]&&(o=l.source[me])}if(o||(o=m.lodInfos.get(s)),!o)h&&console.warn(`Can not load LOD ${e}: no LOD info found for "${s}" ${t.name}`,t.type,m.lodInfos);else{if(e>0){let u=!1;const w=Array.isArray(o.lods);if(w&&e>=o.lods.length?u=!0:w||(u=!0),u){const p=this.lowresCache.get(s);if(p){const y=p.deref();if(y)return y;this.lowresCache.delete(s),h&&console.log(`[gltf-progressive] Lowres cache entry was GC'd: ${s}`)}return null}}const l=Array.isArray(o.lods)?o.lods[e]?.path:o.lods;if(!l)return h&&!o["missing:uri"]&&(o["missing:uri"]=!0,console.warn("Missing uri for progressive asset for LOD "+e,o)),null;const a=et(n.url,l);if(a.endsWith(".glb")||a.endsWith(".gltf")){if(!o.guid)return console.warn("missing pointer for glb/gltf texture",o),null;const u=a+"_"+o.guid,w=await this.queue.slot(a),p=this.cache.get(u);if(p!==void 0)if(r&&console.log(`LOD ${e} was already loading/loaded: ${u}`),p instanceof WeakRef){const c=p.deref();if(c){let x=c,v=!1;if(x instanceof $&&t instanceof $?x.image?.data||x.source?.data?x=this.copySettings(t,x):v=!0:x instanceof W&&t instanceof W&&(x.attributes.position?.array||(v=!0)),!v)return x}this.cache.delete(u),h&&console.log(`[gltf-progressive] Re-loading GC'd/disposed resource: ${u}`)}else{let c=await p.catch(v=>(console.error(`Error loading LOD ${e} from ${a}
|
|
4
|
-
`,v),null)),x=!1;if(c==null||(c instanceof $&&t instanceof $?c.image?.data||c.source?.data?c=this.copySettings(t,c):(x=!0,this.cache.delete(u)):c instanceof W&&t instanceof W&&(c.attributes.position?.array||(x=!0,this.cache.delete(u)))),!x)return c}if(!w.use)return h&&console.log(`LOD ${e} was aborted: ${a}`),null;const y=o,P=new Promise(async(c,x)=>{if(ht){const g=await(await ct({})).load(a);if(g.textures.length>0)for(const d of g.textures){let f=d.texture;return m.assignLODInformation(n.url,f,s,e,void 0),t instanceof $&&(f=this.copySettings(t,f)),f&&(f.guid=y.guid),c(f)}if(g.geometries.length>0){const d=new Array;for(const f of g.geometries){const L=f.geometry;m.assignLODInformation(n.url,L,s,e,f.primitiveIndex),d.push(L)}return c(d)}return c(null)}const v=new le;ue(v),h&&(await new Promise(g=>setTimeout(g,1e3)),r&&console.warn("Start loading (delayed) "+a,y.guid));let C=a;if(y&&Array.isArray(y.lods)){const g=y.lods[e];g.hash&&(C+="?v="+g.hash)}const b=await v.loadAsync(C).catch(g=>(console.error(`Error loading LOD ${e} from ${a}
|
|
5
|
-
`,g),c(null)));if(!b)return c(null);const R=b.parser;r&&console.log("Loading finished "+a,y.guid);let M=0;if(b.parser.json.textures){let g=!1;for(const d of b.parser.json.textures){if(d?.extensions){const f=d?.extensions[B];if(f?.guid&&f.guid===y.guid){g=!0;break}}M++}if(g){let d=await R.getDependency("texture",M);return d&&m.assignLODInformation(n.url,d,s,e,void 0),r&&console.log('change "'+t.name+'" \u2192 "'+d.name+'"',a,M,d,u),t instanceof $&&(d=this.copySettings(t,d)),d&&(d.guid=y.guid),c(d)}else h&&console.warn("Could not find texture with guid",y.guid,b.parser.json)}if(M=0,b.parser.json.meshes){let g=!1;for(const d of b.parser.json.meshes){if(d?.extensions){const f=d?.extensions[B];if(f?.guid&&f.guid===y.guid){g=!0;break}}M++}if(g){const d=await R.getDependency("mesh",M);if(r&&console.log(`Loaded Mesh "${d.name}"`,a,M,d,u),d.isMesh===!0){const f=d.geometry;return m.assignLODInformation(n.url,f,s,e,0),c(f)}else{const f=new Array;for(let L=0;L<d.children.length;L++){const S=d.children[L];if(S.isMesh===!0){const G=S.geometry;m.assignLODInformation(n.url,G,s,e,L),f.push(G)}}return c(f)}}else h&&console.warn("Could not find mesh with guid",y.guid,b.parser.json)}return c(null)});this.cache.set(u,P),w.use(P);const _=await P;return _!=null?_ instanceof $?(this.cache.set(u,new WeakRef(_)),m._resourceRegistry.register(_,u)):Array.isArray(_)?this.cache.set(u,Promise.resolve(_)):this.cache.set(u,Promise.resolve(_)):this.cache.set(u,Promise.resolve(null)),_}else if(t instanceof $){r&&console.log("Load texture from uri: "+a);const u=await new We().loadAsync(a);return u?(u.guid=o.guid,u.flipY=!1,u.needsUpdate=!0,u.colorSpace=t.colorSpace,r&&console.log(o,u)):h&&console.warn("failed loading",a),u}}return null}static maxConcurrent=50;static queue=new tt(m.maxConcurrent,{debug:h!=!1});static assignLODInformation(t,e,r,n,s){if(!e)return;e.userData||(e.userData={});const o=new gt(t,r,n,s);e.userData.LODS=o,"source"in e&&typeof e.source=="object"&&(e.source.LODS=o)}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?(h==="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 gt{url;key;level;index;constructor(t,e,r,n){this.url=t,this.key=e,this.level=r,n!=null&&(this.index=n)}}class pe{static addPromise=(t,e,r,n)=>{n.forEach(s=>{s.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(n=>{this._resolve=n}),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){h&&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)){let n=this._seen.get(e);if(n>=this._maxPromisesPerObject){h&&console.warn("PromiseGroup: Already awaiting object ignoring new promise for it.");return}this._seen.set(e,n+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 E=j("debugprogressive"),ft=j("noprogressive"),xe=Symbol("Needle:LODSManager"),we=Symbol("Needle:LODState"),U=Symbol("Needle:CurrentLOD"),O={mesh_lod:-1,texture_lod:-1};let Q=class D{static debugDrawLine;static getObjectLODState(t){return t[we]}static addPlugin(t){N.push(t)}static removePlugin(t){const e=N.indexOf(t);e>=0&&N.splice(e,1)}static get(t,e){if(t[xe])return console.debug("[gltf-progressive] LODsManager already exists for this renderer"),t[xe];const r=new D(t,{engine:"unknown",...e});return t[xe]=r,r}renderer;context;projectionScreenMatrix=new _e;get plugins(){return N}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 pe(this.#s,{...t});this._newPromiseGroups.push(r);const n=performance.now();return r.ready.finally(()=>{const s=this._newPromiseGroups.indexOf(r);s>=0&&(this._newPromiseGroups.splice(s,1),Te()&&performance.measure("LODsManager:awaitLoading",{start:n,detail:{id:e,name:t?.name,awaited:r.awaitedCount,resolved:r.resolvedCount}}))}),r.ready}_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){t==="changed"&&this._lodchangedlisteners.push(e)}removeEventListener(t,e){if(t==="changed"){const r=this._lodchangedlisteners.indexOf(e);r>=0&&this._lodchangedlisteners.splice(r,1)}}constructor(t,e){this.renderer=t,this.context={...e}}#t;#o=new Fe;#s=0;#n=0;#i=0;#r=0;_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;te(this.renderer),this.renderer.render=function(r,n){const s=e.renderer.getRenderTarget();(s==null||"isXRRenderTarget"in s&&s.isXRRenderTarget)&&(t=0,e.#s+=1,e.#n=e.#o.getDelta(),e.#i+=e.#n,e._fpsBuffer.shift(),e._fpsBuffer.push(1/e.#n),e.#r=e._fpsBuffer.reduce((l,a)=>l+a)/e._fpsBuffer.length,E&&e.#s%200===0&&console.log("FPS",Math.round(e.#r),"Interval:",e.#e));const o=t++;e.#t.call(this,r,n),e.onAfterRender(r,n,o)}}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 n=this.renderer.renderLists.get(t,0).opaque;let s=!0;if(n.length===1){const o=n[0].material;(o.name==="EffectMaterial"||o.name==="CopyShader")&&(s=!1)}if((e.parent&&e.parent.type==="CubeCamera"||r>=1&&e.type==="OrthographicCamera")&&(s=!1),s){if(ft||(this.updateInterval==="auto"?this.#r<40&&this.#e<10?(this.#e+=1,E&&console.warn("\u2193 Reducing LOD updates",this.#e,this.#r.toFixed(0))):this.#r>=60&&this.#e>1&&(this.#e-=1,E&&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.renderer.renderLists.get(t,0),n=r.opaque;this.projectionScreenMatrix.multiplyMatrices(e.projectionMatrix,e.matrixWorldInverse);const s=this.targetTriangleDensity;for(const a of n){if(a.material&&(a.geometry?.type==="BoxGeometry"||a.geometry?.type==="BufferGeometry")&&(a.material.name==="SphericalGaussianBlur"||a.material.name=="BackgroundCubeMaterial"||a.material.name==="CubemapFromEquirect"||a.material.name==="EquirectangularToCubeUV")){E&&(a.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]||(a.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]=!0,console.warn("Ignoring skybox or BLIT object",a,a.material.name,a.material.type)));continue}switch(a.material.type){case"LineBasicMaterial":case"LineDashedMaterial":case"PointsMaterial":case"ShadowMaterial":case"MeshDistanceMaterial":case"MeshDepthMaterial":continue}if(E==="color"&&a.material&&!a.object.progressive_debug_color){a.object.progressive_debug_color=!0;const w=Math.random()*16777215,p=new ze({color:w});a.object.material=p}const u=a.object;(u instanceof F||u.isMesh)&&this.updateLODs(t,e,u,s)}const o=r.transparent;for(const a of o){const u=a.object;(u instanceof F||u.isMesh)&&this.updateLODs(t,e,u,s)}const l=r.transmissive;for(const a of l){const u=a.object;(u instanceof F||u.isMesh)&&this.updateLODs(t,e,u,s)}}updateLODs(t,e,r,n){r.userData||(r.userData={});let s=r[we];if(s||(s=new mt,r[we]=s),s.frames++<2)return;for(const l of N)l.onBeforeUpdateLOD?.(this.renderer,t,e,r);const o=this.overrideLodLevel!==void 0?this.overrideLodLevel:q;o>=0?(O.mesh_lod=o,O.texture_lod=o):(this.calculateLodLevel(e,r,s,n,O),O.mesh_lod=Math.round(O.mesh_lod),O.texture_lod=Math.round(O.texture_lod)),O.mesh_lod>=0&&this.loadProgressiveMeshes(r,O.mesh_lod),r.material&&O.texture_lod>=0&&this.loadProgressiveTextures(r.material,O.texture_lod,o),h&&r.material&&!r.isGizmo&&Pe(r.material);for(const l of N)l.onAfterUpdatedLOD?.(this.renderer,t,e,r,O);s.lastLodLevel_Mesh=O.mesh_lod,s.lastLodLevel_Texture=O.texture_lod}loadProgressiveTextures(t,e,r){if(!t)return;if(Array.isArray(t)){for(const s of t)this.loadProgressiveTextures(s,e);return}let n=!1;if((t[U]===void 0||e<t[U])&&(n=!0),r!==void 0&&r>=0&&(n=t[U]!=r,e=r),n){t[U]=e;const s=m.assignTextureLOD(t,e).then(o=>{this._lodchangedlisteners.forEach(l=>l({type:"texture",level:e,object:t}))});pe.addPromise("texture",t,s,this._newPromiseGroups)}}loadProgressiveMeshes(t,e){if(!t)return Promise.resolve(null);let r=t[U]!==e;const n=t["DEBUG:LOD"];if(n!=null&&(r=t[U]!=n,e=n),r){t[U]=e;const s=t.geometry,o=m.assignMeshLOD(t,e).then(l=>(l&&t[U]==e&&s!=t.geometry&&this._lodchangedlisteners.forEach(a=>a({type:"mesh",level:e,object:t})),l));return pe.addPromise("mesh",t,o,this._newPromiseGroups),o}return Promise.resolve(null)}_sphere=new Le;_tempBox=new ae;_tempBox2=new ae;tempMatrix=new _e;_tempWorldPosition=new A;_tempBoxSize=new A;_tempBox2Size=new A;static corner0=new A;static corner1=new A;static corner2=new A;static corner3=new A;static _tempPtInside=new A;static isInside(t,e){const r=t.min,n=t.max,s=(r.x+n.x)*.5,o=(r.y+n.y)*.5;return this._tempPtInside.set(s,o,r.z).applyMatrix4(e).z<0}static skinnedMeshBoundsFrameOffsetCounter=0;static $skinnedMeshBoundsOffset=Symbol("gltf-progressive-skinnedMeshBoundsOffset");calculateLodLevel(t,e,r,n,s){if(!e){s.mesh_lod=-1,s.texture_lod=-1;return}if(!t){s.mesh_lod=-1,s.texture_lod=-1;return}let o=10+1,l=!1;if(E&&e["DEBUG:LOD"]!=null)return e["DEBUG:LOD"];const a=m.getMeshLODExtension(e.geometry)?.lods,u=m.getPrimitiveIndex(e.geometry),w=a&&a.length>0,p=m.getMaterialMinMaxLODsCount(e.material),y=p.min_count!==1/0&&p.min_count>=0&&p.max_count>=0;if(!w&&!y){s.mesh_lod=0,s.texture_lod=0;return}w||(l=!0,o=0);const P=this.renderer.domElement.clientHeight||this.renderer.domElement.height;let _=e.geometry.boundingBox;if(e.type==="SkinnedMesh"){const c=e;if(!c.boundingBox)c.computeBoundingBox();else if(this.skinnedMeshAutoUpdateBoundsInterval>0){if(!c[D.$skinnedMeshBoundsOffset]){const v=D.skinnedMeshBoundsFrameOffsetCounter++;c[D.$skinnedMeshBoundsOffset]=v}const x=c[D.$skinnedMeshBoundsOffset];if((r.frames+x)%this.skinnedMeshAutoUpdateBoundsInterval===0){const v=V(c),C=c.geometry;v&&(c.geometry=v),c.computeBoundingBox(),c.geometry=C}}_=c.boundingBox}if(_){const c=t;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 g=t.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(g)){s.mesh_lod=0,s.texture_lod=0;return}}if(this._tempBox.copy(_),this._tempBox.applyMatrix4(e.matrixWorld),c.isPerspectiveCamera&&D.isInside(this._tempBox,this.projectionScreenMatrix)){s.mesh_lod=0,s.texture_lod=0;return}if(this._tempBox.applyMatrix4(this.projectionScreenMatrix),this.renderer.xr.enabled&&c.isPerspectiveCamera&&c.fov>70){const g=this._tempBox.min,d=this._tempBox.max;let f=g.x,L=g.y,S=d.x,G=d.y;const J=2,oe=1.5,Z=(g.x+d.x)*.5,ee=(g.y+d.y)*.5;f=(f-Z)*J+Z,L=(L-ee)*J+ee,S=(S-Z)*J+Z,G=(G-ee)*J+ee;const Re=f<0&&S>0?0:Math.min(Math.abs(g.x),Math.abs(d.x)),$e=L<0&&G>0?0:Math.min(Math.abs(g.y),Math.abs(d.y)),ie=Math.max(Re,$e);r.lastCentrality=(oe-ie)*(oe-ie)*(oe-ie)}else r.lastCentrality=1;const x=this._tempBox.getSize(this._tempBoxSize);x.multiplyScalar(.5),screen.availHeight>0&&P>0&&x.multiplyScalar(P/screen.availHeight),t.isPerspectiveCamera?x.x*=t.aspect:t.isOrthographicCamera;const v=t.matrixWorldInverse,C=this._tempBox2;C.copy(_),C.applyMatrix4(e.matrixWorld),C.applyMatrix4(v);const b=C.getSize(this._tempBox2Size),R=Math.max(b.x,b.y);if(Math.max(x.x,x.y)!=0&&R!=0&&(x.z=b.z/Math.max(b.x,b.y)*Math.max(x.x,x.y)),r.lastScreenCoverage=Math.max(x.x,x.y,x.z),r.lastScreenspaceVolume.copy(x),r.lastScreenCoverage*=r.lastCentrality,E&&D.debugDrawLine){const g=this.tempMatrix.copy(this.projectionScreenMatrix);g.invert();const d=D.corner0,f=D.corner1,L=D.corner2,S=D.corner3;d.copy(this._tempBox.min),f.copy(this._tempBox.max),f.x=d.x,L.copy(this._tempBox.max),L.y=d.y,S.copy(this._tempBox.max);const G=(d.z+S.z)*.5;d.z=f.z=L.z=S.z=G,d.applyMatrix4(g),f.applyMatrix4(g),L.applyMatrix4(g),S.applyMatrix4(g),D.debugDrawLine(d,f,255),D.debugDrawLine(d,L,255),D.debugDrawLine(f,S,255),D.debugDrawLine(L,S,255)}let M=999;if(a&&r.lastScreenCoverage>0)for(let g=0;g<a.length;g++){const d=a[g],f=(d.densities?.[u]||d.density||1e-5)/r.lastScreenCoverage;if(u>0&&Te()&&!d.densities&&!globalThis["NEEDLE:MISSING_LOD_PRIMITIVE_DENSITIES"]&&(window["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.")),f<n){M=g;break}}M<o&&(o=M,l=!0)}if(l?s.mesh_lod=o:s.mesh_lod=r.lastLodLevel_Mesh,E&&s.mesh_lod!=r.lastLodLevel_Mesh){const c=a?.[s.mesh_lod];c&&console.log(`Mesh LOD changed: ${r.lastLodLevel_Mesh} \u2192 ${s.mesh_lod} (density: ${c.densities?.[u].toFixed(0)}) | ${e.name}`)}if(y){const c="saveData"in globalThis.navigator&&globalThis.navigator.saveData===!0;if(r.lastLodLevel_Texture<0){if(s.texture_lod=p.max_count-1,E){const x=p.lods[p.max_count-1];E&&console.log(`First Texture LOD ${s.texture_lod} (${x.max_height}px) - ${e.name}`)}}else{const x=r.lastScreenspaceVolume.x+r.lastScreenspaceVolume.y+r.lastScreenspaceVolume.z;let v=r.lastScreenCoverage*4;this.context?.engine==="model-viewer"&&(v*=1.5);const C=P/window.devicePixelRatio*v;let b=!1;for(let R=p.lods.length-1;R>=0;R--){const M=p.lods[R];if(!(c&&M.max_height>=2048)&&!(Se()&&M.max_height>4096)&&(M.max_height>C||!b&&R===0)){if(b=!0,s.texture_lod=R,E&&s.texture_lod<r.lastLodLevel_Texture){const g=M.max_height;console.log(`Texture LOD changed: ${r.lastLodLevel_Texture} \u2192 ${s.texture_lod} = ${g}px
|
|
8
|
-
Screensize: ${C.toFixed(0)}px, Coverage: ${(100*r.lastScreenCoverage).toFixed(2)}%, Volume ${x.toFixed(1)}
|
|
9
|
-
${e.name}`)}break}}}}else s.texture_lod=0}};class mt{frames=0;lastLodLevel_Mesh=-1;lastLodLevel_Texture=-1;lastScreenCoverage=0;lastScreenspaceVolume=new A;lastCentrality=0}const Ce=Symbol("NEEDLE_mesh_lod"),ne=Symbol("NEEDLE_texture_lod");let ye=null;function Ae(){const i=pt();i&&(i.mapURLs(function(t){return ke(),t}),ke(),ye?.disconnect(),ye=new MutationObserver(t=>{t.forEach(e=>{e.addedNodes.forEach(r=>{r instanceof HTMLElement&&r.tagName.toLowerCase()==="model-viewer"&&Ee(r)})})}),ye.observe(document,{childList:!0,subtree:!0}))}function pt(){return typeof customElements>"u"?null:customElements.get("model-viewer")||(customElements.whenDefined("model-viewer").then(()=>{console.debug("[gltf-progressive] model-viewer defined"),Ae()}),null)}function ke(){typeof document>"u"||document.querySelectorAll("model-viewer").forEach(i=>{Ee(i)})}const Ie=new WeakSet;let xt=0;function Ee(i){if(!i||Ie.has(i))return null;Ie.add(i),console.debug("[gltf-progressive] found new model-viewer..."+ ++xt+`
|
|
10
|
-
`,i.getAttribute("src"));let t=null,e=null,r=null;for(let n=i;n!=null;n=Object.getPrototypeOf(n)){const s=Object.getOwnPropertySymbols(n),o=s.find(u=>u.toString()=="Symbol(renderer)"),l=s.find(u=>u.toString()=="Symbol(scene)"),a=s.find(u=>u.toString()=="Symbol(needsRender)");!t&&o!=null&&(t=i[o].threeRenderer),!e&&l!=null&&(e=i[l]),!r&&a!=null&&(r=i[a])}if(t&&e){let n=function(){if(r){let o=0,l=setInterval(()=>{if(o++>5){clearInterval(l);return}r?.call(i)},300)}};console.debug("[gltf-progressive] setup model-viewer");const s=Q.get(t,{engine:"model-viewer"});return Q.addPlugin(new wt),s.enable(),s.addEventListener("changed",()=>{r?.call(i)}),i.addEventListener("model-visibility",o=>{o.detail.visible&&r?.call(i)}),i.addEventListener("load",()=>{n()}),()=>{s.disable()}}return null}class wt{_didWarnAboutMissingUrl=!1;onBeforeUpdateLOD(t,e,r,n){this.tryParseMeshLOD(e,n),this.tryParseTextureLOD(e,n)}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[ne]==!0)return;e[ne]=!0;const r=this.tryGetCurrentGLTF(t),n=this.tryGetCurrentModelViewer(t),s=this.getUrl(n);if(s&&r&&e.material){let o=function(a){if(a[ne]==!0)return;a[ne]=!0,a.userData&&(a.userData.LOD=-1);const u=Object.keys(a);for(let w=0;w<u.length;w++){const p=u[w],y=a[p];if(y?.isTexture===!0){const P=y.userData?.associations?.textures;if(P==null)continue;const _=r.parser.json.textures[P];if(!_){console.warn("Texture data not found for texture index "+P);continue}if(_?.extensions?.[B]){const c=_.extensions[B];c&&s&&m.registerTexture(s,y,c.lods.length,P,c)}}}};const l=e.material;if(Array.isArray(l))for(const a of l)o(a);else o(l)}}tryParseMeshLOD(t,e){if(e[Ce]==!0)return;e[Ce]=!0;const r=this.tryGetCurrentModelViewer(t),n=this.getUrl(r);if(!n)return;const s=e.userData?.gltfExtensions?.[B];if(s&&n){const o=e.uuid;m.registerMesh(n,o,e,0,s.lods.length,s)}}}function yt(...i){let t,e,r,n;switch(i.length){case 2:[r,e]=i,n={};break;case 3:[r,e,n]=i;break;case 4:[t,e,r,n]=i;break;default:throw new Error("Invalid arguments")}te(e),ue(r),he(r,{progressive:!0,...n?.hints}),r.register(o=>new m(o));const s=Q.get(e);return n?.enableLODsManager!==!1&&s.enable(),s}if(Ae(),!nt){const i={gltfProgressive:{useNeedleProgressive:yt,LODsManager:Q,configureLoader:he,getRaycastMesh:V,useRaycastMeshes:it}};if(!globalThis.Needle)globalThis.Needle=i;else for(const t in i)globalThis.Needle[t]=i[t]}export{Q as LODsManager,m as NEEDLE_progressive,ue as addDracoAndKTX2Loaders,he as configureLoader,te as createLoaders,V as getRaycastMesh,Me as setDracoDecoderLocation,De as setKTX2TranscoderLocation};
|