@needle-tools/gltf-progressive 3.1.1-next.fd37564 → 3.2.0-next.613b13f
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 +4 -0
- package/README.md +3 -3
- package/examples/react-three-fiber/src/App.tsx +1 -1
- package/examples/threejs/main.js +1 -1
- package/gltf-progressive.js +47 -32
- package/gltf-progressive.min.js +5 -5
- package/gltf-progressive.umd.cjs +7 -7
- package/lib/extension.d.ts +1 -1
- package/lib/extension.js +2 -1
- package/lib/index.d.ts +3 -17
- package/lib/index.js +29 -3
- package/lib/version.js +1 -1
- package/lib/worker/loader.worker.js +5 -8
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,10 @@ All notable changes to this package will be documented in this file.
|
|
|
4
4
|
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
|
5
5
|
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [3.2.0] - 2025-08-11
|
|
8
|
+
- Change: `useNeedleProgressive` hook parameters. It now only requires the GLTFLoader and WebGLRenderer: `useNeedleProgressive(<loader>, <renderer>)` instead of `useNeedleProgressive(<url>, <renderer>, <loader>)`. This simplify usage when using the same GLTFLoader for multiple different model load operations.
|
|
9
|
+
- Internal: Start of web worker for texture and mesh LOD loading. Currently this can only be enabled by appending the `gltf-progressive-worker` query parameter to the URL.
|
|
10
|
+
|
|
7
11
|
## [3.1.1] - 2025-08-07
|
|
8
12
|
- Fix: Issue where compressed texture did loose LOD information ([issue](https://linear.app/needle/issue/NE-6669))
|
|
9
13
|
- Chore: Warn once if multi-mesh primitive is detected but compressed asset doesnt have densities per primitive, meaning an older compression pipeline version was used in processing this asset that would require an update for correct LOD updates
|
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ import { useNeedleProgressive } from "@needle-tools/gltf-progressive";
|
|
|
10
10
|
|
|
11
11
|
// Before loading with GLTFLoader
|
|
12
12
|
// call 'useNeedleProgressive' once to register the loader plugin
|
|
13
|
-
useNeedleProgressive(
|
|
13
|
+
useNeedleProgressive(gltf_loader, webgl_renderer)
|
|
14
14
|
```
|
|
15
15
|
|
|
16
16
|
## Features
|
|
@@ -60,7 +60,7 @@ const gltfLoader = new GLTFLoader();
|
|
|
60
60
|
const url = "https://cloud.needle.tools/-/assets/Z23hmXBZN45qJ-ZN45qJ-world/file";
|
|
61
61
|
|
|
62
62
|
// register the progressive loader plugin
|
|
63
|
-
useNeedleProgressive(
|
|
63
|
+
useNeedleProgressive(gltfLoader, renderer)
|
|
64
64
|
|
|
65
65
|
// just call the load method as usual
|
|
66
66
|
gltfLoader.load(url, gltf => {
|
|
@@ -94,7 +94,7 @@ function MyModel() {
|
|
|
94
94
|
const { gl } = useThree()
|
|
95
95
|
const url = 'https://cloud.needle.tools/-/assets/Z23hmXBZN45qJ-ZN45qJ-world/file'
|
|
96
96
|
const { scene } = useGLTF(url, false, false, (loader) => {
|
|
97
|
-
useNeedleProgressive(
|
|
97
|
+
useNeedleProgressive(loader as any, gl as any)
|
|
98
98
|
})
|
|
99
99
|
return <primitive object={scene} />
|
|
100
100
|
}
|
|
@@ -11,7 +11,7 @@ function MyModel() {
|
|
|
11
11
|
const { gl } = useThree()
|
|
12
12
|
const url = 'https://engine.needle.tools/demos/gltf-progressive/assets/church/model.glb'
|
|
13
13
|
const { scene } = useGLTF(url, false, false, (loader) => {
|
|
14
|
-
useNeedleProgressive(
|
|
14
|
+
useNeedleProgressive(loader as any, gl as any);
|
|
15
15
|
})
|
|
16
16
|
return <primitive object={scene} />
|
|
17
17
|
}
|
package/examples/threejs/main.js
CHANGED
|
@@ -89,7 +89,7 @@ function loadScene() {
|
|
|
89
89
|
// Create a new GLTFLoader instance
|
|
90
90
|
const gltfLoader = new GLTFLoader();
|
|
91
91
|
/** Call this method to register the progressive loader */
|
|
92
|
-
useNeedleProgressive(
|
|
92
|
+
useNeedleProgressive(gltfLoader, renderer)
|
|
93
93
|
|
|
94
94
|
// just call the load method as usual
|
|
95
95
|
gltfLoader.load(url, gltf => {
|
package/gltf-progressive.js
CHANGED
|
@@ -6,8 +6,8 @@ import { KTX2Loader as Xe } from "three/examples/jsm/loaders/KTX2Loader.js";
|
|
|
6
6
|
const Ke = "";
|
|
7
7
|
globalThis.GLTF_PROGRESSIVE_VERSION = Ke;
|
|
8
8
|
console.debug("[gltf-progressive] version -");
|
|
9
|
-
let
|
|
10
|
-
const je =
|
|
9
|
+
let C = "https://www.gstatic.com/draco/versioned/decoders/1.5.7/", V = "https://www.gstatic.com/basis-universal/versioned/2021-04-15-ba1c3e4/";
|
|
10
|
+
const je = C, Ye = V, Oe = new URL(C + "draco_decoder.js");
|
|
11
11
|
Oe.searchParams.append("range", "true");
|
|
12
12
|
fetch(Oe, {
|
|
13
13
|
method: "GET",
|
|
@@ -15,16 +15,16 @@ fetch(Oe, {
|
|
|
15
15
|
Range: "bytes=0-1"
|
|
16
16
|
}
|
|
17
17
|
}).catch((i) => {
|
|
18
|
-
console.debug(`Failed to fetch remote Draco decoder from ${
|
|
18
|
+
console.debug(`Failed to fetch remote Draco decoder from ${C} (offline: ${typeof navigator < "u" ? navigator.onLine : "unknown"})`), C === je && Qe("./include/draco/"), V === Ye && Je("./include/ktx2/");
|
|
19
19
|
}).finally(() => {
|
|
20
20
|
Pe();
|
|
21
21
|
});
|
|
22
22
|
const He = () => ({
|
|
23
|
-
dracoDecoderPath:
|
|
23
|
+
dracoDecoderPath: C,
|
|
24
24
|
ktx2TranscoderPath: V
|
|
25
25
|
});
|
|
26
26
|
function Qe(i) {
|
|
27
|
-
|
|
27
|
+
C = i, T && T[ge] != C ? (console.debug("Updating Draco decoder path to " + i), T[ge] = C, T.setDecoderPath(C), T.preload()) : console.debug("Setting Draco decoder path to " + i);
|
|
28
28
|
}
|
|
29
29
|
function Je(i) {
|
|
30
30
|
V = i, R && R.transcoderPath != V ? (console.debug("Updating KTX2 transcoder path to " + i), R.setTranscoderPath(V), R.init()) : console.debug("Setting KTX2 transcoder path to " + i);
|
|
@@ -38,7 +38,7 @@ function Se(i) {
|
|
|
38
38
|
const ge = Symbol("dracoDecoderPath");
|
|
39
39
|
let T, ne, R;
|
|
40
40
|
function Pe() {
|
|
41
|
-
T || (T = new Ve(), T[ge] =
|
|
41
|
+
T || (T = new Ve(), T[ge] = C, T.setDecoderPath(C), T.setDecoderConfig({ type: "js" }), T.preload()), R || (R = new Xe(), R.setTranscoderPath(V), R.init()), ne || (ne = qe);
|
|
42
42
|
}
|
|
43
43
|
const pe = /* @__PURE__ */ new WeakMap();
|
|
44
44
|
function Te(i, t) {
|
|
@@ -202,7 +202,7 @@ class we {
|
|
|
202
202
|
static async createWorker(t) {
|
|
203
203
|
const e = new Worker(new URL(
|
|
204
204
|
/* @vite-ignore */
|
|
205
|
-
"/assets/loader.worker-
|
|
205
|
+
"/assets/loader.worker-CwaLRBn2.js",
|
|
206
206
|
import.meta.url
|
|
207
207
|
), {
|
|
208
208
|
type: "module"
|
|
@@ -516,7 +516,8 @@ class y {
|
|
|
516
516
|
}
|
|
517
517
|
parser;
|
|
518
518
|
url;
|
|
519
|
-
constructor(t
|
|
519
|
+
constructor(t) {
|
|
520
|
+
const e = t.options.path;
|
|
520
521
|
g && console.log("Progressive extension registered for", e), this.parser = t, this.url = e;
|
|
521
522
|
}
|
|
522
523
|
_isLoadingMesh;
|
|
@@ -835,7 +836,7 @@ class ue {
|
|
|
835
836
|
});
|
|
836
837
|
}
|
|
837
838
|
}
|
|
838
|
-
const
|
|
839
|
+
const k = X("debugprogressive"), gt = X("noprogressive"), de = Symbol("Needle:LODSManager"), ce = Symbol("Needle:LODState"), U = Symbol("Needle:CurrentLOD"), P = { mesh_lod: -1, texture_lod: -1 };
|
|
839
840
|
class L {
|
|
840
841
|
/**
|
|
841
842
|
* Assign a function to draw debug lines for the LODs. This function will be called with the start and end position of the line and the color of the line when the `debugprogressive` query parameter is set.
|
|
@@ -959,7 +960,7 @@ class L {
|
|
|
959
960
|
const e = this;
|
|
960
961
|
xe(this.renderer), this.renderer.render = function(s, n) {
|
|
961
962
|
const r = e.renderer.getRenderTarget();
|
|
962
|
-
(r == null || "isXRRenderTarget" in r && r.isXRRenderTarget) && (t = 0, e.#r += 1, e.#n = e.#o.getDelta(), e.#i += e.#n, e._fpsBuffer.shift(), e._fpsBuffer.push(1 / e.#n), e.#s = e._fpsBuffer.reduce((l, a) => l + a) / e._fpsBuffer.length,
|
|
963
|
+
(r == null || "isXRRenderTarget" in r && r.isXRRenderTarget) && (t = 0, e.#r += 1, e.#n = e.#o.getDelta(), e.#i += e.#n, e._fpsBuffer.shift(), e._fpsBuffer.push(1 / e.#n), e.#s = e._fpsBuffer.reduce((l, a) => l + a) / e._fpsBuffer.length, k && e.#r % 200 === 0 && console.log("FPS", Math.round(e.#s), "Interval:", e.#e));
|
|
963
964
|
const o = t++;
|
|
964
965
|
e.#t.call(this, s, n), e.onAfterRender(s, n, o);
|
|
965
966
|
};
|
|
@@ -979,7 +980,7 @@ class L {
|
|
|
979
980
|
(l.name === "EffectMaterial" || l.name === "CopyShader") && (o = !1);
|
|
980
981
|
}
|
|
981
982
|
if ((e.parent && e.parent.type === "CubeCamera" || s >= 1 && e.type === "OrthographicCamera") && (o = !1), o) {
|
|
982
|
-
if (gt || (this.updateInterval === "auto" ? this.#s < 40 && this.#e < 10 ? (this.#e += 1,
|
|
983
|
+
if (gt || (this.updateInterval === "auto" ? this.#s < 40 && this.#e < 10 ? (this.#e += 1, k && console.warn("↓ Reducing LOD updates", this.#e, this.#s.toFixed(0))) : this.#s >= 60 && this.#e > 1 && (this.#e -= 1, k && console.warn("↑ Increasing LOD updates", this.#e, this.#s.toFixed(0))) : this.#e = this.updateInterval, this.#e > 0 && this.#r % this.#e != 0))
|
|
983
984
|
return;
|
|
984
985
|
this.internalUpdate(t, e), this._postprocessPromiseGroups();
|
|
985
986
|
}
|
|
@@ -993,7 +994,7 @@ class L {
|
|
|
993
994
|
const r = this.targetTriangleDensity;
|
|
994
995
|
for (const a of n) {
|
|
995
996
|
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")) {
|
|
996
|
-
|
|
997
|
+
k && (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)));
|
|
997
998
|
continue;
|
|
998
999
|
}
|
|
999
1000
|
switch (a.material.type) {
|
|
@@ -1005,7 +1006,7 @@ class L {
|
|
|
1005
1006
|
case "MeshDepthMaterial":
|
|
1006
1007
|
continue;
|
|
1007
1008
|
}
|
|
1008
|
-
if (
|
|
1009
|
+
if (k === "color" && a.material && !a.object.progressive_debug_color) {
|
|
1009
1010
|
a.object.progressive_debug_color = !0;
|
|
1010
1011
|
const c = Math.random() * 16777215, p = new Ne({ color: c });
|
|
1011
1012
|
a.object.material = p;
|
|
@@ -1106,7 +1107,7 @@ class L {
|
|
|
1106
1107
|
return;
|
|
1107
1108
|
}
|
|
1108
1109
|
let l = 10 + 1, a = !1;
|
|
1109
|
-
if (
|
|
1110
|
+
if (k && e["DEBUG:LOD"] != null)
|
|
1110
1111
|
return e["DEBUG:LOD"];
|
|
1111
1112
|
const u = y.getMeshLODExtension(e.geometry)?.lods, c = y.getPrimitiveIndex(e.geometry), p = u && u.length > 0, _ = y.getMaterialMinMaxLODsCount(e.material), v = _.min_count !== 1 / 0 && _.min_count >= 0 && _.max_count >= 0;
|
|
1112
1113
|
if (!p && !v) {
|
|
@@ -1161,7 +1162,7 @@ class L {
|
|
|
1161
1162
|
const O = t.matrixWorldInverse, B = this._tempBox2;
|
|
1162
1163
|
B.copy(G), B.applyMatrix4(e.matrixWorld), B.applyMatrix4(O);
|
|
1163
1164
|
const b = B.getSize(this._tempBox2Size), z = Math.max(b.x, b.y);
|
|
1164
|
-
if (Math.max(x.x, x.y) != 0 && z != 0 && (x.z = b.z / Math.max(b.x, b.y) * Math.max(x.x, x.y)), s.lastScreenCoverage = Math.max(x.x, x.y, x.z), s.lastScreenspaceVolume.copy(x), s.lastScreenCoverage *= s.lastCentrality,
|
|
1165
|
+
if (Math.max(x.x, x.y) != 0 && z != 0 && (x.z = b.z / Math.max(b.x, b.y) * Math.max(x.x, x.y)), s.lastScreenCoverage = Math.max(x.x, x.y, x.z), s.lastScreenspaceVolume.copy(x), s.lastScreenCoverage *= s.lastCentrality, k && L.debugDrawLine) {
|
|
1165
1166
|
const f = this.tempMatrix.copy(this.projectionScreenMatrix);
|
|
1166
1167
|
f.invert();
|
|
1167
1168
|
const h = L.corner0, M = L.corner1, D = L.corner2, $ = L.corner3;
|
|
@@ -1180,16 +1181,16 @@ class L {
|
|
|
1180
1181
|
}
|
|
1181
1182
|
w < l && (l = w, a = !0);
|
|
1182
1183
|
}
|
|
1183
|
-
if (a ? r.mesh_lod = l : r.mesh_lod = s.lastLodLevel_Mesh,
|
|
1184
|
+
if (a ? r.mesh_lod = l : r.mesh_lod = s.lastLodLevel_Mesh, k && r.mesh_lod != s.lastLodLevel_Mesh) {
|
|
1184
1185
|
const x = u?.[r.mesh_lod];
|
|
1185
1186
|
x && console.debug(`Mesh LOD changed: ${s.lastLodLevel_Mesh} → ${r.mesh_lod} (density: ${x.densities?.[c].toFixed(0)}) | ${e.name}`);
|
|
1186
1187
|
}
|
|
1187
1188
|
if (v) {
|
|
1188
1189
|
const d = "saveData" in globalThis.navigator && globalThis.navigator.saveData === !0;
|
|
1189
1190
|
if (s.lastLodLevel_Texture < 0) {
|
|
1190
|
-
if (r.texture_lod = _.max_count - 1,
|
|
1191
|
+
if (r.texture_lod = _.max_count - 1, k) {
|
|
1191
1192
|
const x = _.lods[_.max_count - 1];
|
|
1192
|
-
|
|
1193
|
+
k && console.log(`First Texture LOD ${r.texture_lod} (${x.max_height}px) - ${e.name}`);
|
|
1193
1194
|
}
|
|
1194
1195
|
} else {
|
|
1195
1196
|
const x = s.lastScreenspaceVolume.x + s.lastScreenspaceVolume.y + s.lastScreenspaceVolume.z;
|
|
@@ -1200,7 +1201,7 @@ class L {
|
|
|
1200
1201
|
for (let S = _.lods.length - 1; S >= 0; S--) {
|
|
1201
1202
|
const w = _.lods[S];
|
|
1202
1203
|
if (!(d && w.max_height >= 2048) && !(Ae() && w.max_height > 4096) && (w.max_height > b || !z && S === 0)) {
|
|
1203
|
-
if (z = !0, r.texture_lod = S,
|
|
1204
|
+
if (z = !0, r.texture_lod = S, k && r.texture_lod < s.lastLodLevel_Texture) {
|
|
1204
1205
|
const m = w.max_height;
|
|
1205
1206
|
console.log(`Texture LOD changed: ${s.lastLodLevel_Texture} → ${r.texture_lod} = ${m}px
|
|
1206
1207
|
Screensize: ${b.toFixed(0)}px, Coverage: ${(100 * s.lastScreenCoverage).toFixed(2)}%, Volume ${x.toFixed(1)}
|
|
@@ -1224,14 +1225,14 @@ class pt {
|
|
|
1224
1225
|
}
|
|
1225
1226
|
const ve = Symbol("NEEDLE_mesh_lod"), se = Symbol("NEEDLE_texture_lod");
|
|
1226
1227
|
let fe = null;
|
|
1227
|
-
function
|
|
1228
|
+
function ke() {
|
|
1228
1229
|
const i = mt();
|
|
1229
1230
|
i && (i.mapURLs(function(t) {
|
|
1230
1231
|
return be(), t;
|
|
1231
1232
|
}), be(), fe?.disconnect(), fe = new MutationObserver((t) => {
|
|
1232
1233
|
t.forEach((e) => {
|
|
1233
1234
|
e.addedNodes.forEach((s) => {
|
|
1234
|
-
s instanceof HTMLElement && s.tagName.toLowerCase() === "model-viewer" &&
|
|
1235
|
+
s instanceof HTMLElement && s.tagName.toLowerCase() === "model-viewer" && Ce(s);
|
|
1235
1236
|
});
|
|
1236
1237
|
});
|
|
1237
1238
|
}), fe.observe(document, { childList: !0, subtree: !0 }));
|
|
@@ -1240,18 +1241,18 @@ function mt() {
|
|
|
1240
1241
|
if (typeof customElements > "u") return null;
|
|
1241
1242
|
const i = customElements.get("model-viewer");
|
|
1242
1243
|
return i || (customElements.whenDefined("model-viewer").then(() => {
|
|
1243
|
-
console.debug("[gltf-progressive] model-viewer defined"),
|
|
1244
|
+
console.debug("[gltf-progressive] model-viewer defined"), ke();
|
|
1244
1245
|
}), null);
|
|
1245
1246
|
}
|
|
1246
1247
|
function be() {
|
|
1247
1248
|
if (typeof document > "u") return;
|
|
1248
1249
|
document.querySelectorAll("model-viewer").forEach((t) => {
|
|
1249
|
-
|
|
1250
|
+
Ce(t);
|
|
1250
1251
|
});
|
|
1251
1252
|
}
|
|
1252
1253
|
const Me = /* @__PURE__ */ new WeakSet();
|
|
1253
1254
|
let yt = 0;
|
|
1254
|
-
function
|
|
1255
|
+
function Ce(i) {
|
|
1255
1256
|
if (!i || Me.has(i))
|
|
1256
1257
|
return null;
|
|
1257
1258
|
Me.add(i), console.debug("[gltf-progressive] found new model-viewer..." + ++yt + `
|
|
@@ -1348,15 +1349,29 @@ class xt {
|
|
|
1348
1349
|
}
|
|
1349
1350
|
}
|
|
1350
1351
|
}
|
|
1351
|
-
function wt(i
|
|
1352
|
-
|
|
1352
|
+
function wt(...i) {
|
|
1353
|
+
let t, e, s, n;
|
|
1354
|
+
switch (i.length) {
|
|
1355
|
+
case 2:
|
|
1356
|
+
[s, e] = i, n = {};
|
|
1357
|
+
break;
|
|
1358
|
+
case 3:
|
|
1359
|
+
[s, e, n] = i;
|
|
1360
|
+
break;
|
|
1361
|
+
case 4:
|
|
1362
|
+
[t, e, s, n] = i;
|
|
1363
|
+
break;
|
|
1364
|
+
default:
|
|
1365
|
+
throw new Error("Invalid arguments");
|
|
1366
|
+
}
|
|
1367
|
+
xe(e), Se(s), Te(s, {
|
|
1353
1368
|
progressive: !0,
|
|
1354
|
-
...
|
|
1355
|
-
}),
|
|
1356
|
-
const
|
|
1357
|
-
return
|
|
1369
|
+
...n?.hints
|
|
1370
|
+
}), s.register((o) => new y(o));
|
|
1371
|
+
const r = L.get(e);
|
|
1372
|
+
return n?.enableLODsManager !== !1 && r.enable(), r;
|
|
1358
1373
|
}
|
|
1359
|
-
|
|
1374
|
+
ke();
|
|
1360
1375
|
if (!rt) {
|
|
1361
1376
|
const i = {
|
|
1362
1377
|
gltfProgressive: {
|
|
@@ -1382,7 +1397,7 @@ export {
|
|
|
1382
1397
|
Te as configureLoader,
|
|
1383
1398
|
xe as createLoaders,
|
|
1384
1399
|
J as getRaycastMesh,
|
|
1385
|
-
|
|
1400
|
+
ke as patchModelViewer,
|
|
1386
1401
|
nt as registerRaycastMesh,
|
|
1387
1402
|
Qe as setDracoDecoderLocation,
|
|
1388
1403
|
Je as setKTX2TranscoderLocation,
|
package/gltf-progressive.min.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import{BufferGeometry as V,Mesh as N,Box3 as ne,Vector3 as I,Sphere as ve,CompressedTexture as Ne,Texture as j,Matrix3 as je,InterleavedBuffer as Fe,InterleavedBufferAttribute as Ue,BufferAttribute as We,TextureLoader as ze,Matrix4 as Le,Clock as qe,MeshStandardMaterial as Ve}from"three";import{GLTFLoader as ie}from"three/examples/jsm/loaders/GLTFLoader.js";import{MeshoptDecoder as Xe}from"three/examples/jsm/libs/meshopt_decoder.module.js";import{DRACOLoader as Ke}from"three/examples/jsm/loaders/DRACOLoader.js";import{KTX2Loader as He}from"three/examples/jsm/loaders/KTX2Loader.js";const be="";globalThis.GLTF_PROGRESSIVE_VERSION=be,console.debug("[gltf-progressive] version -");let k="https://www.gstatic.com/draco/versioned/decoders/1.5.7/",F="https://www.gstatic.com/basis-universal/versioned/2021-04-15-ba1c3e4/";const Ye=k,Qe=F,_e=new URL(k+"draco_decoder.js");_e.searchParams.append("range","true"),fetch(_e,{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===Ye&&De("./include/draco/"),F===Qe&&Me("./include/ktx2/")}).finally(()=>{Oe()});const Je=()=>({dracoDecoderPath:k,ktx2TranscoderPath:F});function De(i){k=i,P&&P[le]!=k?(console.debug("Updating Draco decoder path to "+i),P[le]=k,P.setDecoderPath(k),P.preload()):console.debug("Setting Draco decoder path to "+i)}function Me(i){F=i,C&&C.transcoderPath!=F?(console.debug("Updating KTX2 transcoder path to "+i),C.setTranscoderPath(F),C.init()):console.debug("Setting KTX2 transcoder path to "+i)}function J(i){return Oe(),i?C.detectSupport(i):i!==null&&console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures might fail"),{dracoLoader:P,ktx2Loader:C,meshoptDecoder:Z}}function ae(i){i.dracoLoader||i.setDRACOLoader(P),i.ktx2Loader||i.setKTX2Loader(C),i.meshoptDecoder||i.setMeshoptDecoder(Z)}const le=Symbol("dracoDecoderPath");let P,Z,C;function Oe(){P||(P=new Ke,P[le]=k,P.setDecoderPath(k),P.setDecoderConfig({type:"js"}),P.preload()),C||(C=new He,C.setTranscoderPath(F),C.init()),Z||(Z=Xe)}const ue=new WeakMap;function ce(i,t){let e=ue.get(i);e?e=Object.assign(e,t):e=t,ue.set(i,e)}const Ze=ie.prototype.load;function et(...i){const t=ue.get(this);let e=i[0];const r=new URL(e,window.location.href);if(r.hostname.endsWith("needle.tools")){const o=t?.progressive!==void 0?t.progressive:!0,s=t?.usecase?t.usecase:"default";o?this.requestHeader.Accept=`*/*;progressive=allowed;usecase=${s}`:this.requestHeader.Accept=`*/*;usecase=${s}`,e=r.toString()}return i[0]=e,Ze?.call(this,...i)}ie.prototype.load=et,U("debugprogressive");function U(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 tt(i,t){if(t===void 0||t.startsWith("./")||t.startsWith("http")||i===void 0)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}let ee;function Se(){return ee!==void 0||(ee=/iPhone|iPad|iPod|Android|IEMobile/i.test(navigator.userAgent),U("debugprogressive")&&console.log("[glTF Progressive]: isMobileDevice",ee)),ee}function Pe(){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 rt{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:o}=this._queue.shift();o({use:s=>this.add(r,s)})}}}const st=typeof window>"u"&&typeof document>"u",de=Symbol("needle:raycast-mesh");function z(i){return i?.[de]instanceof V?i[de]:null}function Te(i,t){if((i.type==="Mesh"||i.type==="SkinnedMesh")&&!z(i)){const e=ot(t);e.userData={isRaycastMesh:!0},i[de]=e}}function Ae(i=!0){if(i){if(X)return;const t=X=N.prototype.raycast;N.prototype.raycast=function(e,r){const o=this,s=z(o);let n;s&&o.isMesh&&(n=o.geometry,o.geometry=s),t.call(this,e,r),n&&(o.geometry=n)}}else{if(!X)return;N.prototype.raycast=X,X=null}}let X=null;function ot(i){const t=new V;for(const e in i.attributes)t.setAttribute(e,i.getAttribute(e));return t.setIndex(i.getIndex()),t}const R=new Array,h=U("debugprogressive");let te,W=-1;if(h){let i=function(){W+=1,W>=t&&(W=-1),console.log(`Toggle LOD level [${W}]`)},t=6;window.addEventListener("keyup",e=>{e.key==="p"&&i(),e.key==="w"&&(te=!te,console.log(`Toggle wireframe [${te}]`));const r=parseInt(e.key);!isNaN(r)&&r>=0&&(W=r,console.log(`Set LOD level to [${W}]`))})}function Ie(i){if(h)if(Array.isArray(i))for(const t of i)Ie(t);else i&&"wireframe"in i&&(i.wireframe=te===!0)}const K=new Array;let nt=0;const it=Se()?2:10;function at(i){if(K.length<it){const e=K.length;h&&console.warn(`[Worker] Creating new worker #${e}`);const r=ye.createWorker(i||{});return K.push(r),r}const t=nt++%K.length;return K[t]}class ye{constructor(t,e){this.worker=t,this._debug=e.debug??!1,t.onmessage=r=>{const o=r.data;switch(this._debug&&console.log("[Worker] EVENT",o),o.type){case"loaded-gltf":for(const s of this._running)if(s.url===o.result.url){lt(o.result),s.resolve(o.result);const n=s.url;n.startsWith("blob:")&&URL.revokeObjectURL(n)}}},t.onerror=r=>{console.error("[Worker] Error in gltf-progressive worker:",r)},t.postMessage({type:"init"})}static async createWorker(t){const e=new Worker(new URL("/assets/loader.worker-8olmVOL4.js",import.meta.url),{type:"module"});return new ye(e,t)}_running=[];_webglRenderer=null;async load(t,e){const r=Je();let o=e?.renderer;o||(this._webglRenderer??=(async()=>{const{WebGLRenderer:l}=await import("three");return new l})(),o=await this._webglRenderer);const s=J(o).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 n={type:"load",url:t,dracoDecoderPath:r.dracoDecoderPath,ktx2TranscoderPath:r.ktx2TranscoderPath,ktx2LoaderConfig:s};return this._debug&&console.debug("[Worker] Sending load request",n),this.worker.postMessage(n),new Promise(l=>{this._running.push({url:t.toString(),resolve:l})})}_debug=!1}function lt(i){for(const t of i.geometries){const e=t.geometry,r=new V;if(r.name=e.name||"",e.index){const o=e.index;r.setIndex(he(o))}for(const o in e.attributes){const s=e.attributes[o],n=he(s);r.setAttribute(o,n)}if(e.morphAttributes)for(const o in e.morphAttributes){const s=e.morphAttributes[o].map(n=>he(n));r.morphAttributes[o]=s}if(r.morphTargetsRelative=e.morphTargetsRelative??!1,r.boundingBox=new ne,r.boundingBox.min=new I(e.boundingBox?.min.x,e.boundingBox?.min.y,e.boundingBox?.min.z),r.boundingBox.max=new I(e.boundingBox?.max.x,e.boundingBox?.max.y,e.boundingBox?.max.z),r.boundingSphere=new ve(new I(e.boundingSphere?.center.x,e.boundingSphere?.center.y,e.boundingSphere?.center.z),e.boundingSphere?.radius),e.groups)for(const o of e.groups)r.addGroup(o.start,o.count,o.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 o=e.mipmaps,s=e.image?.width||e.source?.data?.width||-1,n=e.image?.height||e.source?.data?.height||-1;r=new Ne(o,s,n,e.format,e.type,e.mapping,e.wrapS,e.wrapT,e.magFilter,e.minFilter,e.anisotropy,e.colorSpace)}else r=new j(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 je(...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 he(i){let t=i;if("isInterleavedBufferAttribute"in i&&i.isInterleavedBufferAttribute){const e=i.data,r=e.array,o=new Fe(r,e.stride);t=new Ue(o,i.itemSize,r.byteOffset,i.normalized),t.offset=i.offset}else"isBufferAttribute"in i&&i.isBufferAttribute&&(t=new We(i.array,i.itemSize,i.normalized),t.usage=i.usage,t.gpuType=i.gpuType,t.updateRanges=i.updateRanges);return t}const ut=U("gltf-progressive-worker"),ct=U("gltf-progressive-reduce-mipmaps"),ge=Symbol("needle-progressive-texture"),$="NEEDLE_progressive";class g{get name(){return $}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,o="LODS:minmax",s=t[o];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[o]=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&&n(u,e)}}else if(t.isMaterial)for(const l of Object.keys(t)){const a=t[l];a?.isTexture===!0&&n(a,e)}else h&&console.warn(`[getMaterialMinMaxLODsCount] Unsupported material type: ${t.type}`);return t[o]=e,e;function n(l,a){const u=r.getAssignedLODInformation(l);if(u){const f=r.lodInfos.get(u.key);if(f&&f.lods){a.min_count=Math.min(a.min_count,f.lods.length),a.max_count=Math.max(a.max_count,f.lods.length);for(let w=0;w<f.lods.length;w++){const L=f.lods[w];L.width&&(a.lods[w]=a.lods[w]||{min_height:1/0,max_height:0},a.lods[w].min_height=Math.min(a.lods[w].min_height,L.height),a.lods[w].max_height=Math.max(a.lods[w].max_height,L.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 n=t[s];if(n&&n.isTexture&&this.hasLODLevelAvailable(n,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,o;if(t.isMesh?r=t.geometry:(t.isBufferGeometry||t.isTexture)&&(r=t),r&&r?.userData?.LODS){const s=r.userData.LODS;if(o=this.lodInfos.get(s.key),e===void 0)return o!=null;if(o)return Array.isArray(o.lods)?e<o.lods.length:e===0}return!1}static assignMeshLOD(t,e){if(!t)return Promise.resolve(null);if(t instanceof N||t.isMesh===!0){const r=t.geometry,o=this.getAssignedLODInformation(r);if(!o)return Promise.resolve(null);for(const s of R)s.onBeforeGetLODMesh?.(t,e);return t["LOD:requested level"]=e,g.getOrLoadLOD(r,e).then(s=>{if(Array.isArray(s)){const n=o.index||0;s=s[n]}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 o=new Array;for(const s of r.material){const n=this.assignTextureLOD(s,e);o.push(n)}return Promise.all(o).then(s=>{const n=new Array;for(const l of s)Array.isArray(l)&&n.push(...l);return n})}else return this.assignTextureLOD(r.material,e)}if(t.isMaterial===!0){const r=t,o=[],s=new Array;if(r.uniforms&&(r.isRawShaderMaterial||r.isShaderMaterial===!0)){const n=r;for(const l of Object.keys(n.uniforms)){const a=n.uniforms[l].value;if(a?.isTexture===!0){const u=this.assignTextureLODForSlot(a,e,r,l).then(f=>(f&&n.uniforms[l].value!=f&&(n.uniforms[l].value=f,n.uniformsNeedUpdate=!0),f));o.push(u),s.push(l)}}}else for(const n of Object.keys(r)){const l=r[n];if(l?.isTexture===!0){const a=this.assignTextureLODForSlot(l,e,r,n);o.push(a),s.push(n)}}return Promise.all(o).then(n=>{const l=new Array;for(let a=0;a<n.length;a++){const u=n[a],f=s[a];u&&u.isTexture===!0?l.push({material:r,slot:f,texture:u,level:e}):l.push({material:r,slot:f,texture:null,level:e})}return l})}if(t instanceof j||t.isTexture===!0){const r=t;return this.assignTextureLODForSlot(r,e,null,null)}return Promise.resolve(null)}static assignTextureLODForSlot(t,e,r,o){return t?.isTexture!==!0?Promise.resolve(null):o==="glyphMap"?Promise.resolve(t):g.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&&o){const n=r[o];if(n&&!h){const l=this.getAssignedLODInformation(n);if(l&&l?.level<e)return h==="verbose"&&console.warn("Assigned texture level is already higher: ",l.level,e,r,n,s),null}if(ct&&s.mipmaps){const l=s.mipmaps.length;s.mipmaps.length=Math.min(s.mipmaps.length,3),l!==s.mipmaps.length&&h&&console.debug(`Reduced mipmap count from ${l} to ${s.mipmaps.length} for ${s.uuid}: ${s.image?.width}x${s.image?.height}.`)}r[o]=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,e){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?.[$];return e?(this._isLoadingMesh=!0,this.parser.getDependency("mesh",t).then(r=>(this._isLoadingMesh=!1,r&&g.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 o=e?.extensions[$];if(o){if(!o.lods){h&&console.warn("Texture has no LODs",o);return}let s=!1;for(const n of this.parser.associations.keys())n.isTexture===!0&&this.parser.associations.get(n)?.textures===r&&(s=!0,g.registerTexture(this.url,n,o.lods?.length,r,o));s||this.parser.getDependency("texture",r).then(n=>{n&&g.registerTexture(this.url,n,o.lods?.length,r,o)})}}}),this.parser.json.meshes?.forEach((e,r)=>{if(e?.extensions){const o=e?.extensions[$];if(o&&o.lods){for(const s of this.parser.associations.keys())if(s.isMesh){const n=this.parser.associations.get(s);n?.meshes===r&&g.registerMesh(this.url,o.guid,s,o.lods.length,n.primitives,o)}}}}),null}static registerTexture=(t,e,r,o,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(`> Progressive: register texture[${o}] "${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[ge]=s);const n=s.guid;g.assignLODInformation(t,e,n,r,o),g.lodInfos.set(n,s),g.lowresCache.set(n,e)};static registerMesh=(t,e,r,o,s,n)=>{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},n,r),g.assignLODInformation(t,l,e,o,s),g.lodInfos.set(e,n);let a=g.lowresCache.get(e);a?a.push(r.geometry):a=[r.geometry],g.lowresCache.set(e,a),o>0&&!z(r)&&Te(r,l);for(const u of R)u.onRegisteredNewMesh?.(r,n)};static lodInfos=new Map;static previouslyLoaded=new Map;static lowresCache=new Map;static workers=[];static _workersIndex=0;static async getOrLoadLOD(t,e){const r=h=="verbose",o=this.getAssignedLODInformation(t);if(!o)return h&&console.warn(`[gltf-progressive] No LOD information found: ${t.name}, uuid: ${t.uuid}, type: ${t.type}`,t),null;const s=o?.key;let n;if(t.isTexture===!0){const l=t;l.source&&l.source[ge]&&(n=l.source[ge])}if(n||(n=g.lodInfos.get(s)),n){if(e>0){let u=!1;const f=Array.isArray(n.lods);if(f&&e>=n.lods.length?u=!0:f||(u=!0),u)return this.lowresCache.get(s)}const l=Array.isArray(n.lods)?n.lods[e]?.path:n.lods;if(!l)return h&&!n["missing:uri"]&&(n["missing:uri"]=!0,console.warn("Missing uri for progressive asset for LOD "+e,n)),null;const a=tt(o.url,l);if(a.endsWith(".glb")||a.endsWith(".gltf")){if(!n.guid)return console.warn("missing pointer for glb/gltf texture",n),null;const u=a+"_"+n.guid,f=await this.queue.slot(a),w=this.previouslyLoaded.get(u);if(w!==void 0){r&&console.log(`LOD ${e} was already loading/loaded: ${u}`);let p=await w.catch(y=>(console.error(`Error loading LOD ${e} from ${a}
|
|
2
|
-
`,y),null)),m=!1;if(p==null||(p instanceof j&&t instanceof j?p.image?.data||p.source?.data?p=this.copySettings(t,p):(m=!0,this.previouslyLoaded.delete(u)):p instanceof V&&t instanceof V&&(p.attributes.position?.array||(m=!0,this.previouslyLoaded.delete(u)))),!m)return p}if(!f.use)return h&&console.log(`LOD ${e} was aborted: ${a}`),null;const
|
|
3
|
-
`,x),p(null)));if(!_)return p(null);const E=_.parser;r&&console.log("Loading finished "+a,
|
|
1
|
+
import{BufferGeometry as V,Mesh as N,Box3 as ne,Vector3 as I,Sphere as ve,CompressedTexture as Ne,Texture as j,Matrix3 as je,InterleavedBuffer as Fe,InterleavedBufferAttribute as Ue,BufferAttribute as We,TextureLoader as ze,Matrix4 as be,Clock as qe,MeshStandardMaterial as Ve}from"three";import{GLTFLoader as ie}from"three/examples/jsm/loaders/GLTFLoader.js";import{MeshoptDecoder as Xe}from"three/examples/jsm/libs/meshopt_decoder.module.js";import{DRACOLoader as Ke}from"three/examples/jsm/loaders/DRACOLoader.js";import{KTX2Loader as He}from"three/examples/jsm/loaders/KTX2Loader.js";const Le="";globalThis.GLTF_PROGRESSIVE_VERSION=Le,console.debug("[gltf-progressive] version -");let k="https://www.gstatic.com/draco/versioned/decoders/1.5.7/",F="https://www.gstatic.com/basis-universal/versioned/2021-04-15-ba1c3e4/";const Ye=k,Qe=F,_e=new URL(k+"draco_decoder.js");_e.searchParams.append("range","true"),fetch(_e,{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===Ye&&De("./include/draco/"),F===Qe&&Me("./include/ktx2/")}).finally(()=>{Oe()});const Je=()=>({dracoDecoderPath:k,ktx2TranscoderPath:F});function De(i){k=i,P&&P[le]!=k?(console.debug("Updating Draco decoder path to "+i),P[le]=k,P.setDecoderPath(k),P.preload()):console.debug("Setting Draco decoder path to "+i)}function Me(i){F=i,C&&C.transcoderPath!=F?(console.debug("Updating KTX2 transcoder path to "+i),C.setTranscoderPath(F),C.init()):console.debug("Setting KTX2 transcoder path to "+i)}function J(i){return Oe(),i?C.detectSupport(i):i!==null&&console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures might fail"),{dracoLoader:P,ktx2Loader:C,meshoptDecoder:Z}}function ae(i){i.dracoLoader||i.setDRACOLoader(P),i.ktx2Loader||i.setKTX2Loader(C),i.meshoptDecoder||i.setMeshoptDecoder(Z)}const le=Symbol("dracoDecoderPath");let P,Z,C;function Oe(){P||(P=new Ke,P[le]=k,P.setDecoderPath(k),P.setDecoderConfig({type:"js"}),P.preload()),C||(C=new He,C.setTranscoderPath(F),C.init()),Z||(Z=Xe)}const ue=new WeakMap;function ce(i,t){let e=ue.get(i);e?e=Object.assign(e,t):e=t,ue.set(i,e)}const Ze=ie.prototype.load;function et(...i){const t=ue.get(this);let e=i[0];const r=new URL(e,window.location.href);if(r.hostname.endsWith("needle.tools")){const o=t?.progressive!==void 0?t.progressive:!0,s=t?.usecase?t.usecase:"default";o?this.requestHeader.Accept=`*/*;progressive=allowed;usecase=${s}`:this.requestHeader.Accept=`*/*;usecase=${s}`,e=r.toString()}return i[0]=e,Ze?.call(this,...i)}ie.prototype.load=et,U("debugprogressive");function U(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 tt(i,t){if(t===void 0||t.startsWith("./")||t.startsWith("http")||i===void 0)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}let ee;function Se(){return ee!==void 0||(ee=/iPhone|iPad|iPod|Android|IEMobile/i.test(navigator.userAgent),U("debugprogressive")&&console.log("[glTF Progressive]: isMobileDevice",ee)),ee}function Pe(){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 rt{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:o}=this._queue.shift();o({use:s=>this.add(r,s)})}}}const st=typeof window>"u"&&typeof document>"u",de=Symbol("needle:raycast-mesh");function z(i){return i?.[de]instanceof V?i[de]:null}function Te(i,t){if((i.type==="Mesh"||i.type==="SkinnedMesh")&&!z(i)){const e=ot(t);e.userData={isRaycastMesh:!0},i[de]=e}}function Ae(i=!0){if(i){if(X)return;const t=X=N.prototype.raycast;N.prototype.raycast=function(e,r){const o=this,s=z(o);let n;s&&o.isMesh&&(n=o.geometry,o.geometry=s),t.call(this,e,r),n&&(o.geometry=n)}}else{if(!X)return;N.prototype.raycast=X,X=null}}let X=null;function ot(i){const t=new V;for(const e in i.attributes)t.setAttribute(e,i.getAttribute(e));return t.setIndex(i.getIndex()),t}const R=new Array,h=U("debugprogressive");let te,W=-1;if(h){let i=function(){W+=1,W>=t&&(W=-1),console.log(`Toggle LOD level [${W}]`)},t=6;window.addEventListener("keyup",e=>{e.key==="p"&&i(),e.key==="w"&&(te=!te,console.log(`Toggle wireframe [${te}]`));const r=parseInt(e.key);!isNaN(r)&&r>=0&&(W=r,console.log(`Set LOD level to [${W}]`))})}function Ie(i){if(h)if(Array.isArray(i))for(const t of i)Ie(t);else i&&"wireframe"in i&&(i.wireframe=te===!0)}const K=new Array;let nt=0;const it=Se()?2:10;function at(i){if(K.length<it){const e=K.length;h&&console.warn(`[Worker] Creating new worker #${e}`);const r=ye.createWorker(i||{});return K.push(r),r}const t=nt++%K.length;return K[t]}class ye{constructor(t,e){this.worker=t,this._debug=e.debug??!1,t.onmessage=r=>{const o=r.data;switch(this._debug&&console.log("[Worker] EVENT",o),o.type){case"loaded-gltf":for(const s of this._running)if(s.url===o.result.url){lt(o.result),s.resolve(o.result);const n=s.url;n.startsWith("blob:")&&URL.revokeObjectURL(n)}}},t.onerror=r=>{console.error("[Worker] Error in gltf-progressive worker:",r)},t.postMessage({type:"init"})}static async createWorker(t){const e=new Worker(new URL("/assets/loader.worker-CwaLRBn2.js",import.meta.url),{type:"module"});return new ye(e,t)}_running=[];_webglRenderer=null;async load(t,e){const r=Je();let o=e?.renderer;o||(this._webglRenderer??=(async()=>{const{WebGLRenderer:l}=await import("three");return new l})(),o=await this._webglRenderer);const s=J(o).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 n={type:"load",url:t,dracoDecoderPath:r.dracoDecoderPath,ktx2TranscoderPath:r.ktx2TranscoderPath,ktx2LoaderConfig:s};return this._debug&&console.debug("[Worker] Sending load request",n),this.worker.postMessage(n),new Promise(l=>{this._running.push({url:t.toString(),resolve:l})})}_debug=!1}function lt(i){for(const t of i.geometries){const e=t.geometry,r=new V;if(r.name=e.name||"",e.index){const o=e.index;r.setIndex(he(o))}for(const o in e.attributes){const s=e.attributes[o],n=he(s);r.setAttribute(o,n)}if(e.morphAttributes)for(const o in e.morphAttributes){const s=e.morphAttributes[o].map(n=>he(n));r.morphAttributes[o]=s}if(r.morphTargetsRelative=e.morphTargetsRelative??!1,r.boundingBox=new ne,r.boundingBox.min=new I(e.boundingBox?.min.x,e.boundingBox?.min.y,e.boundingBox?.min.z),r.boundingBox.max=new I(e.boundingBox?.max.x,e.boundingBox?.max.y,e.boundingBox?.max.z),r.boundingSphere=new ve(new I(e.boundingSphere?.center.x,e.boundingSphere?.center.y,e.boundingSphere?.center.z),e.boundingSphere?.radius),e.groups)for(const o of e.groups)r.addGroup(o.start,o.count,o.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 o=e.mipmaps,s=e.image?.width||e.source?.data?.width||-1,n=e.image?.height||e.source?.data?.height||-1;r=new Ne(o,s,n,e.format,e.type,e.mapping,e.wrapS,e.wrapT,e.magFilter,e.minFilter,e.anisotropy,e.colorSpace)}else r=new j(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 je(...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 he(i){let t=i;if("isInterleavedBufferAttribute"in i&&i.isInterleavedBufferAttribute){const e=i.data,r=e.array,o=new Fe(r,e.stride);t=new Ue(o,i.itemSize,r.byteOffset,i.normalized),t.offset=i.offset}else"isBufferAttribute"in i&&i.isBufferAttribute&&(t=new We(i.array,i.itemSize,i.normalized),t.usage=i.usage,t.gpuType=i.gpuType,t.updateRanges=i.updateRanges);return t}const ut=U("gltf-progressive-worker"),ct=U("gltf-progressive-reduce-mipmaps"),ge=Symbol("needle-progressive-texture"),$="NEEDLE_progressive";class g{get name(){return $}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,o="LODS:minmax",s=t[o];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[o]=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&&n(u,e)}}else if(t.isMaterial)for(const l of Object.keys(t)){const a=t[l];a?.isTexture===!0&&n(a,e)}else h&&console.warn(`[getMaterialMinMaxLODsCount] Unsupported material type: ${t.type}`);return t[o]=e,e;function n(l,a){const u=r.getAssignedLODInformation(l);if(u){const f=r.lodInfos.get(u.key);if(f&&f.lods){a.min_count=Math.min(a.min_count,f.lods.length),a.max_count=Math.max(a.max_count,f.lods.length);for(let w=0;w<f.lods.length;w++){const b=f.lods[w];b.width&&(a.lods[w]=a.lods[w]||{min_height:1/0,max_height:0},a.lods[w].min_height=Math.min(a.lods[w].min_height,b.height),a.lods[w].max_height=Math.max(a.lods[w].max_height,b.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 n=t[s];if(n&&n.isTexture&&this.hasLODLevelAvailable(n,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,o;if(t.isMesh?r=t.geometry:(t.isBufferGeometry||t.isTexture)&&(r=t),r&&r?.userData?.LODS){const s=r.userData.LODS;if(o=this.lodInfos.get(s.key),e===void 0)return o!=null;if(o)return Array.isArray(o.lods)?e<o.lods.length:e===0}return!1}static assignMeshLOD(t,e){if(!t)return Promise.resolve(null);if(t instanceof N||t.isMesh===!0){const r=t.geometry,o=this.getAssignedLODInformation(r);if(!o)return Promise.resolve(null);for(const s of R)s.onBeforeGetLODMesh?.(t,e);return t["LOD:requested level"]=e,g.getOrLoadLOD(r,e).then(s=>{if(Array.isArray(s)){const n=o.index||0;s=s[n]}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 o=new Array;for(const s of r.material){const n=this.assignTextureLOD(s,e);o.push(n)}return Promise.all(o).then(s=>{const n=new Array;for(const l of s)Array.isArray(l)&&n.push(...l);return n})}else return this.assignTextureLOD(r.material,e)}if(t.isMaterial===!0){const r=t,o=[],s=new Array;if(r.uniforms&&(r.isRawShaderMaterial||r.isShaderMaterial===!0)){const n=r;for(const l of Object.keys(n.uniforms)){const a=n.uniforms[l].value;if(a?.isTexture===!0){const u=this.assignTextureLODForSlot(a,e,r,l).then(f=>(f&&n.uniforms[l].value!=f&&(n.uniforms[l].value=f,n.uniformsNeedUpdate=!0),f));o.push(u),s.push(l)}}}else for(const n of Object.keys(r)){const l=r[n];if(l?.isTexture===!0){const a=this.assignTextureLODForSlot(l,e,r,n);o.push(a),s.push(n)}}return Promise.all(o).then(n=>{const l=new Array;for(let a=0;a<n.length;a++){const u=n[a],f=s[a];u&&u.isTexture===!0?l.push({material:r,slot:f,texture:u,level:e}):l.push({material:r,slot:f,texture:null,level:e})}return l})}if(t instanceof j||t.isTexture===!0){const r=t;return this.assignTextureLODForSlot(r,e,null,null)}return Promise.resolve(null)}static assignTextureLODForSlot(t,e,r,o){return t?.isTexture!==!0?Promise.resolve(null):o==="glyphMap"?Promise.resolve(t):g.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&&o){const n=r[o];if(n&&!h){const l=this.getAssignedLODInformation(n);if(l&&l?.level<e)return h==="verbose"&&console.warn("Assigned texture level is already higher: ",l.level,e,r,n,s),null}if(ct&&s.mipmaps){const l=s.mipmaps.length;s.mipmaps.length=Math.min(s.mipmaps.length,3),l!==s.mipmaps.length&&h&&console.debug(`Reduced mipmap count from ${l} to ${s.mipmaps.length} for ${s.uuid}: ${s.image?.width}x${s.image?.height}.`)}r[o]=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?.[$];return e?(this._isLoadingMesh=!0,this.parser.getDependency("mesh",t).then(r=>(this._isLoadingMesh=!1,r&&g.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 o=e?.extensions[$];if(o){if(!o.lods){h&&console.warn("Texture has no LODs",o);return}let s=!1;for(const n of this.parser.associations.keys())n.isTexture===!0&&this.parser.associations.get(n)?.textures===r&&(s=!0,g.registerTexture(this.url,n,o.lods?.length,r,o));s||this.parser.getDependency("texture",r).then(n=>{n&&g.registerTexture(this.url,n,o.lods?.length,r,o)})}}}),this.parser.json.meshes?.forEach((e,r)=>{if(e?.extensions){const o=e?.extensions[$];if(o&&o.lods){for(const s of this.parser.associations.keys())if(s.isMesh){const n=this.parser.associations.get(s);n?.meshes===r&&g.registerMesh(this.url,o.guid,s,o.lods.length,n.primitives,o)}}}}),null}static registerTexture=(t,e,r,o,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(`> Progressive: register texture[${o}] "${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[ge]=s);const n=s.guid;g.assignLODInformation(t,e,n,r,o),g.lodInfos.set(n,s),g.lowresCache.set(n,e)};static registerMesh=(t,e,r,o,s,n)=>{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},n,r),g.assignLODInformation(t,l,e,o,s),g.lodInfos.set(e,n);let a=g.lowresCache.get(e);a?a.push(r.geometry):a=[r.geometry],g.lowresCache.set(e,a),o>0&&!z(r)&&Te(r,l);for(const u of R)u.onRegisteredNewMesh?.(r,n)};static lodInfos=new Map;static previouslyLoaded=new Map;static lowresCache=new Map;static workers=[];static _workersIndex=0;static async getOrLoadLOD(t,e){const r=h=="verbose",o=this.getAssignedLODInformation(t);if(!o)return h&&console.warn(`[gltf-progressive] No LOD information found: ${t.name}, uuid: ${t.uuid}, type: ${t.type}`,t),null;const s=o?.key;let n;if(t.isTexture===!0){const l=t;l.source&&l.source[ge]&&(n=l.source[ge])}if(n||(n=g.lodInfos.get(s)),n){if(e>0){let u=!1;const f=Array.isArray(n.lods);if(f&&e>=n.lods.length?u=!0:f||(u=!0),u)return this.lowresCache.get(s)}const l=Array.isArray(n.lods)?n.lods[e]?.path:n.lods;if(!l)return h&&!n["missing:uri"]&&(n["missing:uri"]=!0,console.warn("Missing uri for progressive asset for LOD "+e,n)),null;const a=tt(o.url,l);if(a.endsWith(".glb")||a.endsWith(".gltf")){if(!n.guid)return console.warn("missing pointer for glb/gltf texture",n),null;const u=a+"_"+n.guid,f=await this.queue.slot(a),w=this.previouslyLoaded.get(u);if(w!==void 0){r&&console.log(`LOD ${e} was already loading/loaded: ${u}`);let p=await w.catch(y=>(console.error(`Error loading LOD ${e} from ${a}
|
|
2
|
+
`,y),null)),m=!1;if(p==null||(p instanceof j&&t instanceof j?p.image?.data||p.source?.data?p=this.copySettings(t,p):(m=!0,this.previouslyLoaded.delete(u)):p instanceof V&&t instanceof V&&(p.attributes.position?.array||(m=!0,this.previouslyLoaded.delete(u)))),!m)return p}if(!f.use)return h&&console.log(`LOD ${e} was aborted: ${a}`),null;const b=n,T=new Promise(async(p,m)=>{if(ut){const x=await(await at({})).load(a);if(x.textures.length>0)for(const c of x.textures){let d=c.texture;return g.assignLODInformation(o.url,d,s,e,void 0),t instanceof j&&(d=this.copySettings(t,d)),d&&(d.guid=b.guid),p(d)}if(x.geometries.length>0){const c=new Array;for(const d of x.geometries){const L=d.geometry;g.assignLODInformation(o.url,L,s,e,d.primitiveIndex),c.push(L)}return p(c)}return p(null)}const y=new ie;ae(y),h&&(await new Promise(x=>setTimeout(x,1e3)),r&&console.warn("Start loading (delayed) "+a,b.guid));let A=a;if(b&&Array.isArray(b.lods)){const x=b.lods[e];x.hash&&(A+="?v="+x.hash)}const _=await y.loadAsync(A).catch(x=>(console.error(`Error loading LOD ${e} from ${a}
|
|
3
|
+
`,x),p(null)));if(!_)return p(null);const E=_.parser;r&&console.log("Loading finished "+a,b.guid);let D=0;if(_.parser.json.textures){let x=!1;for(const c of _.parser.json.textures){if(c?.extensions){const d=c?.extensions[$];if(d?.guid&&d.guid===b.guid){x=!0;break}}D++}if(x){let c=await E.getDependency("texture",D);return c&&g.assignLODInformation(o.url,c,s,e,void 0),r&&console.log('change "'+t.name+'" \u2192 "'+c.name+'"',a,D,c,u),t instanceof j&&(c=this.copySettings(t,c)),c&&(c.guid=b.guid),p(c)}else h&&console.warn("Could not find texture with guid",b.guid,_.parser.json)}if(D=0,_.parser.json.meshes){let x=!1;for(const c of _.parser.json.meshes){if(c?.extensions){const d=c?.extensions[$];if(d?.guid&&d.guid===b.guid){x=!0;break}}D++}if(x){const c=await E.getDependency("mesh",D);if(r&&console.log(`Loaded Mesh "${c.name}"`,a,D,c,u),c.isMesh===!0){const d=c.geometry;return g.assignLODInformation(o.url,d,s,e,0),p(d)}else{const d=new Array;for(let L=0;L<c.children.length;L++){const O=c.children[L];if(O.isMesh===!0){const S=O.geometry;g.assignLODInformation(o.url,S,s,e,L),d.push(S)}}return p(d)}}else h&&console.warn("Could not find mesh with guid",b.guid,_.parser.json)}return p(null)});return this.previouslyLoaded.set(u,T),f.use(T),await T}else if(t instanceof j){r&&console.log("Load texture from uri: "+a);const u=await new ze().loadAsync(a);return u?(u.guid=n.guid,u.flipY=!1,u.needsUpdate=!0,u.colorSpace=t.colorSpace,r&&console.log(n,u)):h&&console.warn("failed loading",a),u}}else h&&console.warn(`Can not load LOD ${e}: no LOD info found for "${s}" ${t.name}`,t.type);return null}static maxConcurrent=50;static queue=new rt(g.maxConcurrent,{debug:h!=!1});static assignLODInformation(t,e,r,o,s){if(!e)return;e.userData||(e.userData={});const n=new dt(t,r,o,s);e.userData.LODS=n,"source"in e&&typeof e.source=="object"&&(e.source.LODS=n)}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
|
|
4
4
|
`,t.uuid,`
|
|
5
|
-
`,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 dt{url;key;level;index;constructor(t,e,r,o){this.url=t,this.key=e,this.level=r,o!=null&&(this.index=o)}}class fe{static addPromise=(t,e,r,o)=>{o.forEach(s=>{s.add(t,e,r)})};frame_start;frame_capture_end;ready;_resolve;_signal;get awaitedCount(){return this._addedCount}get resolvedCount(){return this._resolvedCount}get currentlyAwaiting(){return this._awaiting.length}_resolved=!1;_addedCount=0;_resolvedCount=0;_awaiting=[];_maxPromisesPerObject=1;constructor(t,e){const r=Math.max(e.frames??2,2);this.frame_start=t,this.frame_capture_end=t+r,this.ready=new Promise(o=>{this._resolve=o}),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._signal?.aborted||this._currentFrame>this.frame_capture_end&&this._awaiting.length===0)&&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._currentFrame>this.frame_capture_end)){if(this._maxPromisesPerObject>=1)if(this._seen.has(e)){let o=this._seen.get(e);if(o>=this._maxPromisesPerObject){h&&console.warn("PromiseGroup: Already awaiting object ignoring new promise for it.");return}this._seen.set(e,o+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 B=U("debugprogressive"),ht=U("noprogressive"),me=Symbol("Needle:LODSManager"),pe=Symbol("Needle:LODState"),G=Symbol("Needle:CurrentLOD"),M={mesh_lod:-1,texture_lod:-1};class v{static debugDrawLine;static overrideGlobalLodLevel;static getObjectLODState(t){return t[pe]}static addPlugin(t){R.push(t)}static removePlugin(t){const e=R.indexOf(t);e>=0&&R.splice(e,1)}static get(t,e){if(t[me])return console.debug("[gltf-progressive] LODsManager already exists for this renderer"),t[me];const r=new v(t,{engine:"unknown",...e});return t[me]=r,r}renderer;context;projectionScreenMatrix=new Le;get plugins(){return R}targetTriangleDensity=2e5;skinnedMeshAutoUpdateBoundsInterval=30;updateInterval="auto";#e=1;pause=!1;manual=!1;_newPromiseGroups=[];_promiseGroupIds=0;awaitLoading(t){const e=this._promiseGroupIds++,r=new fe(this.#s,{...t});this._newPromiseGroups.push(r);const o=performance.now();return r.ready.finally(()=>{const s=this._newPromiseGroups.indexOf(r);s>=0&&(this._newPromiseGroups.splice(s,1),Pe()&&performance.measure("LODsManager:awaitLoading",{start:o,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;#n=new qe;#s=0;#o=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;J(this.renderer),this.renderer.render=function(r,o){const s=e.renderer.getRenderTarget();(s==null||"isXRRenderTarget"in s&&s.isXRRenderTarget)&&(t=0,e.#s+=1,e.#o=e.#n.getDelta(),e.#i+=e.#o,e._fpsBuffer.shift(),e._fpsBuffer.push(1/e.#o),e.#r=e._fpsBuffer.reduce((l,a)=>l+a)/e._fpsBuffer.length,B&&e.#s%200===0&&console.log("FPS",Math.round(e.#r),"Interval:",e.#e));const n=t++;e.#t.call(this,r,o),e.onAfterRender(r,o,n)}}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 o=this.renderer.renderLists.get(t,0).opaque;let s=!0;if(o.length===1){const n=o[0].material;(n.name==="EffectMaterial"||n.name==="CopyShader")&&(s=!1)}if((e.parent&&e.parent.type==="CubeCamera"||r>=1&&e.type==="OrthographicCamera")&&(s=!1),s){if(ht||(this.updateInterval==="auto"?this.#r<40&&this.#e<10?(this.#e+=1,B&&console.warn("\u2193 Reducing LOD updates",this.#e,this.#r.toFixed(0))):this.#r>=60&&this.#e>1&&(this.#e-=1,B&&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),o=r.opaque;this.projectionScreenMatrix.multiplyMatrices(e.projectionMatrix,e.matrixWorldInverse);const s=this.targetTriangleDensity;for(const a of o){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")){B&&(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(B==="color"&&a.material&&!a.object.progressive_debug_color){a.object.progressive_debug_color=!0;const f=Math.random()*16777215,w=new Ve({color:f});a.object.material=w}const u=a.object;(u instanceof N||u.isMesh)&&this.updateLODs(t,e,u,s)}const n=r.transparent;for(const a of n){const u=a.object;(u instanceof N||u.isMesh)&&this.updateLODs(t,e,u,s)}const l=r.transmissive;for(const a of l){const u=a.object;(u instanceof N||u.isMesh)&&this.updateLODs(t,e,u,s)}}updateLODs(t,e,r,o){r.userData||(r.userData={});let s=r[pe];if(s||(s=new gt,r[pe]=s),s.frames++<2)return;for(const l of R)l.onBeforeUpdateLOD?.(this.renderer,t,e,r);const n=v.overrideGlobalLodLevel!==void 0?v.overrideGlobalLodLevel:W;n>=0?(M.mesh_lod=n,M.texture_lod=n):(this.calculateLodLevel(e,r,s,o,M),M.mesh_lod=Math.round(M.mesh_lod),M.texture_lod=Math.round(M.texture_lod)),M.mesh_lod>=0&&this.loadProgressiveMeshes(r,M.mesh_lod),r.material&&M.texture_lod>=0&&this.loadProgressiveTextures(r.material,M.texture_lod,n),h&&r.material&&!r.isGizmo&&Ie(r.material);for(const l of R)l.onAfterUpdatedLOD?.(this.renderer,t,e,r,M);s.lastLodLevel_Mesh=M.mesh_lod,s.lastLodLevel_Texture=M.texture_lod}loadProgressiveTextures(t,e,r){if(!t)return;if(Array.isArray(t)){for(const s of t)this.loadProgressiveTextures(s,e);return}let o=!1;if((t[G]===void 0||e<t[G])&&(o=!0),r!==void 0&&r>=0&&(o=t[G]!=r,e=r),o){t[G]=e;const s=g.assignTextureLOD(t,e).then(n=>{this._lodchangedlisteners.forEach(l=>l({type:"texture",level:e,object:t}))});fe.addPromise("texture",t,s,this._newPromiseGroups)}}loadProgressiveMeshes(t,e){if(!t)return Promise.resolve(null);let r=t[G]!==e;const o=t["DEBUG:LOD"];if(o!=null&&(r=t[G]!=o,e=o),r){t[G]=e;const s=t.geometry,n=g.assignMeshLOD(t,e).then(l=>(l&&t[G]==e&&s!=t.geometry&&this._lodchangedlisteners.forEach(a=>a({type:"mesh",level:e,object:t})),l));return fe.addPromise("mesh",t,n,this._newPromiseGroups),n}return Promise.resolve(null)}_sphere=new ve;_tempBox=new ne;_tempBox2=new ne;tempMatrix=new Le;_tempWorldPosition=new I;_tempBoxSize=new I;_tempBox2Size=new I;static corner0=new I;static corner1=new I;static corner2=new I;static corner3=new I;static _tempPtInside=new I;static isInside(t,e){const r=t.min,o=t.max,s=(r.x+o.x)*.5,n=(r.y+o.y)*.5;return this._tempPtInside.set(s,n,r.z).applyMatrix4(e).z<0}static skinnedMeshBoundsFrameOffsetCounter=0;static $skinnedMeshBoundsOffset=Symbol("gltf-progressive-skinnedMeshBoundsOffset");calculateLodLevel(t,e,r,o,s){if(!e){s.mesh_lod=-1,s.texture_lod=-1;return}if(!t){s.mesh_lod=-1,s.texture_lod=-1;return}let n=11,l=!1;if(B&&e["DEBUG:LOD"]!=null)return e["DEBUG:LOD"];const a=g.getMeshLODExtension(e.geometry)?.lods,u=g.getPrimitiveIndex(e.geometry),f=a&&a.length>0,w=g.getMaterialMinMaxLODsCount(e.material),L=w.min_count!==1/0&&w.min_count>=0&&w.max_count>=0;if(!f&&!L){s.mesh_lod=0,s.texture_lod=0;return}f||(l=!0,n=0);const T=this.renderer.domElement.clientHeight||this.renderer.domElement.height;let p=e.geometry.boundingBox;if(e.type==="SkinnedMesh"){const m=e;if(!m.boundingBox)m.computeBoundingBox();else if(this.skinnedMeshAutoUpdateBoundsInterval>0){if(!m[v.$skinnedMeshBoundsOffset]){const A=v.skinnedMeshBoundsFrameOffsetCounter++;m[v.$skinnedMeshBoundsOffset]=A}const y=m[v.$skinnedMeshBoundsOffset];if((r.frames+y)%this.skinnedMeshAutoUpdateBoundsInterval===0){const A=z(m),_=m.geometry;A&&(m.geometry=A),m.computeBoundingBox(),m.geometry=_}}p=m.boundingBox}if(p){const m=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 c=t.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(c)){s.mesh_lod=0,s.texture_lod=0;return}}if(this._tempBox.copy(p),this._tempBox.applyMatrix4(e.matrixWorld),m.isPerspectiveCamera&&v.isInside(this._tempBox,this.projectionScreenMatrix)){s.mesh_lod=0,s.texture_lod=0;return}if(this._tempBox.applyMatrix4(this.projectionScreenMatrix),this.renderer.xr.enabled&&m.isPerspectiveCamera&&m.fov>70){const c=this._tempBox.min,d=this._tempBox.max;let b=c.x,O=c.y,S=d.x,q=d.y;const H=2,se=1.5,Y=(c.x+d.x)*.5,Q=(c.y+d.y)*.5;b=(b-Y)*H+Y,O=(O-Q)*H+Q,S=(S-Y)*H+Y,q=(q-Q)*H+Q;const Re=b<0&&S>0?0:Math.min(Math.abs(c.x),Math.abs(d.x)),Ge=O<0&&q>0?0:Math.min(Math.abs(c.y),Math.abs(d.y)),oe=Math.max(Re,Ge);r.lastCentrality=(se-oe)*(se-oe)*(se-oe)}else r.lastCentrality=1;const y=this._tempBox.getSize(this._tempBoxSize);y.multiplyScalar(.5),screen.availHeight>0&&T>0&&y.multiplyScalar(T/screen.availHeight),t.isPerspectiveCamera?y.x*=t.aspect:t.isOrthographicCamera;const A=t.matrixWorldInverse,_=this._tempBox2;_.copy(p),_.applyMatrix4(e.matrixWorld),_.applyMatrix4(A);const E=_.getSize(this._tempBox2Size),D=Math.max(E.x,E.y);if(Math.max(y.x,y.y)!=0&&D!=0&&(y.z=E.z/Math.max(E.x,E.y)*Math.max(y.x,y.y)),r.lastScreenCoverage=Math.max(y.x,y.y,y.z),r.lastScreenspaceVolume.copy(y),r.lastScreenCoverage*=r.lastCentrality,B&&v.debugDrawLine){const c=this.tempMatrix.copy(this.projectionScreenMatrix);c.invert();const d=v.corner0,b=v.corner1,O=v.corner2,S=v.corner3;d.copy(this._tempBox.min),b.copy(this._tempBox.max),b.x=d.x,O.copy(this._tempBox.max),O.y=d.y,S.copy(this._tempBox.max);const q=(d.z+S.z)*.5;d.z=b.z=O.z=S.z=q,d.applyMatrix4(c),b.applyMatrix4(c),O.applyMatrix4(c),S.applyMatrix4(c),v.debugDrawLine(d,b,255),v.debugDrawLine(d,O,255),v.debugDrawLine(b,S,255),v.debugDrawLine(O,S,255)}let x=999;if(a&&r.lastScreenCoverage>0)for(let c=0;c<a.length;c++){const d=a[c],b=(d.densities?.[u]||d.density||1e-5)/r.lastScreenCoverage;if(u>0&&Pe()&&!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.")),b<o){x=c;break}}x<n&&(n=x,l=!0)}if(l?s.mesh_lod=n:s.mesh_lod=r.lastLodLevel_Mesh,B&&s.mesh_lod!=r.lastLodLevel_Mesh){const m=a?.[s.mesh_lod];m&&console.debug(`Mesh LOD changed: ${r.lastLodLevel_Mesh} \u2192 ${s.mesh_lod} (density: ${m.densities?.[u].toFixed(0)}) | ${e.name}`)}if(L){const m="saveData"in globalThis.navigator&&globalThis.navigator.saveData===!0;if(r.lastLodLevel_Texture<0){if(s.texture_lod=w.max_count-1,B){const y=w.lods[w.max_count-1];B&&console.log(`First Texture LOD ${s.texture_lod} (${y.max_height}px) - ${e.name}`)}}else{const y=r.lastScreenspaceVolume.x+r.lastScreenspaceVolume.y+r.lastScreenspaceVolume.z;let A=r.lastScreenCoverage*4;this.context?.engine==="model-viewer"&&(A*=1.5);const _=T/window.devicePixelRatio*A;let E=!1;for(let D=w.lods.length-1;D>=0;D--){const x=w.lods[D];if(!(m&&x.max_height>=2048)&&!(Se()&&x.max_height>4096)&&(x.max_height>_||!E&&D===0)){if(E=!0,s.texture_lod=D,B&&s.texture_lod<r.lastLodLevel_Texture){const c=x.max_height;console.log(`Texture LOD changed: ${r.lastLodLevel_Texture} \u2192 ${s.texture_lod} = ${c}px
|
|
5
|
+
`,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 dt{url;key;level;index;constructor(t,e,r,o){this.url=t,this.key=e,this.level=r,o!=null&&(this.index=o)}}class fe{static addPromise=(t,e,r,o)=>{o.forEach(s=>{s.add(t,e,r)})};frame_start;frame_capture_end;ready;_resolve;_signal;get awaitedCount(){return this._addedCount}get resolvedCount(){return this._resolvedCount}get currentlyAwaiting(){return this._awaiting.length}_resolved=!1;_addedCount=0;_resolvedCount=0;_awaiting=[];_maxPromisesPerObject=1;constructor(t,e){const r=Math.max(e.frames??2,2);this.frame_start=t,this.frame_capture_end=t+r,this.ready=new Promise(o=>{this._resolve=o}),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._signal?.aborted||this._currentFrame>this.frame_capture_end&&this._awaiting.length===0)&&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._currentFrame>this.frame_capture_end)){if(this._maxPromisesPerObject>=1)if(this._seen.has(e)){let o=this._seen.get(e);if(o>=this._maxPromisesPerObject){h&&console.warn("PromiseGroup: Already awaiting object ignoring new promise for it.");return}this._seen.set(e,o+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 B=U("debugprogressive"),ht=U("noprogressive"),me=Symbol("Needle:LODSManager"),pe=Symbol("Needle:LODState"),G=Symbol("Needle:CurrentLOD"),M={mesh_lod:-1,texture_lod:-1};class v{static debugDrawLine;static overrideGlobalLodLevel;static getObjectLODState(t){return t[pe]}static addPlugin(t){R.push(t)}static removePlugin(t){const e=R.indexOf(t);e>=0&&R.splice(e,1)}static get(t,e){if(t[me])return console.debug("[gltf-progressive] LODsManager already exists for this renderer"),t[me];const r=new v(t,{engine:"unknown",...e});return t[me]=r,r}renderer;context;projectionScreenMatrix=new be;get plugins(){return R}targetTriangleDensity=2e5;skinnedMeshAutoUpdateBoundsInterval=30;updateInterval="auto";#e=1;pause=!1;manual=!1;_newPromiseGroups=[];_promiseGroupIds=0;awaitLoading(t){const e=this._promiseGroupIds++,r=new fe(this.#s,{...t});this._newPromiseGroups.push(r);const o=performance.now();return r.ready.finally(()=>{const s=this._newPromiseGroups.indexOf(r);s>=0&&(this._newPromiseGroups.splice(s,1),Pe()&&performance.measure("LODsManager:awaitLoading",{start:o,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;#n=new qe;#s=0;#o=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;J(this.renderer),this.renderer.render=function(r,o){const s=e.renderer.getRenderTarget();(s==null||"isXRRenderTarget"in s&&s.isXRRenderTarget)&&(t=0,e.#s+=1,e.#o=e.#n.getDelta(),e.#i+=e.#o,e._fpsBuffer.shift(),e._fpsBuffer.push(1/e.#o),e.#r=e._fpsBuffer.reduce((l,a)=>l+a)/e._fpsBuffer.length,B&&e.#s%200===0&&console.log("FPS",Math.round(e.#r),"Interval:",e.#e));const n=t++;e.#t.call(this,r,o),e.onAfterRender(r,o,n)}}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 o=this.renderer.renderLists.get(t,0).opaque;let s=!0;if(o.length===1){const n=o[0].material;(n.name==="EffectMaterial"||n.name==="CopyShader")&&(s=!1)}if((e.parent&&e.parent.type==="CubeCamera"||r>=1&&e.type==="OrthographicCamera")&&(s=!1),s){if(ht||(this.updateInterval==="auto"?this.#r<40&&this.#e<10?(this.#e+=1,B&&console.warn("\u2193 Reducing LOD updates",this.#e,this.#r.toFixed(0))):this.#r>=60&&this.#e>1&&(this.#e-=1,B&&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),o=r.opaque;this.projectionScreenMatrix.multiplyMatrices(e.projectionMatrix,e.matrixWorldInverse);const s=this.targetTriangleDensity;for(const a of o){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")){B&&(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(B==="color"&&a.material&&!a.object.progressive_debug_color){a.object.progressive_debug_color=!0;const f=Math.random()*16777215,w=new Ve({color:f});a.object.material=w}const u=a.object;(u instanceof N||u.isMesh)&&this.updateLODs(t,e,u,s)}const n=r.transparent;for(const a of n){const u=a.object;(u instanceof N||u.isMesh)&&this.updateLODs(t,e,u,s)}const l=r.transmissive;for(const a of l){const u=a.object;(u instanceof N||u.isMesh)&&this.updateLODs(t,e,u,s)}}updateLODs(t,e,r,o){r.userData||(r.userData={});let s=r[pe];if(s||(s=new gt,r[pe]=s),s.frames++<2)return;for(const l of R)l.onBeforeUpdateLOD?.(this.renderer,t,e,r);const n=v.overrideGlobalLodLevel!==void 0?v.overrideGlobalLodLevel:W;n>=0?(M.mesh_lod=n,M.texture_lod=n):(this.calculateLodLevel(e,r,s,o,M),M.mesh_lod=Math.round(M.mesh_lod),M.texture_lod=Math.round(M.texture_lod)),M.mesh_lod>=0&&this.loadProgressiveMeshes(r,M.mesh_lod),r.material&&M.texture_lod>=0&&this.loadProgressiveTextures(r.material,M.texture_lod,n),h&&r.material&&!r.isGizmo&&Ie(r.material);for(const l of R)l.onAfterUpdatedLOD?.(this.renderer,t,e,r,M);s.lastLodLevel_Mesh=M.mesh_lod,s.lastLodLevel_Texture=M.texture_lod}loadProgressiveTextures(t,e,r){if(!t)return;if(Array.isArray(t)){for(const s of t)this.loadProgressiveTextures(s,e);return}let o=!1;if((t[G]===void 0||e<t[G])&&(o=!0),r!==void 0&&r>=0&&(o=t[G]!=r,e=r),o){t[G]=e;const s=g.assignTextureLOD(t,e).then(n=>{this._lodchangedlisteners.forEach(l=>l({type:"texture",level:e,object:t}))});fe.addPromise("texture",t,s,this._newPromiseGroups)}}loadProgressiveMeshes(t,e){if(!t)return Promise.resolve(null);let r=t[G]!==e;const o=t["DEBUG:LOD"];if(o!=null&&(r=t[G]!=o,e=o),r){t[G]=e;const s=t.geometry,n=g.assignMeshLOD(t,e).then(l=>(l&&t[G]==e&&s!=t.geometry&&this._lodchangedlisteners.forEach(a=>a({type:"mesh",level:e,object:t})),l));return fe.addPromise("mesh",t,n,this._newPromiseGroups),n}return Promise.resolve(null)}_sphere=new ve;_tempBox=new ne;_tempBox2=new ne;tempMatrix=new be;_tempWorldPosition=new I;_tempBoxSize=new I;_tempBox2Size=new I;static corner0=new I;static corner1=new I;static corner2=new I;static corner3=new I;static _tempPtInside=new I;static isInside(t,e){const r=t.min,o=t.max,s=(r.x+o.x)*.5,n=(r.y+o.y)*.5;return this._tempPtInside.set(s,n,r.z).applyMatrix4(e).z<0}static skinnedMeshBoundsFrameOffsetCounter=0;static $skinnedMeshBoundsOffset=Symbol("gltf-progressive-skinnedMeshBoundsOffset");calculateLodLevel(t,e,r,o,s){if(!e){s.mesh_lod=-1,s.texture_lod=-1;return}if(!t){s.mesh_lod=-1,s.texture_lod=-1;return}let n=11,l=!1;if(B&&e["DEBUG:LOD"]!=null)return e["DEBUG:LOD"];const a=g.getMeshLODExtension(e.geometry)?.lods,u=g.getPrimitiveIndex(e.geometry),f=a&&a.length>0,w=g.getMaterialMinMaxLODsCount(e.material),b=w.min_count!==1/0&&w.min_count>=0&&w.max_count>=0;if(!f&&!b){s.mesh_lod=0,s.texture_lod=0;return}f||(l=!0,n=0);const T=this.renderer.domElement.clientHeight||this.renderer.domElement.height;let p=e.geometry.boundingBox;if(e.type==="SkinnedMesh"){const m=e;if(!m.boundingBox)m.computeBoundingBox();else if(this.skinnedMeshAutoUpdateBoundsInterval>0){if(!m[v.$skinnedMeshBoundsOffset]){const A=v.skinnedMeshBoundsFrameOffsetCounter++;m[v.$skinnedMeshBoundsOffset]=A}const y=m[v.$skinnedMeshBoundsOffset];if((r.frames+y)%this.skinnedMeshAutoUpdateBoundsInterval===0){const A=z(m),_=m.geometry;A&&(m.geometry=A),m.computeBoundingBox(),m.geometry=_}}p=m.boundingBox}if(p){const m=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 c=t.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(c)){s.mesh_lod=0,s.texture_lod=0;return}}if(this._tempBox.copy(p),this._tempBox.applyMatrix4(e.matrixWorld),m.isPerspectiveCamera&&v.isInside(this._tempBox,this.projectionScreenMatrix)){s.mesh_lod=0,s.texture_lod=0;return}if(this._tempBox.applyMatrix4(this.projectionScreenMatrix),this.renderer.xr.enabled&&m.isPerspectiveCamera&&m.fov>70){const c=this._tempBox.min,d=this._tempBox.max;let L=c.x,O=c.y,S=d.x,q=d.y;const H=2,se=1.5,Y=(c.x+d.x)*.5,Q=(c.y+d.y)*.5;L=(L-Y)*H+Y,O=(O-Q)*H+Q,S=(S-Y)*H+Y,q=(q-Q)*H+Q;const Re=L<0&&S>0?0:Math.min(Math.abs(c.x),Math.abs(d.x)),Ge=O<0&&q>0?0:Math.min(Math.abs(c.y),Math.abs(d.y)),oe=Math.max(Re,Ge);r.lastCentrality=(se-oe)*(se-oe)*(se-oe)}else r.lastCentrality=1;const y=this._tempBox.getSize(this._tempBoxSize);y.multiplyScalar(.5),screen.availHeight>0&&T>0&&y.multiplyScalar(T/screen.availHeight),t.isPerspectiveCamera?y.x*=t.aspect:t.isOrthographicCamera;const A=t.matrixWorldInverse,_=this._tempBox2;_.copy(p),_.applyMatrix4(e.matrixWorld),_.applyMatrix4(A);const E=_.getSize(this._tempBox2Size),D=Math.max(E.x,E.y);if(Math.max(y.x,y.y)!=0&&D!=0&&(y.z=E.z/Math.max(E.x,E.y)*Math.max(y.x,y.y)),r.lastScreenCoverage=Math.max(y.x,y.y,y.z),r.lastScreenspaceVolume.copy(y),r.lastScreenCoverage*=r.lastCentrality,B&&v.debugDrawLine){const c=this.tempMatrix.copy(this.projectionScreenMatrix);c.invert();const d=v.corner0,L=v.corner1,O=v.corner2,S=v.corner3;d.copy(this._tempBox.min),L.copy(this._tempBox.max),L.x=d.x,O.copy(this._tempBox.max),O.y=d.y,S.copy(this._tempBox.max);const q=(d.z+S.z)*.5;d.z=L.z=O.z=S.z=q,d.applyMatrix4(c),L.applyMatrix4(c),O.applyMatrix4(c),S.applyMatrix4(c),v.debugDrawLine(d,L,255),v.debugDrawLine(d,O,255),v.debugDrawLine(L,S,255),v.debugDrawLine(O,S,255)}let x=999;if(a&&r.lastScreenCoverage>0)for(let c=0;c<a.length;c++){const d=a[c],L=(d.densities?.[u]||d.density||1e-5)/r.lastScreenCoverage;if(u>0&&Pe()&&!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.")),L<o){x=c;break}}x<n&&(n=x,l=!0)}if(l?s.mesh_lod=n:s.mesh_lod=r.lastLodLevel_Mesh,B&&s.mesh_lod!=r.lastLodLevel_Mesh){const m=a?.[s.mesh_lod];m&&console.debug(`Mesh LOD changed: ${r.lastLodLevel_Mesh} \u2192 ${s.mesh_lod} (density: ${m.densities?.[u].toFixed(0)}) | ${e.name}`)}if(b){const m="saveData"in globalThis.navigator&&globalThis.navigator.saveData===!0;if(r.lastLodLevel_Texture<0){if(s.texture_lod=w.max_count-1,B){const y=w.lods[w.max_count-1];B&&console.log(`First Texture LOD ${s.texture_lod} (${y.max_height}px) - ${e.name}`)}}else{const y=r.lastScreenspaceVolume.x+r.lastScreenspaceVolume.y+r.lastScreenspaceVolume.z;let A=r.lastScreenCoverage*4;this.context?.engine==="model-viewer"&&(A*=1.5);const _=T/window.devicePixelRatio*A;let E=!1;for(let D=w.lods.length-1;D>=0;D--){const x=w.lods[D];if(!(m&&x.max_height>=2048)&&!(Se()&&x.max_height>4096)&&(x.max_height>_||!E&&D===0)){if(E=!0,s.texture_lod=D,B&&s.texture_lod<r.lastLodLevel_Texture){const c=x.max_height;console.log(`Texture LOD changed: ${r.lastLodLevel_Texture} \u2192 ${s.texture_lod} = ${c}px
|
|
6
6
|
Screensize: ${_.toFixed(0)}px, Coverage: ${(100*r.lastScreenCoverage).toFixed(2)}%, Volume ${y.toFixed(1)}
|
|
7
7
|
${e.name}`)}break}}}}else s.texture_lod=0}}class gt{frames=0;lastLodLevel_Mesh=-1;lastLodLevel_Texture=-1;lastScreenCoverage=0;lastScreenspaceVolume=new I;lastCentrality=0}const Ee=Symbol("NEEDLE_mesh_lod"),re=Symbol("NEEDLE_texture_lod");let xe=null;function we(){const i=ft();i&&(i.mapURLs(function(t){return ke(),t}),ke(),xe?.disconnect(),xe=new MutationObserver(t=>{t.forEach(e=>{e.addedNodes.forEach(r=>{r instanceof HTMLElement&&r.tagName.toLowerCase()==="model-viewer"&&Be(r)})})}),xe.observe(document,{childList:!0,subtree:!0}))}function ft(){return typeof customElements>"u"?null:customElements.get("model-viewer")||(customElements.whenDefined("model-viewer").then(()=>{console.debug("[gltf-progressive] model-viewer defined"),we()}),null)}function ke(){typeof document>"u"||document.querySelectorAll("model-viewer").forEach(i=>{Be(i)})}const Ce=new WeakSet;let mt=0;function Be(i){if(!i||Ce.has(i))return null;Ce.add(i),console.debug("[gltf-progressive] found new model-viewer..."+ ++mt+`
|
|
8
|
-
`,i.getAttribute("src"));let t=null,e=null,r=null;for(let o=i;o!=null;o=Object.getPrototypeOf(o)){const s=Object.getOwnPropertySymbols(o),n=s.find(u=>u.toString()=="Symbol(renderer)"),l=s.find(u=>u.toString()=="Symbol(scene)"),a=s.find(u=>u.toString()=="Symbol(needsRender)");!t&&n!=null&&(t=i[n].threeRenderer),!e&&l!=null&&(e=i[l]),!r&&a!=null&&(r=i[a])}if(t&&e){let o=function(){if(r){let n=0,l=setInterval(()=>{if(n++>5){clearInterval(l);return}r?.call(i)},300)}};console.debug("[gltf-progressive] setup model-viewer");const s=v.get(t,{engine:"model-viewer"});return v.addPlugin(new pt),s.enable(),s.addEventListener("changed",()=>{r?.call(i)}),i.addEventListener("model-visibility",n=>{n.detail.visible&&r?.call(i)}),i.addEventListener("load",()=>{o()}),()=>{s.disable()}}return null}class pt{_didWarnAboutMissingUrl=!1;onBeforeUpdateLOD(t,e,r,o){this.tryParseMeshLOD(e,o),this.tryParseTextureLOD(e,o)}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[re]==!0)return;e[re]=!0;const r=this.tryGetCurrentGLTF(t),o=this.tryGetCurrentModelViewer(t),s=this.getUrl(o);if(s&&r&&e.material){let n=function(a){if(a[re]==!0)return;a[re]=!0,a.userData&&(a.userData.LOD=-1);const u=Object.keys(a);for(let f=0;f<u.length;f++){const w=u[f],
|
|
8
|
+
`,i.getAttribute("src"));let t=null,e=null,r=null;for(let o=i;o!=null;o=Object.getPrototypeOf(o)){const s=Object.getOwnPropertySymbols(o),n=s.find(u=>u.toString()=="Symbol(renderer)"),l=s.find(u=>u.toString()=="Symbol(scene)"),a=s.find(u=>u.toString()=="Symbol(needsRender)");!t&&n!=null&&(t=i[n].threeRenderer),!e&&l!=null&&(e=i[l]),!r&&a!=null&&(r=i[a])}if(t&&e){let o=function(){if(r){let n=0,l=setInterval(()=>{if(n++>5){clearInterval(l);return}r?.call(i)},300)}};console.debug("[gltf-progressive] setup model-viewer");const s=v.get(t,{engine:"model-viewer"});return v.addPlugin(new pt),s.enable(),s.addEventListener("changed",()=>{r?.call(i)}),i.addEventListener("model-visibility",n=>{n.detail.visible&&r?.call(i)}),i.addEventListener("load",()=>{o()}),()=>{s.disable()}}return null}class pt{_didWarnAboutMissingUrl=!1;onBeforeUpdateLOD(t,e,r,o){this.tryParseMeshLOD(e,o),this.tryParseTextureLOD(e,o)}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[re]==!0)return;e[re]=!0;const r=this.tryGetCurrentGLTF(t),o=this.tryGetCurrentModelViewer(t),s=this.getUrl(o);if(s&&r&&e.material){let n=function(a){if(a[re]==!0)return;a[re]=!0,a.userData&&(a.userData.LOD=-1);const u=Object.keys(a);for(let f=0;f<u.length;f++){const w=u[f],b=a[w];if(b?.isTexture===!0){const T=b.userData?.associations?.textures;if(T==null)continue;const p=r.parser.json.textures[T];if(!p){console.warn("Texture data not found for texture index "+T);continue}if(p?.extensions?.[$]){const m=p.extensions[$];m&&s&&g.registerTexture(s,b,m.lods.length,T,m)}}}};const l=e.material;if(Array.isArray(l))for(const a of l)n(a);else n(l)}}tryParseMeshLOD(t,e){if(e[Ee]==!0)return;e[Ee]=!0;const r=this.tryGetCurrentModelViewer(t),o=this.getUrl(r);if(!o)return;const s=e.userData?.gltfExtensions?.[$];if(s&&o){const n=e.uuid;g.registerMesh(o,n,e,0,s.lods.length,s)}}}function $e(...i){let t,e,r,o;switch(i.length){case 2:[r,e]=i,o={};break;case 3:[r,e,o]=i;break;case 4:[t,e,r,o]=i;break;default:throw new Error("Invalid arguments")}J(e),ae(r),ce(r,{progressive:!0,...o?.hints}),r.register(n=>new g(n));const s=v.get(e);return o?.enableLODsManager!==!1&&s.enable(),s}if(we(),!st){const i={gltfProgressive:{useNeedleProgressive:$e,LODsManager:v,configureLoader:ce,getRaycastMesh:z,useRaycastMeshes:Ae}};if(!globalThis.Needle)globalThis.Needle=i;else for(const t in i)globalThis.Needle[t]=i[t]}export{$ as EXTENSION_NAME,v as LODsManager,g as NEEDLE_progressive,Le as VERSION,ae as addDracoAndKTX2Loaders,ce as configureLoader,J as createLoaders,z as getRaycastMesh,we as patchModelViewer,Te as registerRaycastMesh,De as setDracoDecoderLocation,Me as setKTX2TranscoderLocation,$e as useNeedleProgressive,Ae as useRaycastMeshes};
|
package/gltf-progressive.umd.cjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
"use strict";var Fe=Object.create;var Le=Object.defineProperty;var Ue=Object.getOwnPropertyDescriptor;var Ee=Object.getOwnPropertyNames;var We=Object.getPrototypeOf,Ne=Object.prototype.hasOwnProperty;var Ve=(o,t,e,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of Ee(t))!Ne.call(o,n)&&n!==e&&Le(o,n,{get:()=>t[n],enumerable:!(r=Ue(t,n))||r.enumerable});return o};var ze=(o,t,e)=>(e=o!=null?Fe(We(o)):{},Ve(t||!o||!o.__esModule?Le(e,"default",{value:o,enumerable:!0}):e,o));Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const d=require("three"),pe=require("three/examples/jsm/loaders/GLTFLoader.js"),qe=require("three/examples/jsm/libs/meshopt_decoder.module.js"),je=require("three/examples/jsm/loaders/DRACOLoader.js"),Xe=require("three/examples/jsm/loaders/KTX2Loader.js");var oe=typeof document<"u"?document.currentScript:null;const De="";globalThis.GLTF_PROGRESSIVE_VERSION=De;console.debug("[gltf-progressive] version -");let k="https://www.gstatic.com/draco/versioned/decoders/1.5.7/",V="https://www.gstatic.com/basis-universal/versioned/2021-04-15-ba1c3e4/";const Ke=k,Ye=V,Oe=new URL(k+"draco_decoder.js");Oe.searchParams.append("range","true");fetch(Oe,{method:"GET",headers:{Range:"bytes=0-1"}}).catch(o=>{console.debug(`Failed to fetch remote Draco decoder from ${k} (offline: ${typeof navigator<"u"?navigator.onLine:"unknown"})`),k===Ke&&Se("./include/draco/"),V===Ye&&Te("./include/ktx2/")}).finally(()=>{Pe()});const He=()=>({dracoDecoderPath:k,ktx2TranscoderPath:V});function Se(o){k=o,A&&A[fe]!=k?(console.debug("Updating Draco decoder path to "+o),A[fe]=k,A.setDecoderPath(k),A.preload()):console.debug("Setting Draco decoder path to "+o)}function Te(o){V=o,R&&R.transcoderPath!=V?(console.debug("Updating KTX2 transcoder path to "+o),R.setTranscoderPath(V),R.init()):console.debug("Setting KTX2 transcoder path to "+o)}function re(o){return Pe(),o?R.detectSupport(o):o!==null&&console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures might fail"),{dracoLoader:A,ktx2Loader:R,meshoptDecoder:te}}function me(o){o.dracoLoader||o.setDRACOLoader(A),o.ktx2Loader||o.setKTX2Loader(R),o.meshoptDecoder||o.setMeshoptDecoder(te)}const fe=Symbol("dracoDecoderPath");let A,te,R;function Pe(){A||(A=new je.DRACOLoader,A[fe]=k,A.setDecoderPath(k),A.setDecoderConfig({type:"js"}),A.preload()),R||(R=new Xe.KTX2Loader,R.setTranscoderPath(V),R.init()),te||(te=qe.MeshoptDecoder)}const he=new WeakMap;function ye(o,t){let e=he.get(o);e?e=Object.assign(e,t):e=t,he.set(o,e)}const Qe=pe.GLTFLoader.prototype.load;function Je(...o){const t=he.get(this);let e=o[0];const r=new URL(e,window.location.href);if(r.hostname.endsWith("needle.tools")){const s=t?.progressive!==void 0?t.progressive:!0,i=t?.usecase?t.usecase:"default";s?this.requestHeader.Accept=`*/*;progressive=allowed;usecase=${i}`:this.requestHeader.Accept=`*/*;usecase=${i}`,e=r.toString()}return o[0]=e,Qe?.call(this,...o)}pe.GLTFLoader.prototype.load=Je;z("debugprogressive");function z(o){if(typeof window>"u")return!1;const e=new URL(window.location.href).searchParams.get(o);return e==null||e==="0"||e==="false"?!1:e===""?!0:e}function Ze(o,t){if(t===void 0||t.startsWith("./")||t.startsWith("http")||o===void 0)return t;const e=o.lastIndexOf("/");if(e>=0){const r=o.substring(0,e+1);for(;r.endsWith("/")&&t.startsWith("/");)t=t.substring(1);return r+t}return t}let X;function Ae(){return X!==void 0||(X=/iPhone|iPad|iPod|Android|IEMobile/i.test(navigator.userAgent),z("debugprogressive")&&console.log("[glTF Progressive]: isMobileDevice",X)),X}function _e(){if(typeof window>"u")return!1;const o=new URL(window.location.href),t=o.hostname==="localhost"||/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(o.hostname);return o.hostname==="127.0.0.1"||t}class et{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)})}}}const tt=typeof window>"u"&&typeof document>"u",ge=Symbol("needle:raycast-mesh");function q(o){return o?.[ge]instanceof d.BufferGeometry?o[ge]:null}function Ie(o,t){if((o.type==="Mesh"||o.type==="SkinnedMesh")&&!q(o)){const r=rt(t);r.userData={isRaycastMesh:!0},o[ge]=r}}function Ce(o=!0){if(o){if(K)return;const t=K=d.Mesh.prototype.raycast;d.Mesh.prototype.raycast=function(e,r){const n=this,s=q(n);let i;s&&n.isMesh&&(i=n.geometry,n.geometry=s),t.call(this,e,r),i&&(n.geometry=i)}}else{if(!K)return;d.Mesh.prototype.raycast=K,K=null}}let K=null;function rt(o){const t=new d.BufferGeometry;for(const e in o.attributes)t.setAttribute(e,o.getAttribute(e));return t.setIndex(o.getIndex()),t}const E=new Array,p=z("debugprogressive");let ee,N=-1;if(p){let o=function(){N+=1,N>=t&&(N=-1),console.log(`Toggle LOD level [${N}]`)},t=6;window.addEventListener("keyup",e=>{e.key==="p"&&o(),e.key==="w"&&(ee=!ee,console.log(`Toggle wireframe [${ee}]`));const r=parseInt(e.key);!isNaN(r)&&r>=0&&(N=r,console.log(`Set LOD level to [${N}]`))})}function ke(o){if(p)if(Array.isArray(o))for(const t of o)ke(t);else o&&"wireframe"in o&&(o.wireframe=ee===!0)}const Y=new Array;let st=0;const nt=Ae()?2:10;function ot(o){if(Y.length<nt){const r=Y.length;p&&console.warn(`[Worker] Creating new worker #${r}`);const n=xe.createWorker(o||{});return Y.push(n),n}const t=st++%Y.length;return Y[t]}class xe{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){it(n.result),s.resolve(n.result);const i=s.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(new URL("/assets/loader.worker-8olmVOL4.js",typeof document>"u"?require("url").pathToFileURL(__filename).href:oe&&oe.tagName.toUpperCase()==="SCRIPT"&&oe.src||new URL("gltf-progressive.umd.cjs",document.baseURI).href),{type:"module"});return new xe(e,t)}_running=[];_webglRenderer=null;async load(t,e){const r=He();let n=e?.renderer;n||(this._webglRenderer??=(async()=>{const{WebGLRenderer:u}=await import("three");return new u})(),n=await this._webglRenderer);const l=re(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 a={type:"load",url:t,dracoDecoderPath:r.dracoDecoderPath,ktx2TranscoderPath:r.ktx2TranscoderPath,ktx2LoaderConfig:l};return this._debug&&console.debug("[Worker] Sending load request",a),this.worker.postMessage(a),new Promise(u=>{this._running.push({url:t.toString(),resolve:u})})}_debug=!1}function it(o){for(const t of o.geometries){const e=t.geometry,r=new d.BufferGeometry;if(r.name=e.name||"",e.index){const n=e.index;r.setIndex(ie(n))}for(const n in e.attributes){const s=e.attributes[n],i=ie(s);r.setAttribute(n,i)}if(e.morphAttributes)for(const n in e.morphAttributes){const i=e.morphAttributes[n].map(l=>ie(l));r.morphAttributes[n]=i}if(r.morphTargetsRelative=e.morphTargetsRelative??!1,r.boundingBox=new d.Box3,r.boundingBox.min=new d.Vector3(e.boundingBox?.min.x,e.boundingBox?.min.y,e.boundingBox?.min.z),r.boundingBox.max=new d.Vector3(e.boundingBox?.max.x,e.boundingBox?.max.y,e.boundingBox?.max.z),r.boundingSphere=new d.Sphere(new d.Vector3(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 o.textures){const e=t.texture;let r=null;if(e.isCompressedTexture){const n=e.mipmaps,s=e.image?.width||e.source?.data?.width||-1,i=e.image?.height||e.source?.data?.height||-1;r=new d.CompressedTexture(n,s,i,e.format,e.type,e.mapping,e.wrapS,e.wrapT,e.magFilter,e.minFilter,e.anisotropy,e.colorSpace)}else r=new d.Texture(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 d.Matrix3(...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 o}function ie(o){let t=o;if("isInterleavedBufferAttribute"in o&&o.isInterleavedBufferAttribute){const e=o.data,r=e.array,n=new d.InterleavedBuffer(r,e.stride);t=new d.InterleavedBufferAttribute(n,o.itemSize,r.byteOffset,o.normalized),t.offset=o.offset}else"isBufferAttribute"in o&&o.isBufferAttribute&&(t=new d.BufferAttribute(o.array,o.itemSize,o.normalized),t.usage=o.usage,t.gpuType=o.gpuType,t.updateRanges=o.updateRanges);return t}const at=z("gltf-progressive-worker"),lt=z("gltf-progressive-reduce-mipmaps"),ae=Symbol("needle-progressive-texture"),$="NEEDLE_progressive";class x{get name(){return $}static getMeshLODExtension(t){const e=this.getAssignedLODInformation(t);return e?.key?this.lodInfos.get(e.key):null}static getPrimitiveIndex(t){const e=this.getAssignedLODInformation(t)?.index;return e??-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(p==="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&&i(u,e)}}else if(t.isMaterial)for(const l of Object.keys(t)){const a=t[l];a?.isTexture===!0&&i(a,e)}else p&&console.warn(`[getMaterialMinMaxLODsCount] Unsupported material type: ${t.type}`);return t[n]=e,e;function i(l,a){const u=r.getAssignedLODInformation(l);if(u){const f=r.lodInfos.get(u.key);if(f&&f.lods){a.min_count=Math.min(a.min_count,f.lods.length),a.max_count=Math.max(a.max_count,f.lods.length);for(let m=0;m<f.lods.length;m++){const v=f.lods[m];v.width&&(a.lods[m]=a.lods[m]||{min_height:1/0,max_height:0},a.lods[m].min_height=Math.min(a.lods[m].min_height,v.height),a.lods[m].max_height=Math.max(a.lods[m].max_height,v.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 i=t[s];if(i&&i.isTexture&&this.hasLODLevelAvailable(i,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 d.Mesh||t.isMesh===!0){const r=t.geometry,n=this.getAssignedLODInformation(r);if(!n)return Promise.resolve(null);for(const s of E)s.onBeforeGetLODMesh?.(t,e);return t["LOD:requested level"]=e,x.getOrLoadLOD(r,e).then(s=>{if(Array.isArray(s)){const i=n.index||0;s=s[i]}return t["LOD:requested level"]===e&&(delete t["LOD:requested level"],s&&r!=s&&(s?.isBufferGeometry?t.geometry=s:p&&console.error("Invalid LOD geometry",s))),s}).catch(s=>(console.error("Error loading mesh LOD",t,s),null))}else p&&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 i=this.assignTextureLOD(s,e);n.push(i)}return Promise.all(n).then(s=>{const i=new Array;for(const l of s)Array.isArray(l)&&i.push(...l);return i})}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 i=r;for(const l of Object.keys(i.uniforms)){const a=i.uniforms[l].value;if(a?.isTexture===!0){const u=this.assignTextureLODForSlot(a,e,r,l).then(f=>(f&&i.uniforms[l].value!=f&&(i.uniforms[l].value=f,i.uniformsNeedUpdate=!0),f));n.push(u),s.push(l)}}}else for(const i of Object.keys(r)){const l=r[i];if(l?.isTexture===!0){const a=this.assignTextureLODForSlot(l,e,r,i);n.push(a),s.push(i)}}return Promise.all(n).then(i=>{const l=new Array;for(let a=0;a<i.length;a++){const u=i[a],f=s[a];u&&u.isTexture===!0?l.push({material:r,slot:f,texture:u,level:e}):l.push({material:r,slot:f,texture:null,level:e})}return l})}if(t instanceof d.Texture||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):x.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 i=r[n];if(i&&!p){const l=this.getAssignedLODInformation(i);if(l&&l?.level<e)return p==="verbose"&&console.warn("Assigned texture level is already higher: ",l.level,e,r,i,s),null}if(lt&&s.mipmaps){const l=s.mipmaps.length;s.mipmaps.length=Math.min(s.mipmaps.length,3),l!==s.mipmaps.length&&p&&console.debug(`Reduced mipmap count from ${l} to ${s.mipmaps.length} for ${s.uuid}: ${s.image?.width}x${s.image?.height}.`)}r[n]=s}return s}else p=="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,e){p&&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?.[$];return e?(this._isLoadingMesh=!0,this.parser.getDependency("mesh",t).then(r=>(this._isLoadingMesh=!1,r&&x.registerMesh(this.url,e.guid,r,e.lods?.length,0,e),r))):null};afterRoot(t){return p&&console.log("AFTER",this.url,t),this.parser.json.textures?.forEach((e,r)=>{if(e?.extensions){const n=e?.extensions[$];if(n){if(!n.lods){p&&console.warn("Texture has no LODs",n);return}let s=!1;for(const i of this.parser.associations.keys())i.isTexture===!0&&this.parser.associations.get(i)?.textures===r&&(s=!0,x.registerTexture(this.url,i,n.lods?.length,r,n));s||this.parser.getDependency("texture",r).then(i=>{i&&x.registerTexture(this.url,i,n.lods?.length,r,n)})}}}),this.parser.json.meshes?.forEach((e,r)=>{if(e?.extensions){const n=e?.extensions[$];if(n&&n.lods){for(const s of this.parser.associations.keys())if(s.isMesh){const i=this.parser.associations.get(s);i?.meshes===r&&x.registerMesh(this.url,n.guid,s,n.lods.length,i.primitives,n)}}}}),null}static registerTexture=(t,e,r,n,s)=>{if(!e){p&&console.error("gltf-progressive: Called register texture without texture");return}if(p){const l=e.image?.width||e.source?.data?.width||0,a=e.image?.height||e.source?.data?.height||0;console.log(`> 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[ae]=s);const i=s.guid;x.assignLODInformation(t,e,i,r,n),x.lodInfos.set(i,s),x.lowresCache.set(i,e)};static registerMesh=(t,e,r,n,s,i)=>{const l=r.geometry;if(!l){p&&console.warn("gltf-progressive: Register mesh without geometry");return}l.userData||(l.userData={}),p&&console.log("> Progressive: register mesh "+r.name,{index:s,uuid:r.uuid},i,r),x.assignLODInformation(t,l,e,n,s),x.lodInfos.set(e,i);let a=x.lowresCache.get(e);a?a.push(r.geometry):a=[r.geometry],x.lowresCache.set(e,a),n>0&&!q(r)&&Ie(r,l);for(const u of E)u.onRegisteredNewMesh?.(r,i)};static lodInfos=new Map;static previouslyLoaded=new Map;static lowresCache=new Map;static workers=[];static _workersIndex=0;static async getOrLoadLOD(t,e){const r=p=="verbose",n=this.getAssignedLODInformation(t);if(!n)return p&&console.warn(`[gltf-progressive] No LOD information found: ${t.name}, uuid: ${t.uuid}, type: ${t.type}`,t),null;const s=n?.key;let i;if(t.isTexture===!0){const a=t;a.source&&a.source[ae]&&(i=a.source[ae])}if(i||(i=x.lodInfos.get(s)),i){if(e>0){let f=!1;const m=Array.isArray(i.lods);if(m&&e>=i.lods.length?f=!0:m||(f=!0),f)return this.lowresCache.get(s)}const a=Array.isArray(i.lods)?i.lods[e]?.path:i.lods;if(!a)return p&&!i["missing:uri"]&&(i["missing:uri"]=!0,console.warn("Missing uri for progressive asset for LOD "+e,i)),null;const u=Ze(n.url,a);if(u.endsWith(".glb")||u.endsWith(".gltf")){if(!i.guid)return console.warn("missing pointer for glb/gltf texture",i),null;const f=u+"_"+i.guid,m=await this.queue.slot(u),v=this.previouslyLoaded.get(f);if(v!==void 0){r&&console.log(`LOD ${e} was already loading/loaded: ${f}`);let c=await v.catch(S=>(console.error(`Error loading LOD ${e} from ${u}
|
|
2
|
-
`,S),null)),w=!1;if(c==null||(c instanceof d.Texture&&t instanceof d.Texture?c.image?.data||c.source?.data?c=this.copySettings(t,c):(w=!0,this.previouslyLoaded.delete(f)):c instanceof d.BufferGeometry&&t instanceof d.BufferGeometry&&(c.attributes.position?.array||(w=!0,this.previouslyLoaded.delete(f)))),!w)return c}if(!m.use)return p&&console.log(`LOD ${e} was aborted: ${u}`),null;const
|
|
3
|
-
`,_),c(null)));if(!
|
|
1
|
+
"use strict";var Fe=Object.create;var Le=Object.defineProperty;var Ue=Object.getOwnPropertyDescriptor;var Ee=Object.getOwnPropertyNames;var We=Object.getPrototypeOf,Ne=Object.prototype.hasOwnProperty;var ze=(o,t,e,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of Ee(t))!Ne.call(o,n)&&n!==e&&Le(o,n,{get:()=>t[n],enumerable:!(r=Ue(t,n))||r.enumerable});return o};var Ve=(o,t,e)=>(e=o!=null?Fe(We(o)):{},ze(t||!o||!o.__esModule?Le(e,"default",{value:o,enumerable:!0}):e,o));Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const d=require("three"),pe=require("three/examples/jsm/loaders/GLTFLoader.js"),qe=require("three/examples/jsm/libs/meshopt_decoder.module.js"),je=require("three/examples/jsm/loaders/DRACOLoader.js"),Xe=require("three/examples/jsm/loaders/KTX2Loader.js");var oe=typeof document<"u"?document.currentScript:null;const De="";globalThis.GLTF_PROGRESSIVE_VERSION=De;console.debug("[gltf-progressive] version -");let k="https://www.gstatic.com/draco/versioned/decoders/1.5.7/",z="https://www.gstatic.com/basis-universal/versioned/2021-04-15-ba1c3e4/";const Ke=k,Ye=z,Oe=new URL(k+"draco_decoder.js");Oe.searchParams.append("range","true");fetch(Oe,{method:"GET",headers:{Range:"bytes=0-1"}}).catch(o=>{console.debug(`Failed to fetch remote Draco decoder from ${k} (offline: ${typeof navigator<"u"?navigator.onLine:"unknown"})`),k===Ke&&Se("./include/draco/"),z===Ye&&Te("./include/ktx2/")}).finally(()=>{Pe()});const He=()=>({dracoDecoderPath:k,ktx2TranscoderPath:z});function Se(o){k=o,A&&A[fe]!=k?(console.debug("Updating Draco decoder path to "+o),A[fe]=k,A.setDecoderPath(k),A.preload()):console.debug("Setting Draco decoder path to "+o)}function Te(o){z=o,R&&R.transcoderPath!=z?(console.debug("Updating KTX2 transcoder path to "+o),R.setTranscoderPath(z),R.init()):console.debug("Setting KTX2 transcoder path to "+o)}function re(o){return Pe(),o?R.detectSupport(o):o!==null&&console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures might fail"),{dracoLoader:A,ktx2Loader:R,meshoptDecoder:te}}function me(o){o.dracoLoader||o.setDRACOLoader(A),o.ktx2Loader||o.setKTX2Loader(R),o.meshoptDecoder||o.setMeshoptDecoder(te)}const fe=Symbol("dracoDecoderPath");let A,te,R;function Pe(){A||(A=new je.DRACOLoader,A[fe]=k,A.setDecoderPath(k),A.setDecoderConfig({type:"js"}),A.preload()),R||(R=new Xe.KTX2Loader,R.setTranscoderPath(z),R.init()),te||(te=qe.MeshoptDecoder)}const he=new WeakMap;function ye(o,t){let e=he.get(o);e?e=Object.assign(e,t):e=t,he.set(o,e)}const Qe=pe.GLTFLoader.prototype.load;function Je(...o){const t=he.get(this);let e=o[0];const r=new URL(e,window.location.href);if(r.hostname.endsWith("needle.tools")){const s=t?.progressive!==void 0?t.progressive:!0,i=t?.usecase?t.usecase:"default";s?this.requestHeader.Accept=`*/*;progressive=allowed;usecase=${i}`:this.requestHeader.Accept=`*/*;usecase=${i}`,e=r.toString()}return o[0]=e,Qe?.call(this,...o)}pe.GLTFLoader.prototype.load=Je;V("debugprogressive");function V(o){if(typeof window>"u")return!1;const e=new URL(window.location.href).searchParams.get(o);return e==null||e==="0"||e==="false"?!1:e===""?!0:e}function Ze(o,t){if(t===void 0||t.startsWith("./")||t.startsWith("http")||o===void 0)return t;const e=o.lastIndexOf("/");if(e>=0){const r=o.substring(0,e+1);for(;r.endsWith("/")&&t.startsWith("/");)t=t.substring(1);return r+t}return t}let X;function Ae(){return X!==void 0||(X=/iPhone|iPad|iPod|Android|IEMobile/i.test(navigator.userAgent),V("debugprogressive")&&console.log("[glTF Progressive]: isMobileDevice",X)),X}function _e(){if(typeof window>"u")return!1;const o=new URL(window.location.href),t=o.hostname==="localhost"||/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(o.hostname);return o.hostname==="127.0.0.1"||t}class et{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)})}}}const tt=typeof window>"u"&&typeof document>"u",ge=Symbol("needle:raycast-mesh");function q(o){return o?.[ge]instanceof d.BufferGeometry?o[ge]:null}function Ie(o,t){if((o.type==="Mesh"||o.type==="SkinnedMesh")&&!q(o)){const r=rt(t);r.userData={isRaycastMesh:!0},o[ge]=r}}function Ce(o=!0){if(o){if(K)return;const t=K=d.Mesh.prototype.raycast;d.Mesh.prototype.raycast=function(e,r){const n=this,s=q(n);let i;s&&n.isMesh&&(i=n.geometry,n.geometry=s),t.call(this,e,r),i&&(n.geometry=i)}}else{if(!K)return;d.Mesh.prototype.raycast=K,K=null}}let K=null;function rt(o){const t=new d.BufferGeometry;for(const e in o.attributes)t.setAttribute(e,o.getAttribute(e));return t.setIndex(o.getIndex()),t}const E=new Array,p=V("debugprogressive");let ee,N=-1;if(p){let o=function(){N+=1,N>=t&&(N=-1),console.log(`Toggle LOD level [${N}]`)},t=6;window.addEventListener("keyup",e=>{e.key==="p"&&o(),e.key==="w"&&(ee=!ee,console.log(`Toggle wireframe [${ee}]`));const r=parseInt(e.key);!isNaN(r)&&r>=0&&(N=r,console.log(`Set LOD level to [${N}]`))})}function ke(o){if(p)if(Array.isArray(o))for(const t of o)ke(t);else o&&"wireframe"in o&&(o.wireframe=ee===!0)}const Y=new Array;let st=0;const nt=Ae()?2:10;function ot(o){if(Y.length<nt){const r=Y.length;p&&console.warn(`[Worker] Creating new worker #${r}`);const n=xe.createWorker(o||{});return Y.push(n),n}const t=st++%Y.length;return Y[t]}class xe{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){it(n.result),s.resolve(n.result);const i=s.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(new URL("/assets/loader.worker-CwaLRBn2.js",typeof document>"u"?require("url").pathToFileURL(__filename).href:oe&&oe.tagName.toUpperCase()==="SCRIPT"&&oe.src||new URL("gltf-progressive.umd.cjs",document.baseURI).href),{type:"module"});return new xe(e,t)}_running=[];_webglRenderer=null;async load(t,e){const r=He();let n=e?.renderer;n||(this._webglRenderer??=(async()=>{const{WebGLRenderer:u}=await import("three");return new u})(),n=await this._webglRenderer);const l=re(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 a={type:"load",url:t,dracoDecoderPath:r.dracoDecoderPath,ktx2TranscoderPath:r.ktx2TranscoderPath,ktx2LoaderConfig:l};return this._debug&&console.debug("[Worker] Sending load request",a),this.worker.postMessage(a),new Promise(u=>{this._running.push({url:t.toString(),resolve:u})})}_debug=!1}function it(o){for(const t of o.geometries){const e=t.geometry,r=new d.BufferGeometry;if(r.name=e.name||"",e.index){const n=e.index;r.setIndex(ie(n))}for(const n in e.attributes){const s=e.attributes[n],i=ie(s);r.setAttribute(n,i)}if(e.morphAttributes)for(const n in e.morphAttributes){const i=e.morphAttributes[n].map(l=>ie(l));r.morphAttributes[n]=i}if(r.morphTargetsRelative=e.morphTargetsRelative??!1,r.boundingBox=new d.Box3,r.boundingBox.min=new d.Vector3(e.boundingBox?.min.x,e.boundingBox?.min.y,e.boundingBox?.min.z),r.boundingBox.max=new d.Vector3(e.boundingBox?.max.x,e.boundingBox?.max.y,e.boundingBox?.max.z),r.boundingSphere=new d.Sphere(new d.Vector3(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 o.textures){const e=t.texture;let r=null;if(e.isCompressedTexture){const n=e.mipmaps,s=e.image?.width||e.source?.data?.width||-1,i=e.image?.height||e.source?.data?.height||-1;r=new d.CompressedTexture(n,s,i,e.format,e.type,e.mapping,e.wrapS,e.wrapT,e.magFilter,e.minFilter,e.anisotropy,e.colorSpace)}else r=new d.Texture(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 d.Matrix3(...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 o}function ie(o){let t=o;if("isInterleavedBufferAttribute"in o&&o.isInterleavedBufferAttribute){const e=o.data,r=e.array,n=new d.InterleavedBuffer(r,e.stride);t=new d.InterleavedBufferAttribute(n,o.itemSize,r.byteOffset,o.normalized),t.offset=o.offset}else"isBufferAttribute"in o&&o.isBufferAttribute&&(t=new d.BufferAttribute(o.array,o.itemSize,o.normalized),t.usage=o.usage,t.gpuType=o.gpuType,t.updateRanges=o.updateRanges);return t}const at=V("gltf-progressive-worker"),lt=V("gltf-progressive-reduce-mipmaps"),ae=Symbol("needle-progressive-texture"),$="NEEDLE_progressive";class x{get name(){return $}static getMeshLODExtension(t){const e=this.getAssignedLODInformation(t);return e?.key?this.lodInfos.get(e.key):null}static getPrimitiveIndex(t){const e=this.getAssignedLODInformation(t)?.index;return e??-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(p==="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&&i(u,e)}}else if(t.isMaterial)for(const l of Object.keys(t)){const a=t[l];a?.isTexture===!0&&i(a,e)}else p&&console.warn(`[getMaterialMinMaxLODsCount] Unsupported material type: ${t.type}`);return t[n]=e,e;function i(l,a){const u=r.getAssignedLODInformation(l);if(u){const f=r.lodInfos.get(u.key);if(f&&f.lods){a.min_count=Math.min(a.min_count,f.lods.length),a.max_count=Math.max(a.max_count,f.lods.length);for(let m=0;m<f.lods.length;m++){const v=f.lods[m];v.width&&(a.lods[m]=a.lods[m]||{min_height:1/0,max_height:0},a.lods[m].min_height=Math.min(a.lods[m].min_height,v.height),a.lods[m].max_height=Math.max(a.lods[m].max_height,v.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 i=t[s];if(i&&i.isTexture&&this.hasLODLevelAvailable(i,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 d.Mesh||t.isMesh===!0){const r=t.geometry,n=this.getAssignedLODInformation(r);if(!n)return Promise.resolve(null);for(const s of E)s.onBeforeGetLODMesh?.(t,e);return t["LOD:requested level"]=e,x.getOrLoadLOD(r,e).then(s=>{if(Array.isArray(s)){const i=n.index||0;s=s[i]}return t["LOD:requested level"]===e&&(delete t["LOD:requested level"],s&&r!=s&&(s?.isBufferGeometry?t.geometry=s:p&&console.error("Invalid LOD geometry",s))),s}).catch(s=>(console.error("Error loading mesh LOD",t,s),null))}else p&&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 i=this.assignTextureLOD(s,e);n.push(i)}return Promise.all(n).then(s=>{const i=new Array;for(const l of s)Array.isArray(l)&&i.push(...l);return i})}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 i=r;for(const l of Object.keys(i.uniforms)){const a=i.uniforms[l].value;if(a?.isTexture===!0){const u=this.assignTextureLODForSlot(a,e,r,l).then(f=>(f&&i.uniforms[l].value!=f&&(i.uniforms[l].value=f,i.uniformsNeedUpdate=!0),f));n.push(u),s.push(l)}}}else for(const i of Object.keys(r)){const l=r[i];if(l?.isTexture===!0){const a=this.assignTextureLODForSlot(l,e,r,i);n.push(a),s.push(i)}}return Promise.all(n).then(i=>{const l=new Array;for(let a=0;a<i.length;a++){const u=i[a],f=s[a];u&&u.isTexture===!0?l.push({material:r,slot:f,texture:u,level:e}):l.push({material:r,slot:f,texture:null,level:e})}return l})}if(t instanceof d.Texture||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):x.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 i=r[n];if(i&&!p){const l=this.getAssignedLODInformation(i);if(l&&l?.level<e)return p==="verbose"&&console.warn("Assigned texture level is already higher: ",l.level,e,r,i,s),null}if(lt&&s.mipmaps){const l=s.mipmaps.length;s.mipmaps.length=Math.min(s.mipmaps.length,3),l!==s.mipmaps.length&&p&&console.debug(`Reduced mipmap count from ${l} to ${s.mipmaps.length} for ${s.uuid}: ${s.image?.width}x${s.image?.height}.`)}r[n]=s}return s}else p=="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;p&&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?.[$];return e?(this._isLoadingMesh=!0,this.parser.getDependency("mesh",t).then(r=>(this._isLoadingMesh=!1,r&&x.registerMesh(this.url,e.guid,r,e.lods?.length,0,e),r))):null};afterRoot(t){return p&&console.log("AFTER",this.url,t),this.parser.json.textures?.forEach((e,r)=>{if(e?.extensions){const n=e?.extensions[$];if(n){if(!n.lods){p&&console.warn("Texture has no LODs",n);return}let s=!1;for(const i of this.parser.associations.keys())i.isTexture===!0&&this.parser.associations.get(i)?.textures===r&&(s=!0,x.registerTexture(this.url,i,n.lods?.length,r,n));s||this.parser.getDependency("texture",r).then(i=>{i&&x.registerTexture(this.url,i,n.lods?.length,r,n)})}}}),this.parser.json.meshes?.forEach((e,r)=>{if(e?.extensions){const n=e?.extensions[$];if(n&&n.lods){for(const s of this.parser.associations.keys())if(s.isMesh){const i=this.parser.associations.get(s);i?.meshes===r&&x.registerMesh(this.url,n.guid,s,n.lods.length,i.primitives,n)}}}}),null}static registerTexture=(t,e,r,n,s)=>{if(!e){p&&console.error("gltf-progressive: Called register texture without texture");return}if(p){const l=e.image?.width||e.source?.data?.width||0,a=e.image?.height||e.source?.data?.height||0;console.log(`> 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[ae]=s);const i=s.guid;x.assignLODInformation(t,e,i,r,n),x.lodInfos.set(i,s),x.lowresCache.set(i,e)};static registerMesh=(t,e,r,n,s,i)=>{const l=r.geometry;if(!l){p&&console.warn("gltf-progressive: Register mesh without geometry");return}l.userData||(l.userData={}),p&&console.log("> Progressive: register mesh "+r.name,{index:s,uuid:r.uuid},i,r),x.assignLODInformation(t,l,e,n,s),x.lodInfos.set(e,i);let a=x.lowresCache.get(e);a?a.push(r.geometry):a=[r.geometry],x.lowresCache.set(e,a),n>0&&!q(r)&&Ie(r,l);for(const u of E)u.onRegisteredNewMesh?.(r,i)};static lodInfos=new Map;static previouslyLoaded=new Map;static lowresCache=new Map;static workers=[];static _workersIndex=0;static async getOrLoadLOD(t,e){const r=p=="verbose",n=this.getAssignedLODInformation(t);if(!n)return p&&console.warn(`[gltf-progressive] No LOD information found: ${t.name}, uuid: ${t.uuid}, type: ${t.type}`,t),null;const s=n?.key;let i;if(t.isTexture===!0){const a=t;a.source&&a.source[ae]&&(i=a.source[ae])}if(i||(i=x.lodInfos.get(s)),i){if(e>0){let f=!1;const m=Array.isArray(i.lods);if(m&&e>=i.lods.length?f=!0:m||(f=!0),f)return this.lowresCache.get(s)}const a=Array.isArray(i.lods)?i.lods[e]?.path:i.lods;if(!a)return p&&!i["missing:uri"]&&(i["missing:uri"]=!0,console.warn("Missing uri for progressive asset for LOD "+e,i)),null;const u=Ze(n.url,a);if(u.endsWith(".glb")||u.endsWith(".gltf")){if(!i.guid)return console.warn("missing pointer for glb/gltf texture",i),null;const f=u+"_"+i.guid,m=await this.queue.slot(u),v=this.previouslyLoaded.get(f);if(v!==void 0){r&&console.log(`LOD ${e} was already loading/loaded: ${f}`);let c=await v.catch(S=>(console.error(`Error loading LOD ${e} from ${u}
|
|
2
|
+
`,S),null)),w=!1;if(c==null||(c instanceof d.Texture&&t instanceof d.Texture?c.image?.data||c.source?.data?c=this.copySettings(t,c):(w=!0,this.previouslyLoaded.delete(f)):c instanceof d.BufferGeometry&&t instanceof d.BufferGeometry&&(c.attributes.position?.array||(w=!0,this.previouslyLoaded.delete(f)))),!w)return c}if(!m.use)return p&&console.log(`LOD ${e} was aborted: ${u}`),null;const b=i,I=new Promise(async(c,w)=>{if(at){const y=await(await ot({})).load(u);if(y.textures.length>0)for(const h of y.textures){let g=h.texture;return x.assignLODInformation(n.url,g,s,e,void 0),t instanceof d.Texture&&(g=this.copySettings(t,g)),g&&(g.guid=b.guid),c(g)}if(y.geometries.length>0){const h=new Array;for(const g of y.geometries){const D=g.geometry;x.assignLODInformation(n.url,D,s,e,g.primitiveIndex),h.push(D)}return c(h)}return c(null)}const S=new pe.GLTFLoader;me(S),p&&(await new Promise(_=>setTimeout(_,1e3)),r&&console.warn("Start loading (delayed) "+u,b.guid));let B=u;if(b&&Array.isArray(b.lods)){const _=b.lods[e];_.hash&&(B+="?v="+_.hash)}const M=await S.loadAsync(B).catch(_=>(console.error(`Error loading LOD ${e} from ${u}
|
|
3
|
+
`,_),c(null)));if(!M)return c(null);const W=M.parser;r&&console.log("Loading finished "+u,b.guid);let T=0;if(M.parser.json.textures){let _=!1;for(const y of M.parser.json.textures){if(y?.extensions){const h=y?.extensions[$];if(h?.guid&&h.guid===b.guid){_=!0;break}}T++}if(_){let y=await W.getDependency("texture",T);return y&&x.assignLODInformation(n.url,y,s,e,void 0),r&&console.log('change "'+t.name+'" → "'+y.name+'"',u,T,y,f),t instanceof d.Texture&&(y=this.copySettings(t,y)),y&&(y.guid=b.guid),c(y)}else p&&console.warn("Could not find texture with guid",b.guid,M.parser.json)}if(T=0,M.parser.json.meshes){let _=!1;for(const y of M.parser.json.meshes){if(y?.extensions){const h=y?.extensions[$];if(h?.guid&&h.guid===b.guid){_=!0;break}}T++}if(_){const y=await W.getDependency("mesh",T);if(r&&console.log(`Loaded Mesh "${y.name}"`,u,T,y,f),y.isMesh===!0){const h=y.geometry;return x.assignLODInformation(n.url,h,s,e,0),c(h)}else{const h=new Array;for(let g=0;g<y.children.length;g++){const D=y.children[g];if(D.isMesh===!0){const O=D.geometry;x.assignLODInformation(n.url,O,s,e,g),h.push(O)}}return c(h)}}else p&&console.warn("Could not find mesh with guid",b.guid,M.parser.json)}return c(null)});return this.previouslyLoaded.set(f,I),m.use(I),await I}else if(t instanceof d.Texture){r&&console.log("Load texture from uri: "+u);const m=await new d.TextureLoader().loadAsync(u);return m?(m.guid=i.guid,m.flipY=!1,m.needsUpdate=!0,m.colorSpace=t.colorSpace,r&&console.log(i,m)):p&&console.warn("failed loading",u),m}}else p&&console.warn(`Can not load LOD ${e}: no LOD info found for "${s}" ${t.name}`,t.type);return null}static maxConcurrent=50;static queue=new et(x.maxConcurrent,{debug:p!=!1});static assignLODInformation(t,e,r,n,s){if(!e)return;e.userData||(e.userData={});const i=new ut(t,r,n,s);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?(p==="verbose"&&console.debug(`Copy texture settings
|
|
4
4
|
`,t.uuid,`
|
|
5
|
-
`,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 ut{url;key;level;index;constructor(t,e,r,n){this.url=t,this.key=e,this.level=r,n!=null&&(this.index=n)}}class le{static addPromise=(t,e,r,n)=>{n.forEach(s=>{s.add(t,e,r)})};frame_start;frame_capture_end;ready;_resolve;_signal;get awaitedCount(){return this._addedCount}get resolvedCount(){return this._resolvedCount}get currentlyAwaiting(){return this._awaiting.length}_resolved=!1;_addedCount=0;_resolvedCount=0;_awaiting=[];_maxPromisesPerObject=1;constructor(t,e){const n=Math.max(e.frames??2,2);this.frame_start=t,this.frame_capture_end=t+n,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._signal?.aborted||this._currentFrame>this.frame_capture_end&&this._awaiting.length===0)&&this.resolveNow()}_seen=new WeakMap;add(t,e,r){if(this._resolved){p&&console.warn("PromiseGroup: Trying to add a promise to a resolved group, ignoring.");return}if(!(this._currentFrame>this.frame_capture_end)){if(this._maxPromisesPerObject>=1)if(this._seen.has(e)){let n=this._seen.get(e);if(n>=this._maxPromisesPerObject){p&&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 C=z("debugprogressive"),dt=z("noprogressive"),ue=Symbol("Needle:LODSManager"),de=Symbol("Needle:LODState"),U=Symbol("Needle:CurrentLOD"),P={mesh_lod:-1,texture_lod:-1};class L{static debugDrawLine;static overrideGlobalLodLevel;static getObjectLODState(t){return t[de]}static addPlugin(t){E.push(t)}static removePlugin(t){const e=E.indexOf(t);e>=0&&E.splice(e,1)}static get(t,e){if(t[ue])return console.debug("[gltf-progressive] LODsManager already exists for this renderer"),t[ue];const r=new L(t,{engine:"unknown",...e});return t[ue]=r,r}renderer;context;projectionScreenMatrix=new d.Matrix4;get plugins(){return E}targetTriangleDensity=2e5;skinnedMeshAutoUpdateBoundsInterval=30;updateInterval="auto";#e=1;pause=!1;manual=!1;_newPromiseGroups=[];_promiseGroupIds=0;awaitLoading(t){const e=this._promiseGroupIds++,r=new le(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),_e()&&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 d.Clock;#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;re(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,C&&e.#s%200===0&&console.log("FPS",Math.round(e.#r),"Interval:",e.#e));const i=t++;e.#t.call(this,r,n),e.onAfterRender(r,n,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.renderer.renderLists.get(t,0).opaque;let i=!0;if(s.length===1){const l=s[0].material;(l.name==="EffectMaterial"||l.name==="CopyShader")&&(i=!1)}if((e.parent&&e.parent.type==="CubeCamera"||r>=1&&e.type==="OrthographicCamera")&&(i=!1),i){if(dt||(this.updateInterval==="auto"?this.#r<40&&this.#e<10?(this.#e+=1,C&&console.warn("↓ Reducing LOD updates",this.#e,this.#r.toFixed(0))):this.#r>=60&&this.#e>1&&(this.#e-=1,C&&console.warn("↑ 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")){C&&(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(C==="color"&&a.material&&!a.object.progressive_debug_color){a.object.progressive_debug_color=!0;const f=Math.random()*16777215,m=new d.MeshStandardMaterial({color:f});a.object.material=m}const u=a.object;(u instanceof d.Mesh||u.isMesh)&&this.updateLODs(t,e,u,s)}const i=r.transparent;for(const a of i){const u=a.object;(u instanceof d.Mesh||u.isMesh)&&this.updateLODs(t,e,u,s)}const l=r.transmissive;for(const a of l){const u=a.object;(u instanceof d.Mesh||u.isMesh)&&this.updateLODs(t,e,u,s)}}updateLODs(t,e,r,n){r.userData||(r.userData={});let s=r[de];if(s||(s=new ct,r[de]=s),s.frames++<2)return;for(const l of E)l.onBeforeUpdateLOD?.(this.renderer,t,e,r);const i=L.overrideGlobalLodLevel!==void 0?L.overrideGlobalLodLevel:N;i>=0?(P.mesh_lod=i,P.texture_lod=i):(this.calculateLodLevel(e,r,s,n,P),P.mesh_lod=Math.round(P.mesh_lod),P.texture_lod=Math.round(P.texture_lod)),P.mesh_lod>=0&&this.loadProgressiveMeshes(r,P.mesh_lod),r.material&&P.texture_lod>=0&&this.loadProgressiveTextures(r.material,P.texture_lod,i),p&&r.material&&!r.isGizmo&&ke(r.material);for(const l of E)l.onAfterUpdatedLOD?.(this.renderer,t,e,r,P);s.lastLodLevel_Mesh=P.mesh_lod,s.lastLodLevel_Texture=P.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=x.assignTextureLOD(t,e).then(i=>{this._lodchangedlisteners.forEach(l=>l({type:"texture",level:e,object:t}))});le.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,i=x.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 le.addPromise("mesh",t,i,this._newPromiseGroups),i}return Promise.resolve(null)}_sphere=new d.Sphere;_tempBox=new d.Box3;_tempBox2=new d.Box3;tempMatrix=new d.Matrix4;_tempWorldPosition=new d.Vector3;_tempBoxSize=new d.Vector3;_tempBox2Size=new d.Vector3;static corner0=new d.Vector3;static corner1=new d.Vector3;static corner2=new d.Vector3;static corner3=new d.Vector3;static _tempPtInside=new d.Vector3;static isInside(t,e){const r=t.min,n=t.max,s=(r.x+n.x)*.5,i=(r.y+n.y)*.5;return this._tempPtInside.set(s,i,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 l=10+1,a=!1;if(C&&e["DEBUG:LOD"]!=null)return e["DEBUG:LOD"];const u=x.getMeshLODExtension(e.geometry)?.lods,f=x.getPrimitiveIndex(e.geometry),m=u&&u.length>0,v=x.getMaterialMinMaxLODsCount(e.material),M=v.min_count!==1/0&&v.min_count>=0&&v.max_count>=0;if(!m&&!M){s.mesh_lod=0,s.texture_lod=0;return}m||(a=!0,l=0);const I=this.renderer.domElement.clientHeight||this.renderer.domElement.height;let F=e.geometry.boundingBox;if(e.type==="SkinnedMesh"){const c=e;if(!c.boundingBox)c.computeBoundingBox();else if(this.skinnedMeshAutoUpdateBoundsInterval>0){if(!c[L.$skinnedMeshBoundsOffset]){const S=L.skinnedMeshBoundsFrameOffsetCounter++;c[L.$skinnedMeshBoundsOffset]=S}const w=c[L.$skinnedMeshBoundsOffset];if((r.frames+w)%this.skinnedMeshAutoUpdateBoundsInterval===0){const S=q(c),B=c.geometry;S&&(c.geometry=S),c.computeBoundingBox(),c.geometry=B}}F=c.boundingBox}if(F){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 h=t.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(h)){s.mesh_lod=0,s.texture_lod=0;return}}if(this._tempBox.copy(F),this._tempBox.applyMatrix4(e.matrixWorld),c.isPerspectiveCamera&&L.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 h=this._tempBox.min,g=this._tempBox.max;let D=h.x,O=h.y,G=g.x,j=g.y;const H=2,se=1.5,Q=(h.x+g.x)*.5,J=(h.y+g.y)*.5;D=(D-Q)*H+Q,O=(O-J)*H+J,G=(G-Q)*H+Q,j=(j-J)*H+J;const Ge=D<0&&G>0?0:Math.min(Math.abs(h.x),Math.abs(g.x)),$e=O<0&&j>0?0:Math.min(Math.abs(h.y),Math.abs(g.y)),ne=Math.max(Ge,$e);r.lastCentrality=(se-ne)*(se-ne)*(se-ne)}else r.lastCentrality=1;const w=this._tempBox.getSize(this._tempBoxSize);w.multiplyScalar(.5),screen.availHeight>0&&I>0&&w.multiplyScalar(I/screen.availHeight),t.isPerspectiveCamera?w.x*=t.aspect:t.isOrthographicCamera;const S=t.matrixWorldInverse,B=this._tempBox2;B.copy(F),B.applyMatrix4(e.matrixWorld),B.applyMatrix4(S);const b=B.getSize(this._tempBox2Size),W=Math.max(b.x,b.y);if(Math.max(w.x,w.y)!=0&&W!=0&&(w.z=b.z/Math.max(b.x,b.y)*Math.max(w.x,w.y)),r.lastScreenCoverage=Math.max(w.x,w.y,w.z),r.lastScreenspaceVolume.copy(w),r.lastScreenCoverage*=r.lastCentrality,C&&L.debugDrawLine){const h=this.tempMatrix.copy(this.projectionScreenMatrix);h.invert();const g=L.corner0,D=L.corner1,O=L.corner2,G=L.corner3;g.copy(this._tempBox.min),D.copy(this._tempBox.max),D.x=g.x,O.copy(this._tempBox.max),O.y=g.y,G.copy(this._tempBox.max);const j=(g.z+G.z)*.5;g.z=D.z=O.z=G.z=j,g.applyMatrix4(h),D.applyMatrix4(h),O.applyMatrix4(h),G.applyMatrix4(h),L.debugDrawLine(g,D,255),L.debugDrawLine(g,O,255),L.debugDrawLine(D,G,255),L.debugDrawLine(O,G,255)}let _=999;if(u&&r.lastScreenCoverage>0)for(let h=0;h<u.length;h++){const g=u[h],O=(g.densities?.[f]||g.density||1e-5)/r.lastScreenCoverage;if(f>0&&_e()&&!g.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.")),O<n){_=h;break}}_<l&&(l=_,a=!0)}if(a?s.mesh_lod=l:s.mesh_lod=r.lastLodLevel_Mesh,C&&s.mesh_lod!=r.lastLodLevel_Mesh){const w=u?.[s.mesh_lod];w&&console.debug(`Mesh LOD changed: ${r.lastLodLevel_Mesh} → ${s.mesh_lod} (density: ${w.densities?.[f].toFixed(0)}) | ${e.name}`)}if(M){const c="saveData"in globalThis.navigator&&globalThis.navigator.saveData===!0;if(r.lastLodLevel_Texture<0){if(s.texture_lod=v.max_count-1,C){const w=v.lods[v.max_count-1];C&&console.log(`First Texture LOD ${s.texture_lod} (${w.max_height}px) - ${e.name}`)}}else{const w=r.lastScreenspaceVolume.x+r.lastScreenspaceVolume.y+r.lastScreenspaceVolume.z;let S=r.lastScreenCoverage*4;this.context?.engine==="model-viewer"&&(S*=1.5);const b=I/window.devicePixelRatio*S;let W=!1;for(let T=v.lods.length-1;T>=0;T--){const _=v.lods[T];if(!(c&&_.max_height>=2048)&&!(Ae()&&_.max_height>4096)&&(_.max_height>b||!W&&T===0)){if(W=!0,s.texture_lod=T,C&&s.texture_lod<r.lastLodLevel_Texture){const y=_.max_height;console.log(`Texture LOD changed: ${r.lastLodLevel_Texture} → ${s.texture_lod} = ${y}px
|
|
6
|
-
Screensize: ${
|
|
7
|
-
${e.name}`)}break}}}}else s.texture_lod=0}}class ct{frames=0;lastLodLevel_Mesh=-1;lastLodLevel_Texture=-1;lastScreenCoverage=0;lastScreenspaceVolume=new d.Vector3;lastCentrality=0}const ve=Symbol("NEEDLE_mesh_lod"),Z=Symbol("NEEDLE_texture_lod");let ce=null;function we(){const o=ft();o&&(o.mapURLs(function(t){return
|
|
8
|
-
`,o.getAttribute("src"));let t=null,e=null,r=null;for(let n=o;n!=null;n=Object.getPrototypeOf(n)){const s=Object.getOwnPropertySymbols(n),i=s.find(u=>u.toString()=="Symbol(renderer)"),l=s.find(u=>u.toString()=="Symbol(scene)"),a=s.find(u=>u.toString()=="Symbol(needsRender)");!t&&i!=null&&(t=o[i].threeRenderer),!e&&l!=null&&(e=o[l]),!r&&a!=null&&(r=o[a])}if(t&&e){let n=function(){if(r){let i=0,l=setInterval(()=>{if(i++>5){clearInterval(l);return}r?.call(o)},300)}};console.debug("[gltf-progressive] setup model-viewer");const s=L.get(t,{engine:"model-viewer"});return L.addPlugin(new gt),s.enable(),s.addEventListener("changed",()=>{r?.call(o)}),o.addEventListener("model-visibility",i=>{i.detail.visible&&r?.call(o)}),o.addEventListener("load",()=>{n()}),()=>{s.disable()}}return null}class gt{_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[Z]==!0)return;e[Z]=!0;const r=this.tryGetCurrentGLTF(t),n=this.tryGetCurrentModelViewer(t),s=this.getUrl(n);if(s&&r&&e.material){let i=function(a){if(a[Z]==!0)return;a[Z]=!0,a.userData&&(a.userData.LOD=-1);const u=Object.keys(a);for(let f=0;f<u.length;f++){const m=u[f],v=a[m];if(v?.isTexture===!0){const
|
|
5
|
+
`,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 ut{url;key;level;index;constructor(t,e,r,n){this.url=t,this.key=e,this.level=r,n!=null&&(this.index=n)}}class le{static addPromise=(t,e,r,n)=>{n.forEach(s=>{s.add(t,e,r)})};frame_start;frame_capture_end;ready;_resolve;_signal;get awaitedCount(){return this._addedCount}get resolvedCount(){return this._resolvedCount}get currentlyAwaiting(){return this._awaiting.length}_resolved=!1;_addedCount=0;_resolvedCount=0;_awaiting=[];_maxPromisesPerObject=1;constructor(t,e){const n=Math.max(e.frames??2,2);this.frame_start=t,this.frame_capture_end=t+n,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._signal?.aborted||this._currentFrame>this.frame_capture_end&&this._awaiting.length===0)&&this.resolveNow()}_seen=new WeakMap;add(t,e,r){if(this._resolved){p&&console.warn("PromiseGroup: Trying to add a promise to a resolved group, ignoring.");return}if(!(this._currentFrame>this.frame_capture_end)){if(this._maxPromisesPerObject>=1)if(this._seen.has(e)){let n=this._seen.get(e);if(n>=this._maxPromisesPerObject){p&&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 C=V("debugprogressive"),dt=V("noprogressive"),ue=Symbol("Needle:LODSManager"),de=Symbol("Needle:LODState"),U=Symbol("Needle:CurrentLOD"),P={mesh_lod:-1,texture_lod:-1};class L{static debugDrawLine;static overrideGlobalLodLevel;static getObjectLODState(t){return t[de]}static addPlugin(t){E.push(t)}static removePlugin(t){const e=E.indexOf(t);e>=0&&E.splice(e,1)}static get(t,e){if(t[ue])return console.debug("[gltf-progressive] LODsManager already exists for this renderer"),t[ue];const r=new L(t,{engine:"unknown",...e});return t[ue]=r,r}renderer;context;projectionScreenMatrix=new d.Matrix4;get plugins(){return E}targetTriangleDensity=2e5;skinnedMeshAutoUpdateBoundsInterval=30;updateInterval="auto";#e=1;pause=!1;manual=!1;_newPromiseGroups=[];_promiseGroupIds=0;awaitLoading(t){const e=this._promiseGroupIds++,r=new le(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),_e()&&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 d.Clock;#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;re(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,C&&e.#s%200===0&&console.log("FPS",Math.round(e.#r),"Interval:",e.#e));const i=t++;e.#t.call(this,r,n),e.onAfterRender(r,n,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.renderer.renderLists.get(t,0).opaque;let i=!0;if(s.length===1){const l=s[0].material;(l.name==="EffectMaterial"||l.name==="CopyShader")&&(i=!1)}if((e.parent&&e.parent.type==="CubeCamera"||r>=1&&e.type==="OrthographicCamera")&&(i=!1),i){if(dt||(this.updateInterval==="auto"?this.#r<40&&this.#e<10?(this.#e+=1,C&&console.warn("↓ Reducing LOD updates",this.#e,this.#r.toFixed(0))):this.#r>=60&&this.#e>1&&(this.#e-=1,C&&console.warn("↑ 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")){C&&(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(C==="color"&&a.material&&!a.object.progressive_debug_color){a.object.progressive_debug_color=!0;const f=Math.random()*16777215,m=new d.MeshStandardMaterial({color:f});a.object.material=m}const u=a.object;(u instanceof d.Mesh||u.isMesh)&&this.updateLODs(t,e,u,s)}const i=r.transparent;for(const a of i){const u=a.object;(u instanceof d.Mesh||u.isMesh)&&this.updateLODs(t,e,u,s)}const l=r.transmissive;for(const a of l){const u=a.object;(u instanceof d.Mesh||u.isMesh)&&this.updateLODs(t,e,u,s)}}updateLODs(t,e,r,n){r.userData||(r.userData={});let s=r[de];if(s||(s=new ct,r[de]=s),s.frames++<2)return;for(const l of E)l.onBeforeUpdateLOD?.(this.renderer,t,e,r);const i=L.overrideGlobalLodLevel!==void 0?L.overrideGlobalLodLevel:N;i>=0?(P.mesh_lod=i,P.texture_lod=i):(this.calculateLodLevel(e,r,s,n,P),P.mesh_lod=Math.round(P.mesh_lod),P.texture_lod=Math.round(P.texture_lod)),P.mesh_lod>=0&&this.loadProgressiveMeshes(r,P.mesh_lod),r.material&&P.texture_lod>=0&&this.loadProgressiveTextures(r.material,P.texture_lod,i),p&&r.material&&!r.isGizmo&&ke(r.material);for(const l of E)l.onAfterUpdatedLOD?.(this.renderer,t,e,r,P);s.lastLodLevel_Mesh=P.mesh_lod,s.lastLodLevel_Texture=P.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=x.assignTextureLOD(t,e).then(i=>{this._lodchangedlisteners.forEach(l=>l({type:"texture",level:e,object:t}))});le.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,i=x.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 le.addPromise("mesh",t,i,this._newPromiseGroups),i}return Promise.resolve(null)}_sphere=new d.Sphere;_tempBox=new d.Box3;_tempBox2=new d.Box3;tempMatrix=new d.Matrix4;_tempWorldPosition=new d.Vector3;_tempBoxSize=new d.Vector3;_tempBox2Size=new d.Vector3;static corner0=new d.Vector3;static corner1=new d.Vector3;static corner2=new d.Vector3;static corner3=new d.Vector3;static _tempPtInside=new d.Vector3;static isInside(t,e){const r=t.min,n=t.max,s=(r.x+n.x)*.5,i=(r.y+n.y)*.5;return this._tempPtInside.set(s,i,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 l=10+1,a=!1;if(C&&e["DEBUG:LOD"]!=null)return e["DEBUG:LOD"];const u=x.getMeshLODExtension(e.geometry)?.lods,f=x.getPrimitiveIndex(e.geometry),m=u&&u.length>0,v=x.getMaterialMinMaxLODsCount(e.material),b=v.min_count!==1/0&&v.min_count>=0&&v.max_count>=0;if(!m&&!b){s.mesh_lod=0,s.texture_lod=0;return}m||(a=!0,l=0);const I=this.renderer.domElement.clientHeight||this.renderer.domElement.height;let F=e.geometry.boundingBox;if(e.type==="SkinnedMesh"){const c=e;if(!c.boundingBox)c.computeBoundingBox();else if(this.skinnedMeshAutoUpdateBoundsInterval>0){if(!c[L.$skinnedMeshBoundsOffset]){const S=L.skinnedMeshBoundsFrameOffsetCounter++;c[L.$skinnedMeshBoundsOffset]=S}const w=c[L.$skinnedMeshBoundsOffset];if((r.frames+w)%this.skinnedMeshAutoUpdateBoundsInterval===0){const S=q(c),B=c.geometry;S&&(c.geometry=S),c.computeBoundingBox(),c.geometry=B}}F=c.boundingBox}if(F){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 h=t.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(h)){s.mesh_lod=0,s.texture_lod=0;return}}if(this._tempBox.copy(F),this._tempBox.applyMatrix4(e.matrixWorld),c.isPerspectiveCamera&&L.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 h=this._tempBox.min,g=this._tempBox.max;let D=h.x,O=h.y,G=g.x,j=g.y;const H=2,se=1.5,Q=(h.x+g.x)*.5,J=(h.y+g.y)*.5;D=(D-Q)*H+Q,O=(O-J)*H+J,G=(G-Q)*H+Q,j=(j-J)*H+J;const Ge=D<0&&G>0?0:Math.min(Math.abs(h.x),Math.abs(g.x)),$e=O<0&&j>0?0:Math.min(Math.abs(h.y),Math.abs(g.y)),ne=Math.max(Ge,$e);r.lastCentrality=(se-ne)*(se-ne)*(se-ne)}else r.lastCentrality=1;const w=this._tempBox.getSize(this._tempBoxSize);w.multiplyScalar(.5),screen.availHeight>0&&I>0&&w.multiplyScalar(I/screen.availHeight),t.isPerspectiveCamera?w.x*=t.aspect:t.isOrthographicCamera;const S=t.matrixWorldInverse,B=this._tempBox2;B.copy(F),B.applyMatrix4(e.matrixWorld),B.applyMatrix4(S);const M=B.getSize(this._tempBox2Size),W=Math.max(M.x,M.y);if(Math.max(w.x,w.y)!=0&&W!=0&&(w.z=M.z/Math.max(M.x,M.y)*Math.max(w.x,w.y)),r.lastScreenCoverage=Math.max(w.x,w.y,w.z),r.lastScreenspaceVolume.copy(w),r.lastScreenCoverage*=r.lastCentrality,C&&L.debugDrawLine){const h=this.tempMatrix.copy(this.projectionScreenMatrix);h.invert();const g=L.corner0,D=L.corner1,O=L.corner2,G=L.corner3;g.copy(this._tempBox.min),D.copy(this._tempBox.max),D.x=g.x,O.copy(this._tempBox.max),O.y=g.y,G.copy(this._tempBox.max);const j=(g.z+G.z)*.5;g.z=D.z=O.z=G.z=j,g.applyMatrix4(h),D.applyMatrix4(h),O.applyMatrix4(h),G.applyMatrix4(h),L.debugDrawLine(g,D,255),L.debugDrawLine(g,O,255),L.debugDrawLine(D,G,255),L.debugDrawLine(O,G,255)}let _=999;if(u&&r.lastScreenCoverage>0)for(let h=0;h<u.length;h++){const g=u[h],O=(g.densities?.[f]||g.density||1e-5)/r.lastScreenCoverage;if(f>0&&_e()&&!g.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.")),O<n){_=h;break}}_<l&&(l=_,a=!0)}if(a?s.mesh_lod=l:s.mesh_lod=r.lastLodLevel_Mesh,C&&s.mesh_lod!=r.lastLodLevel_Mesh){const w=u?.[s.mesh_lod];w&&console.debug(`Mesh LOD changed: ${r.lastLodLevel_Mesh} → ${s.mesh_lod} (density: ${w.densities?.[f].toFixed(0)}) | ${e.name}`)}if(b){const c="saveData"in globalThis.navigator&&globalThis.navigator.saveData===!0;if(r.lastLodLevel_Texture<0){if(s.texture_lod=v.max_count-1,C){const w=v.lods[v.max_count-1];C&&console.log(`First Texture LOD ${s.texture_lod} (${w.max_height}px) - ${e.name}`)}}else{const w=r.lastScreenspaceVolume.x+r.lastScreenspaceVolume.y+r.lastScreenspaceVolume.z;let S=r.lastScreenCoverage*4;this.context?.engine==="model-viewer"&&(S*=1.5);const M=I/window.devicePixelRatio*S;let W=!1;for(let T=v.lods.length-1;T>=0;T--){const _=v.lods[T];if(!(c&&_.max_height>=2048)&&!(Ae()&&_.max_height>4096)&&(_.max_height>M||!W&&T===0)){if(W=!0,s.texture_lod=T,C&&s.texture_lod<r.lastLodLevel_Texture){const y=_.max_height;console.log(`Texture LOD changed: ${r.lastLodLevel_Texture} → ${s.texture_lod} = ${y}px
|
|
6
|
+
Screensize: ${M.toFixed(0)}px, Coverage: ${(100*r.lastScreenCoverage).toFixed(2)}%, Volume ${w.toFixed(1)}
|
|
7
|
+
${e.name}`)}break}}}}else s.texture_lod=0}}class ct{frames=0;lastLodLevel_Mesh=-1;lastLodLevel_Texture=-1;lastScreenCoverage=0;lastScreenspaceVolume=new d.Vector3;lastCentrality=0}const ve=Symbol("NEEDLE_mesh_lod"),Z=Symbol("NEEDLE_texture_lod");let ce=null;function we(){const o=ft();o&&(o.mapURLs(function(t){return be(),t}),be(),ce?.disconnect(),ce=new MutationObserver(t=>{t.forEach(e=>{e.addedNodes.forEach(r=>{r instanceof HTMLElement&&r.tagName.toLowerCase()==="model-viewer"&&Re(r)})})}),ce.observe(document,{childList:!0,subtree:!0}))}function ft(){if(typeof customElements>"u")return null;const o=customElements.get("model-viewer");return o||(customElements.whenDefined("model-viewer").then(()=>{console.debug("[gltf-progressive] model-viewer defined"),we()}),null)}function be(){if(typeof document>"u")return;document.querySelectorAll("model-viewer").forEach(t=>{Re(t)})}const Me=new WeakSet;let ht=0;function Re(o){if(!o||Me.has(o))return null;Me.add(o),console.debug("[gltf-progressive] found new model-viewer..."+ ++ht+`
|
|
8
|
+
`,o.getAttribute("src"));let t=null,e=null,r=null;for(let n=o;n!=null;n=Object.getPrototypeOf(n)){const s=Object.getOwnPropertySymbols(n),i=s.find(u=>u.toString()=="Symbol(renderer)"),l=s.find(u=>u.toString()=="Symbol(scene)"),a=s.find(u=>u.toString()=="Symbol(needsRender)");!t&&i!=null&&(t=o[i].threeRenderer),!e&&l!=null&&(e=o[l]),!r&&a!=null&&(r=o[a])}if(t&&e){let n=function(){if(r){let i=0,l=setInterval(()=>{if(i++>5){clearInterval(l);return}r?.call(o)},300)}};console.debug("[gltf-progressive] setup model-viewer");const s=L.get(t,{engine:"model-viewer"});return L.addPlugin(new gt),s.enable(),s.addEventListener("changed",()=>{r?.call(o)}),o.addEventListener("model-visibility",i=>{i.detail.visible&&r?.call(o)}),o.addEventListener("load",()=>{n()}),()=>{s.disable()}}return null}class gt{_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[Z]==!0)return;e[Z]=!0;const r=this.tryGetCurrentGLTF(t),n=this.tryGetCurrentModelViewer(t),s=this.getUrl(n);if(s&&r&&e.material){let i=function(a){if(a[Z]==!0)return;a[Z]=!0,a.userData&&(a.userData.LOD=-1);const u=Object.keys(a);for(let f=0;f<u.length;f++){const m=u[f],v=a[m];if(v?.isTexture===!0){const b=v.userData?.associations?.textures;if(b==null)continue;const I=r.parser.json.textures[b];if(!I){console.warn("Texture data not found for texture index "+b);continue}if(I?.extensions?.[$]){const F=I.extensions[$];F&&s&&x.registerTexture(s,v,F.lods.length,b,F)}}}};const l=e.material;if(Array.isArray(l))for(const a of l)i(a);else i(l)}}tryParseMeshLOD(t,e){if(e[ve]==!0)return;e[ve]=!0;const r=this.tryGetCurrentModelViewer(t),n=this.getUrl(r);if(!n)return;const s=e.userData?.gltfExtensions?.[$];if(s&&n){const i=e.uuid;x.registerMesh(n,i,e,0,s.lods.length,s)}}}function Be(...o){let t,e,r,n;switch(o.length){case 2:[r,e]=o,n={};break;case 3:[r,e,n]=o;break;case 4:[t,e,r,n]=o;break;default:throw new Error("Invalid arguments")}re(e),me(r),ye(r,{progressive:!0,...n?.hints}),r.register(i=>new x(i));const s=L.get(e);return n?.enableLODsManager!==!1&&s.enable(),s}we();if(!tt){const o={gltfProgressive:{useNeedleProgressive:Be,LODsManager:L,configureLoader:ye,getRaycastMesh:q,useRaycastMeshes:Ce}};if(!globalThis.Needle)globalThis.Needle=o;else for(const t in o)globalThis.Needle[t]=o[t]}exports.EXTENSION_NAME=$;exports.LODsManager=L;exports.NEEDLE_progressive=x;exports.VERSION=De;exports.addDracoAndKTX2Loaders=me;exports.configureLoader=ye;exports.createLoaders=re;exports.getRaycastMesh=q;exports.patchModelViewer=we;exports.registerRaycastMesh=Ie;exports.setDracoDecoderLocation=Se;exports.setKTX2TranscoderLocation=Te;exports.useNeedleProgressive=Be;exports.useRaycastMeshes=Ce;
|
package/lib/extension.d.ts
CHANGED
|
@@ -104,7 +104,7 @@ export declare class NEEDLE_progressive implements GLTFLoaderPlugin {
|
|
|
104
104
|
private static assignTextureLODForSlot;
|
|
105
105
|
private readonly parser;
|
|
106
106
|
private readonly url;
|
|
107
|
-
constructor(parser: GLTFParser
|
|
107
|
+
constructor(parser: GLTFParser);
|
|
108
108
|
private _isLoadingMesh;
|
|
109
109
|
loadMesh: (meshIndex: number) => Promise<any> | null;
|
|
110
110
|
afterRoot(gltf: GLTF): null;
|
package/lib/extension.js
CHANGED
|
@@ -371,7 +371,8 @@ export class NEEDLE_progressive {
|
|
|
371
371
|
}
|
|
372
372
|
parser;
|
|
373
373
|
url;
|
|
374
|
-
constructor(parser
|
|
374
|
+
constructor(parser) {
|
|
375
|
+
const url = parser.options.path;
|
|
375
376
|
if (debug)
|
|
376
377
|
console.log("Progressive extension registered for", url);
|
|
377
378
|
this.parser = parser;
|
package/lib/index.d.ts
CHANGED
|
@@ -7,7 +7,6 @@ export { getRaycastMesh, registerRaycastMesh, useRaycastMeshes } from "./utils.j
|
|
|
7
7
|
import { WebGLRenderer } from "three";
|
|
8
8
|
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
|
|
9
9
|
import { SmartLoadingHints } from "./loaders.js";
|
|
10
|
-
import { LODsManager } from "./lods.manager.js";
|
|
11
10
|
declare type UseNeedleGLTFProgressiveOptions = {
|
|
12
11
|
/**
|
|
13
12
|
* When set to true the LODs manager will automatically be enabled
|
|
@@ -18,19 +17,6 @@ declare type UseNeedleGLTFProgressiveOptions = {
|
|
|
18
17
|
*/
|
|
19
18
|
hints?: Omit<SmartLoadingHints, "progressive">;
|
|
20
19
|
};
|
|
21
|
-
/** Use this
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
* @param loader The gltf loader.
|
|
25
|
-
* @param opts Options.
|
|
26
|
-
* @returns The LODsManager instance.
|
|
27
|
-
* @example In react-three-fiber:
|
|
28
|
-
* ```ts
|
|
29
|
-
* const url = 'https://yourdomain.com/yourmodel.glb'
|
|
30
|
-
* const { scene } = useGLTF(url, false, false, (loader) => {
|
|
31
|
-
* useNeedleGLTFProgressive(url, gl, loader)
|
|
32
|
-
* })
|
|
33
|
-
* return <primitive object={scene} />
|
|
34
|
-
* ```
|
|
35
|
-
*/
|
|
36
|
-
export declare function useNeedleProgressive(url: string, renderer: WebGLRenderer, loader: GLTFLoader, opts?: UseNeedleGLTFProgressiveOptions): LODsManager;
|
|
20
|
+
/** @deprecated Use `useNeedleProgressive(loader, renderer)` - this method will be removed in gltf-progressive 4 */
|
|
21
|
+
export declare function useNeedleProgressive(url: string, renderer: WebGLRenderer, loader: GLTFLoader, opts?: UseNeedleGLTFProgressiveOptions): any;
|
|
22
|
+
export declare function useNeedleProgressive(loader: GLTFLoader, renderer: WebGLRenderer, opts?: UseNeedleGLTFProgressiveOptions): any;
|
package/lib/index.js
CHANGED
|
@@ -13,7 +13,15 @@ import { LODsManager } from "./lods.manager.js";
|
|
|
13
13
|
* @param loader The gltf loader.
|
|
14
14
|
* @param opts Options.
|
|
15
15
|
* @returns The LODsManager instance.
|
|
16
|
-
*
|
|
16
|
+
*
|
|
17
|
+
* @example Usage with vanilla three.js:
|
|
18
|
+
* ```ts
|
|
19
|
+
* const url = 'https://yourdomain.com/yourmodel.glb'
|
|
20
|
+
* const loader = new GLTFLoader()
|
|
21
|
+
* useNeedleProgressive(url, renderer, loader)
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* @example Usage with react-three-fiber:
|
|
17
25
|
* ```ts
|
|
18
26
|
* const url = 'https://yourdomain.com/yourmodel.glb'
|
|
19
27
|
* const { scene } = useGLTF(url, false, false, (loader) => {
|
|
@@ -22,14 +30,32 @@ import { LODsManager } from "./lods.manager.js";
|
|
|
22
30
|
* return <primitive object={scene} />
|
|
23
31
|
* ```
|
|
24
32
|
*/
|
|
25
|
-
export function useNeedleProgressive(
|
|
33
|
+
export function useNeedleProgressive(...args) {
|
|
34
|
+
let url;
|
|
35
|
+
let renderer;
|
|
36
|
+
let loader;
|
|
37
|
+
let opts;
|
|
38
|
+
switch (args.length) {
|
|
39
|
+
case 2:
|
|
40
|
+
[loader, renderer] = args;
|
|
41
|
+
opts = {};
|
|
42
|
+
break;
|
|
43
|
+
case 3:
|
|
44
|
+
[loader, renderer, opts] = args;
|
|
45
|
+
break;
|
|
46
|
+
case 4: // legacy
|
|
47
|
+
[url, renderer, loader, opts] = args;
|
|
48
|
+
break;
|
|
49
|
+
default:
|
|
50
|
+
throw new Error("Invalid arguments");
|
|
51
|
+
}
|
|
26
52
|
createLoaders(renderer);
|
|
27
53
|
addDracoAndKTX2Loaders(loader);
|
|
28
54
|
configureLoader(loader, {
|
|
29
55
|
progressive: true,
|
|
30
56
|
...opts?.hints,
|
|
31
57
|
});
|
|
32
|
-
loader.register(p => new NEEDLE_progressive(p
|
|
58
|
+
loader.register(p => new NEEDLE_progressive(p));
|
|
33
59
|
const lod = LODsManager.get(renderer);
|
|
34
60
|
if (opts?.enableLODsManager !== false) {
|
|
35
61
|
lod.enable();
|
package/lib/version.js
CHANGED
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
// import { GLTFLoader } from "https://cdn.jsdelivr.net/npm/three@0.179.1/examples/jsm/loaders/GLTFLoader.js";
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
import { DRACOLoader } from 'https://esm.sh/three@0.168.0/examples/jsm/loaders/DRACOLoader.js';
|
|
9
|
-
/** @ts-ignore */
|
|
10
|
-
import { KTX2Loader } from 'https://esm.sh/three@0.168.0/examples/jsm/loaders/KTX2Loader.js';
|
|
3
|
+
import { MeshoptDecoder } from 'three/examples/jsm/libs/meshopt_decoder.module.js';
|
|
4
|
+
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js';
|
|
5
|
+
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'
|
|
6
|
+
import { KTX2Loader } from 'three/examples/jsm/loaders/KTX2Loader.js';
|
|
7
|
+
|
|
11
8
|
|
|
12
9
|
|
|
13
10
|
let debug = false;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@needle-tools/gltf-progressive",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.2.0-next.613b13f",
|
|
4
4
|
"description": "three.js support for loading glTF or GLB files that contain progressive loading data",
|
|
5
5
|
"homepage": "https://needle.tools",
|
|
6
6
|
"author": {
|