@needle-tools/gltf-progressive 3.6.0-beta.1 → 3.6.0-canary.51f6448
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 +0 -3
- package/gltf-progressive.js +59 -60
- package/gltf-progressive.min.js +7 -7
- package/gltf-progressive.umd.cjs +1 -1
- package/lib/lods.manager.js +12 -16
- package/lib/version.js +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -4,9 +4,6 @@ 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.6.0-beta.1] - 2026-06-09
|
|
8
|
-
- Fix: texture LOD selection regressed for meshes that only have texture LODs (no mesh LODs); screen coverage is now computed unconditionally so texture LODs update with camera distance again.
|
|
9
|
-
|
|
10
7
|
## [3.6.0-alpha.3] - 2026-05-27
|
|
11
8
|
- Fix: stale progressive mesh and texture LOD requests no longer overwrite newer explicit targets, while still allowing useful intermediate LODs to apply during rapid target changes.
|
|
12
9
|
- Fix: progressive worker/debug imports no longer assume `window` exists, and texture LOD selection now uses the renderer pixel ratio so main-thread and worker/offscreen renderers can make matching LOD decisions.
|
package/gltf-progressive.js
CHANGED
|
@@ -376,7 +376,7 @@ function fe(i) {
|
|
|
376
376
|
const Pt = V("gltf-progressive-worker");
|
|
377
377
|
V("gltf-progressive-reduce-mipmaps");
|
|
378
378
|
const se = V("gltf-progressive-gc"), de = /* @__PURE__ */ Symbol("needle-progressive-texture"), U = "NEEDLE_progressive";
|
|
379
|
-
class
|
|
379
|
+
class p {
|
|
380
380
|
/** The name of the extension */
|
|
381
381
|
get name() {
|
|
382
382
|
return U;
|
|
@@ -509,7 +509,7 @@ class g {
|
|
|
509
509
|
a.onBeforeGetLODMesh?.(t, e);
|
|
510
510
|
t["LOD:requested level"] = e;
|
|
511
511
|
const o = () => t["LOD:requested level"] === e || this.shouldApplyStaleMeshLOD(t, e);
|
|
512
|
-
return
|
|
512
|
+
return p.getOrLoadLOD(r, e, {
|
|
513
513
|
isCurrent: o
|
|
514
514
|
}).then((a) => {
|
|
515
515
|
if (Array.isArray(a)) {
|
|
@@ -581,10 +581,10 @@ class g {
|
|
|
581
581
|
* @default 50 on desktop, 20 on mobile devices
|
|
582
582
|
*/
|
|
583
583
|
static set maxConcurrentLoadingTasks(t) {
|
|
584
|
-
|
|
584
|
+
p.queue.maxConcurrent = t;
|
|
585
585
|
}
|
|
586
586
|
static get maxConcurrentLoadingTasks() {
|
|
587
|
-
return
|
|
587
|
+
return p.queue.maxConcurrent;
|
|
588
588
|
}
|
|
589
589
|
// #region INTERNAL
|
|
590
590
|
static assignTextureLODForSlot(t, e, s, r, n) {
|
|
@@ -600,7 +600,7 @@ class g {
|
|
|
600
600
|
if (c && c.level === e && c.force === n)
|
|
601
601
|
return c.promise;
|
|
602
602
|
}
|
|
603
|
-
const a = s && r ? this.nextTextureSlotRequestId(s, r, e, n) : 0, l = () => !s || !r || this.getLatestTextureSlotRequest(s, r)?.id === a, u = () => l() || this.shouldApplyStaleTextureSlotLOD(s, r, e, n), f =
|
|
603
|
+
const a = s && r ? this.nextTextureSlotRequestId(s, r, e, n) : 0, l = () => !s || !r || this.getLatestTextureSlotRequest(s, r)?.id === a, u = () => l() || this.shouldApplyStaleTextureSlotLOD(s, r, e, n), f = p.getOrLoadLOD(t, e, {
|
|
604
604
|
isCurrent: u
|
|
605
605
|
}).then((c) => {
|
|
606
606
|
if (!l() && !this.shouldApplyStaleTextureSlotLOD(s, r, e, n)) return null;
|
|
@@ -610,9 +610,9 @@ class g {
|
|
|
610
610
|
if (c != t && s && r) {
|
|
611
611
|
const d = this.getMaterialTextureSlot(s, r) ?? t;
|
|
612
612
|
if (d && !n) {
|
|
613
|
-
const
|
|
614
|
-
if (
|
|
615
|
-
return h === "verbose" && console.warn("Assigned texture level is already higher: ",
|
|
613
|
+
const g = this.getAssignedLODInformation(d);
|
|
614
|
+
if (g && g?.level < e)
|
|
615
|
+
return h === "verbose" && console.warn("Assigned texture level is already higher: ", g.level, e, s, d, c), null;
|
|
616
616
|
}
|
|
617
617
|
this.assignTrackedTextureSlot(s, r, c);
|
|
618
618
|
}
|
|
@@ -718,7 +718,7 @@ class g {
|
|
|
718
718
|
loadMesh = (t) => {
|
|
719
719
|
if (this._isLoadingMesh) return null;
|
|
720
720
|
const e = this.parser.json.meshes[t]?.extensions?.[U];
|
|
721
|
-
return e ? (this._isLoadingMesh = !0, this.parser.getDependency("mesh", t).then((s) => (this._isLoadingMesh = !1, s &&
|
|
721
|
+
return e ? (this._isLoadingMesh = !0, this.parser.getDependency("mesh", t).then((s) => (this._isLoadingMesh = !1, s && p.registerMesh(this.url, e.guid, s, e.lods?.length, 0, e), s))) : null;
|
|
722
722
|
};
|
|
723
723
|
// private _isLoadingTexture;
|
|
724
724
|
// loadTexture = (textureIndex: number) => {
|
|
@@ -745,9 +745,9 @@ class g {
|
|
|
745
745
|
}
|
|
746
746
|
let n = !1;
|
|
747
747
|
for (const o of this.parser.associations.keys())
|
|
748
|
-
o.isTexture === !0 && this.parser.associations.get(o)?.textures === s && (n = !0,
|
|
748
|
+
o.isTexture === !0 && this.parser.associations.get(o)?.textures === s && (n = !0, p.registerTexture(this.url, o, r.lods?.length, s, r));
|
|
749
749
|
n || this.parser.getDependency("texture", s).then((o) => {
|
|
750
|
-
o &&
|
|
750
|
+
o && p.registerTexture(this.url, o, r.lods?.length, s, r);
|
|
751
751
|
});
|
|
752
752
|
}
|
|
753
753
|
}
|
|
@@ -758,7 +758,7 @@ class g {
|
|
|
758
758
|
for (const n of this.parser.associations.keys())
|
|
759
759
|
if (n.isMesh) {
|
|
760
760
|
const o = this.parser.associations.get(n);
|
|
761
|
-
o?.meshes === s &&
|
|
761
|
+
o?.meshes === s && p.registerMesh(this.url, r.guid, n, r.lods.length, o.primitives, r);
|
|
762
762
|
}
|
|
763
763
|
}
|
|
764
764
|
}
|
|
@@ -786,7 +786,7 @@ class g {
|
|
|
786
786
|
}
|
|
787
787
|
e.source && (e.source[de] = n);
|
|
788
788
|
const o = n.guid;
|
|
789
|
-
|
|
789
|
+
p.assignLODInformation(t, e, o, s, r), p.lodInfos.set(o, n), p.lowresCache.set(o, new WeakRef(e));
|
|
790
790
|
};
|
|
791
791
|
/**
|
|
792
792
|
* Register a mesh with progressive LOD information. This associates the mesh geometry with its LOD extension data
|
|
@@ -807,9 +807,9 @@ class g {
|
|
|
807
807
|
h && console.warn("gltf-progressive: Register mesh without geometry");
|
|
808
808
|
return;
|
|
809
809
|
}
|
|
810
|
-
a.userData || (a.userData = {}), h && console.log("> Progressive: register mesh " + s.name, { index: n, uuid: s.uuid }, o, s),
|
|
811
|
-
let u =
|
|
812
|
-
u ? u.push(s.geometry) : u = [s.geometry],
|
|
810
|
+
a.userData || (a.userData = {}), h && console.log("> Progressive: register mesh " + s.name, { index: n, uuid: s.uuid }, o, s), p.assignLODInformation(t, a, e, r, n), p.lodInfos.set(e, o);
|
|
811
|
+
let u = p.lowresCache.get(e)?.deref();
|
|
812
|
+
u ? u.push(s.geometry) : u = [s.geometry], p.lowresCache.set(e, new WeakRef(u)), r > 0 && !ne(s) && Mt(s, a);
|
|
813
813
|
for (const f of F)
|
|
814
814
|
f.onRegisteredNewMesh?.(s, o);
|
|
815
815
|
};
|
|
@@ -884,9 +884,9 @@ class g {
|
|
|
884
884
|
* The held value is the cache key string used in `previouslyLoaded`.
|
|
885
885
|
*/
|
|
886
886
|
static _resourceRegistry = new FinalizationRegistry((t) => {
|
|
887
|
-
const e =
|
|
887
|
+
const e = p.cache.get(t);
|
|
888
888
|
(h || se) && console.debug(`[gltf-progressive] Memory: Resource GC'd
|
|
889
|
-
${t}`), e instanceof WeakRef && (e.deref() || (
|
|
889
|
+
${t}`), e instanceof WeakRef && (e.deref() || (p.cache.delete(t), (h || se) && console.log("[gltf-progressive] ↪ Cache entry deleted (GC)")));
|
|
890
890
|
});
|
|
891
891
|
/**
|
|
892
892
|
* Track texture usage by incrementing reference count
|
|
@@ -928,16 +928,16 @@ ${e}`);
|
|
|
928
928
|
const u = t;
|
|
929
929
|
u.source && u.source[de] && (a = u.source[de]);
|
|
930
930
|
}
|
|
931
|
-
if (a || (a =
|
|
932
|
-
h && console.warn(`Can not load LOD ${e}: no LOD info found for "${o}" ${t.name}`, t.type,
|
|
931
|
+
if (a || (a = p.lodInfos.get(o)), !a)
|
|
932
|
+
h && console.warn(`Can not load LOD ${e}: no LOD info found for "${o}" ${t.name}`, t.type, p.lodInfos);
|
|
933
933
|
else {
|
|
934
934
|
if (e > 0) {
|
|
935
935
|
let c = !1;
|
|
936
936
|
const d = Array.isArray(a.lods);
|
|
937
937
|
if (d && e >= a.lods.length ? c = !0 : d || (c = !0), c) {
|
|
938
|
-
const
|
|
939
|
-
if (
|
|
940
|
-
const b =
|
|
938
|
+
const g = this.lowresCache.get(o);
|
|
939
|
+
if (g) {
|
|
940
|
+
const b = g.deref();
|
|
941
941
|
if (b) return b;
|
|
942
942
|
this.lowresCache.delete(o), h && console.log(`[gltf-progressive] Lowres cache entry was GC'd: ${o}`);
|
|
943
943
|
}
|
|
@@ -955,12 +955,12 @@ ${e}`);
|
|
|
955
955
|
if (d.found) return d.value;
|
|
956
956
|
if (s?.isCurrent?.() === !1)
|
|
957
957
|
return r && console.log(`Skipping stale LOD ${e} request before queue: ${f}`), null;
|
|
958
|
-
const
|
|
958
|
+
const g = await this.queue.slot(f);
|
|
959
959
|
if (s?.isCurrent?.() === !1)
|
|
960
960
|
return r && console.log(`Skipping stale LOD ${e} request after queue: ${f}`), null;
|
|
961
961
|
const b = await this.tryResolveLODCacheEntry(this.cache.get(c), c, f, t, e, r);
|
|
962
962
|
if (b.found) return b.value;
|
|
963
|
-
if (!
|
|
963
|
+
if (!g.use)
|
|
964
964
|
return h && console.log(`LOD ${e} was aborted: ${f}`), null;
|
|
965
965
|
const y = a, x = new Promise(async (L, z) => {
|
|
966
966
|
if (Pt) {
|
|
@@ -968,13 +968,13 @@ ${e}`);
|
|
|
968
968
|
if (m.textures.length > 0)
|
|
969
969
|
for (const D of m.textures) {
|
|
970
970
|
let M = D.texture;
|
|
971
|
-
return
|
|
971
|
+
return p.assignLODInformation(n.url, M, o, e, void 0), t instanceof q && (M = this.copySettings(t, M)), M && (M.guid = y.guid), L(M);
|
|
972
972
|
}
|
|
973
973
|
if (m.geometries.length > 0) {
|
|
974
974
|
const D = new Array();
|
|
975
975
|
for (const M of m.geometries) {
|
|
976
976
|
const G = M.geometry;
|
|
977
|
-
|
|
977
|
+
p.assignLODInformation(n.url, G, o, e, M.primitiveIndex), D.push(G);
|
|
978
978
|
}
|
|
979
979
|
return L(D);
|
|
980
980
|
}
|
|
@@ -1008,7 +1008,7 @@ ${e}`);
|
|
|
1008
1008
|
}
|
|
1009
1009
|
if (_) {
|
|
1010
1010
|
let m = await P.getDependency("texture", S);
|
|
1011
|
-
return m &&
|
|
1011
|
+
return m && p.assignLODInformation(n.url, m, o, e, void 0), r && console.log('change "' + t.name + '" → "' + m.name + '"', f, S, m, c), t instanceof q && (m = this.copySettings(t, m)), m && (m.guid = y.guid), L(m);
|
|
1012
1012
|
} else h && console.warn("Could not find texture with guid", y.guid, O.parser.json);
|
|
1013
1013
|
}
|
|
1014
1014
|
if (S = 0, O.parser.json.meshes) {
|
|
@@ -1027,14 +1027,14 @@ ${e}`);
|
|
|
1027
1027
|
const m = await P.getDependency("mesh", S);
|
|
1028
1028
|
if (r && console.log(`Loaded Mesh "${m.name}"`, f, S, m, c), m.isMesh === !0) {
|
|
1029
1029
|
const D = m.geometry;
|
|
1030
|
-
return
|
|
1030
|
+
return p.assignLODInformation(n.url, D, o, e, 0), L(D);
|
|
1031
1031
|
} else {
|
|
1032
1032
|
const D = new Array();
|
|
1033
1033
|
for (let M = 0; M < m.children.length; M++) {
|
|
1034
1034
|
const G = m.children[M];
|
|
1035
1035
|
if (G.isMesh === !0) {
|
|
1036
1036
|
const ie = G.geometry;
|
|
1037
|
-
|
|
1037
|
+
p.assignLODInformation(n.url, ie, o, e, M), D.push(ie);
|
|
1038
1038
|
}
|
|
1039
1039
|
}
|
|
1040
1040
|
return L(D);
|
|
@@ -1043,9 +1043,9 @@ ${e}`);
|
|
|
1043
1043
|
}
|
|
1044
1044
|
return L(null);
|
|
1045
1045
|
});
|
|
1046
|
-
this.cache.set(c, x),
|
|
1046
|
+
this.cache.set(c, x), g.use(x);
|
|
1047
1047
|
const w = await x;
|
|
1048
|
-
return w != null ? w instanceof q ? (this.cache.set(c, new WeakRef(w)),
|
|
1048
|
+
return w != null ? w instanceof q ? (this.cache.set(c, new WeakRef(w)), p._resourceRegistry.register(w, c)) : Array.isArray(w) ? this.cache.set(c, Promise.resolve(w)) : this.cache.set(c, Promise.resolve(w)) : this.cache.set(c, Promise.resolve(null)), w;
|
|
1049
1049
|
} else if (t instanceof q) {
|
|
1050
1050
|
if (s?.isCurrent?.() === !1)
|
|
1051
1051
|
return r && console.log(`Skipping stale texture LOD ${e} request: ${f}`), null;
|
|
@@ -1234,47 +1234,46 @@ function Ut(i) {
|
|
|
1234
1234
|
xrEnabled: l = !1,
|
|
1235
1235
|
debugDrawLine: u,
|
|
1236
1236
|
warnMissingPrimitiveDensities: f = !1
|
|
1237
|
-
} = i, c =
|
|
1237
|
+
} = i, c = p.getMeshLODExtension(t)?.lods, d = p.getPrimitiveIndex(t), g = i.target ?? {
|
|
1238
1238
|
level: a,
|
|
1239
1239
|
primitiveIndex: d,
|
|
1240
1240
|
screenCoverage: 0,
|
|
1241
1241
|
screenspaceVolume: new k(),
|
|
1242
1242
|
centrality: 1
|
|
1243
1243
|
};
|
|
1244
|
-
|
|
1244
|
+
if (g.level = a, g.primitiveIndex = d, g.screenCoverage = 0, g.screenspaceVolume.set(0, 0, 0), g.centrality = 1, !c?.length) return g;
|
|
1245
1245
|
let b = i.boundingBox ?? t.boundingBox;
|
|
1246
|
-
if (b || (t.computeBoundingBox(), b = t.boundingBox), !b) return
|
|
1246
|
+
if (b || (t.computeBoundingBox(), b = t.boundingBox), !b) return g;
|
|
1247
1247
|
if (pe.copy(b).applyMatrix4(e), s.isPerspectiveCamera && Ft(pe, r))
|
|
1248
|
-
return
|
|
1248
|
+
return g.level = 0, g.screenCoverage = 1 / 0, g.screenspaceVolume.set(1 / 0, 1 / 0, 1 / 0), g;
|
|
1249
1249
|
if (N.copy(pe).applyMatrix4(r), l && s.isPerspectiveCamera && s.fov > 70) {
|
|
1250
1250
|
const v = N.min, T = N.max;
|
|
1251
1251
|
let O = v.x, P = v.y, S = T.x, _ = T.y;
|
|
1252
1252
|
const m = 2, D = 1.5, M = (v.x + T.x) * 0.5, G = (v.y + T.y) * 0.5;
|
|
1253
1253
|
O = (O - M) * m + M, P = (P - G) * m + G, S = (S - M) * m + M, _ = (_ - G) * m + G;
|
|
1254
1254
|
const ie = O < 0 && S > 0 ? 0 : Math.min(Math.abs(v.x), Math.abs(T.x)), Ve = P < 0 && _ > 0 ? 0 : Math.min(Math.abs(v.y), Math.abs(T.y)), ce = Math.max(ie, Ve);
|
|
1255
|
-
|
|
1255
|
+
g.centrality = (D - ce) * (D - ce) * (D - ce);
|
|
1256
1256
|
}
|
|
1257
1257
|
const y = N.getSize(Gt);
|
|
1258
1258
|
y.multiplyScalar(0.5), globalThis.screen?.availHeight > 0 && o > 0 && y.multiplyScalar(o / globalThis.screen.availHeight), s.isPerspectiveCamera && (y.x *= s.aspect), Re.copy(b).applyMatrix4(e).applyMatrix4(s.matrixWorldInverse);
|
|
1259
1259
|
const x = Re.getSize(qt), w = Math.max(y.x, y.y), L = Math.max(x.x, x.y);
|
|
1260
1260
|
w !== 0 && L !== 0 && (y.z = x.z / L * w);
|
|
1261
|
-
const z = Math.max(y.x, y.y, y.z) *
|
|
1262
|
-
if (
|
|
1261
|
+
const z = Math.max(y.x, y.y, y.z) * g.centrality;
|
|
1262
|
+
if (g.screenCoverage = z, g.screenspaceVolume.copy(y), z <= 0) return g;
|
|
1263
1263
|
if (u) {
|
|
1264
1264
|
const v = Wt.copy(r);
|
|
1265
1265
|
v.invert(), W.copy(N.min), H.copy(N.max), H.x = W.x, Q.copy(N.max), Q.y = W.y, J.copy(N.max);
|
|
1266
1266
|
const T = (W.z + J.z) * 0.5;
|
|
1267
1267
|
W.z = H.z = Q.z = J.z = T, W.applyMatrix4(v), H.applyMatrix4(v), Q.applyMatrix4(v), J.applyMatrix4(v), u(W, H, 255), u(W, Q, 255), u(H, J, 255), u(Q, J, 255);
|
|
1268
1268
|
}
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
break;
|
|
1275
|
-
}
|
|
1269
|
+
for (let v = 0; v < c.length; v++) {
|
|
1270
|
+
const T = c[v], O = T.densities?.[d] || T.density || 1e-5;
|
|
1271
|
+
if (d > 0 && f && Fe() && !T.densities && !globalThis["NEEDLE:MISSING_LOD_PRIMITIVE_DENSITIES"] && (globalThis["NEEDLE:MISSING_LOD_PRIMITIVE_DENSITIES"] = !0, console.warn("[Needle Progressive] Detected usage of mesh without primitive densities. This might cause incorrect LOD level selection: Consider re-optimizing your model by updating your Needle Integration, Needle glTF Pipeline or running optimization again on Needle Cloud.")), O / z < n) {
|
|
1272
|
+
g.level = v;
|
|
1273
|
+
break;
|
|
1276
1274
|
}
|
|
1277
|
-
|
|
1275
|
+
}
|
|
1276
|
+
return g;
|
|
1278
1277
|
}
|
|
1279
1278
|
class I {
|
|
1280
1279
|
/**
|
|
@@ -1587,7 +1586,7 @@ class I {
|
|
|
1587
1586
|
const n = s !== void 0 && s >= 0;
|
|
1588
1587
|
if (n && (r = t[E] != s, e = s), r) {
|
|
1589
1588
|
t[E] = e;
|
|
1590
|
-
const o = n ? { force: !0 } : void 0, a =
|
|
1589
|
+
const o = n ? { force: !0 } : void 0, a = p.assignTextureLOD(t, e, o).then((l) => {
|
|
1591
1590
|
this._lodchangedlisteners.forEach((u) => u({ type: "texture", level: e, object: t }));
|
|
1592
1591
|
});
|
|
1593
1592
|
oe.addPromise("texture", t, a, this._newPromiseGroups);
|
|
@@ -1605,7 +1604,7 @@ class I {
|
|
|
1605
1604
|
const r = t["DEBUG:LOD"];
|
|
1606
1605
|
if (r != null && (s = t[E] != r, e = r), s) {
|
|
1607
1606
|
t[E] = e;
|
|
1608
|
-
const n = t.geometry, o =
|
|
1607
|
+
const n = t.geometry, o = p.assignMeshLOD(t, e).then((a) => (a && t[E] == e && n != t.geometry && this._lodchangedlisteners.forEach((l) => l({ type: "mesh", level: e, object: t })), a));
|
|
1609
1608
|
return oe.addPromise("mesh", t, o, this._newPromiseGroups), o;
|
|
1610
1609
|
}
|
|
1611
1610
|
return Promise.resolve(null);
|
|
@@ -1628,8 +1627,8 @@ class I {
|
|
|
1628
1627
|
let a = 10 + 1, l = !1;
|
|
1629
1628
|
if (A && e["DEBUG:LOD"] != null)
|
|
1630
1629
|
return e["DEBUG:LOD"];
|
|
1631
|
-
const u =
|
|
1632
|
-
if (!c && !
|
|
1630
|
+
const u = p.getMeshLODExtension(e.geometry)?.lods, f = p.getPrimitiveIndex(e.geometry), c = u && u.length > 0, d = p.getMaterialMinMaxLODsCount(e.material), g = d.min_count !== 1 / 0 && d.min_count >= 0 && d.max_count >= 0;
|
|
1631
|
+
if (!c && !g) {
|
|
1633
1632
|
n.mesh_lod = 0, n.texture_lod = 0;
|
|
1634
1633
|
return;
|
|
1635
1634
|
}
|
|
@@ -1685,7 +1684,7 @@ class I {
|
|
|
1685
1684
|
const w = u?.[n.mesh_lod];
|
|
1686
1685
|
w && console.log(`Mesh LOD changed: ${s.lastLodLevel_Mesh} → ${n.mesh_lod} (density: ${w.densities?.[f].toFixed(0)}) | ${e.name}`);
|
|
1687
1686
|
}
|
|
1688
|
-
if (
|
|
1687
|
+
if (g) {
|
|
1689
1688
|
const x = "saveData" in globalThis.navigator && globalThis.navigator.saveData === !0;
|
|
1690
1689
|
if (s.lastLodLevel_Texture < 0) {
|
|
1691
1690
|
if (n.texture_lod = d.max_count - 1, A) {
|
|
@@ -1832,16 +1831,16 @@ class Xt {
|
|
|
1832
1831
|
for (let f = 0; f < u.length; f++) {
|
|
1833
1832
|
const c = u[f], d = l[c];
|
|
1834
1833
|
if (d?.isTexture === !0) {
|
|
1835
|
-
const
|
|
1836
|
-
if (
|
|
1837
|
-
const b = s.parser.json.textures[
|
|
1834
|
+
const g = d.userData?.associations?.textures;
|
|
1835
|
+
if (g == null) continue;
|
|
1836
|
+
const b = s.parser.json.textures[g];
|
|
1838
1837
|
if (!b) {
|
|
1839
|
-
console.warn("Texture data not found for texture index " +
|
|
1838
|
+
console.warn("Texture data not found for texture index " + g);
|
|
1840
1839
|
continue;
|
|
1841
1840
|
}
|
|
1842
1841
|
if (b?.extensions?.[U]) {
|
|
1843
1842
|
const y = b.extensions[U];
|
|
1844
|
-
y && n &&
|
|
1843
|
+
y && n && p.registerTexture(n, d, y.lods.length, g, y);
|
|
1845
1844
|
}
|
|
1846
1845
|
}
|
|
1847
1846
|
}
|
|
@@ -1860,7 +1859,7 @@ class Xt {
|
|
|
1860
1859
|
const n = e.userData?.gltfExtensions?.[U];
|
|
1861
1860
|
if (n && r) {
|
|
1862
1861
|
const o = e.uuid;
|
|
1863
|
-
|
|
1862
|
+
p.registerMesh(r, o, e, 0, n.lods.length, n);
|
|
1864
1863
|
}
|
|
1865
1864
|
}
|
|
1866
1865
|
}
|
|
@@ -1882,7 +1881,7 @@ function jt(...i) {
|
|
|
1882
1881
|
_e(e), Ge(s), We(s, {
|
|
1883
1882
|
progressive: !0,
|
|
1884
1883
|
...r?.hints
|
|
1885
|
-
}), s.register((o) => new
|
|
1884
|
+
}), s.register((o) => new p(o));
|
|
1886
1885
|
const n = I.get(e);
|
|
1887
1886
|
return r?.enableLODsManager !== !1 && n.enable(), n;
|
|
1888
1887
|
}
|
|
@@ -1906,7 +1905,7 @@ if (!_t) {
|
|
|
1906
1905
|
export {
|
|
1907
1906
|
U as EXTENSION_NAME,
|
|
1908
1907
|
I as LODsManager,
|
|
1909
|
-
|
|
1908
|
+
p as NEEDLE_progressive,
|
|
1910
1909
|
ut as VERSION,
|
|
1911
1910
|
Ge as addDracoAndKTX2Loaders,
|
|
1912
1911
|
Ut as calculateMeshLODLevel,
|
package/gltf-progressive.min.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import*as et from"three";import{RedFormat as tt,RedIntegerFormat as rt,RGFormat as st,RGIntegerFormat as ot,RGBFormat as nt,RGBAFormat as it,RGBAIntegerFormat as at,BufferGeometry as V,Mesh as X,Box3 as ne,Vector3 as k,Sphere as ke,CompressedTexture as lt,Texture as I,Matrix3 as ut,InterleavedBuffer as ct,InterleavedBufferAttribute as dt,BufferAttribute as ht,TextureLoader as ft,Color as Pe,Matrix4 as Re}from"three";import{GLTFLoader as de}from"three/examples/jsm/loaders/GLTFLoader.js";import{MeshoptDecoder as gt}from"three/examples/jsm/libs/meshopt_decoder.module.js";import{DRACOLoader as mt}from"three/examples/jsm/loaders/DRACOLoader.js";import{KTX2Loader as pt}from"three/examples/jsm/loaders/KTX2Loader.js";const Ce="";globalThis.GLTF_PROGRESSIVE_VERSION=Ce,console.debug("[gltf-progressive] version -");let C="https://www.gstatic.com/draco/versioned/decoders/1.5.7/",H="https://cdn.needle.tools/static/three/0.179.1/basis2/";const xt=C,yt=H,Ae=new URL(C+"draco_decoder.js");Ae.searchParams.append("range","true"),fetch(Ae,{method:"GET",headers:{Range:"bytes=0-1"}}).catch(n=>{console.debug(`Failed to fetch remote Draco decoder from ${C} (offline: ${typeof navigator<"u"?navigator.onLine:"unknown"})`),C===xt&&Ee("./include/draco/"),H===yt&&Ie("./include/ktx2/")}).finally(()=>{$e()});const vt=()=>({dracoDecoderPath:C,ktx2TranscoderPath:H});function Ee(n){C=n,O&&O[fe]!=C?(console.debug("Updating Draco decoder path to "+n),O[fe]=C,O.setDecoderPath(C),O.preload()):console.debug("Setting Draco decoder path to "+n)}function Ie(n){H=n,A&&A.transcoderPath!=H?(console.debug("Updating KTX2 transcoder path to "+n),A.setTranscoderPath(H),A.init()):console.debug("Setting KTX2 transcoder path to "+n)}function ie(n){return $e(),n?A.detectSupport(n):n!==null&&console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures might fail"),{dracoLoader:O,ktx2Loader:A,meshoptDecoder:ae}}function he(n){n.dracoLoader||n.setDRACOLoader(O),n.ktx2Loader||n.setKTX2Loader(A),n.meshoptDecoder||n.setMeshoptDecoder(ae)}const fe=Symbol("dracoDecoderPath");let O,ae,A;function $e(){O||(O=new mt,O[fe]=C,O.setDecoderPath(C),O.setDecoderConfig({type:"js"}),O.preload()),A||(A=new pt,A.setTranscoderPath(H),A.init()),ae||(ae=gt)}const ge=new WeakMap;function me(n,t){let e=ge.get(n);e?e=Object.assign(e,t):e=t,ge.set(n,e)}const wt=de.prototype.load;function Lt(...n){const t=ge.get(this);let e=n[0];const r=bt(e);if(r?.hostname.endsWith("needle.tools")){const s=t?.progressive!==void 0?t.progressive:!0,o=t?.usecase?t.usecase:"default";s?this.requestHeader.Accept=`*/*;progressive=allowed;usecase=${o}`:this.requestHeader.Accept=`*/*;usecase=${o}`,e=r.toString()}return n[0]=e,wt?.call(this,...n)}de.prototype.load=Lt;function bt(n){try{if(n instanceof URL)return n;const t=globalThis.location?.href;return t?new URL(n,t):new URL(n)}catch{return null}}function Be(n){return n!=null&&n.data!=null}function pe(n){const t=n.source?.data;return t!=null&&typeof t=="object"?t:null}function Dt(n){const t=n.image;return t!=null&&typeof t=="object"?t:null}function xe(n){const t=Dt(n),e=pe(n);return{width:t?.width||e?.width||0,height:t?.height||e?.height||0}}W("debugprogressive");function W(n){const t=globalThis.location?.href;if(!t)return!1;const e=new URL(t).searchParams.get(n);return e==null||e==="0"||e==="false"?!1:e===""?!0:e}function Mt(n,t){if(t===void 0||n===void 0||t.startsWith("./")||t.startsWith("http")||t.startsWith("data:")||t.startsWith("blob:"))return t;const e=n.lastIndexOf("/");if(e>=0){const r=n.substring(0,e+1);for(;r.endsWith("/")&&t.startsWith("/");)t=t.substring(1);return r+t}return t}function ye(){if(ee!==void 0)return ee;const n=globalThis.navigator?.userAgent||"";return ee=/iPhone|iPad|iPod|Android|IEMobile/i.test(n),W("debugprogressive")&&console.log("[glTF Progressive]: isMobileDevice",ee),ee}let ee;function Ge(){const n=globalThis.location?.href;if(!n)return!1;const t=new URL(n),e=t.hostname==="localhost"||/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(t.hostname);return t.hostname==="127.0.0.1"||e}class _t{constructor(t,e={}){this.maxConcurrent=t,this.debug=e.debug??!1,typeof globalThis.requestAnimationFrame=="function"?globalThis.requestAnimationFrame(this.tick):setTimeout(this.tick,0)}_running=new Map;_queue=[];debug=!1;tick=()=>{this.internalUpdate(),setTimeout(this.tick,10)};slot(t){return this.debug&&console.debug(`[PromiseQueue]: Requesting slot for key ${t}, running: ${this._running.size}, waiting: ${this._queue.length}`),new Promise(e=>{this._queue.push({key:t,resolve:e})})}add(t,e){this._running.has(t)||(this._running.set(t,e),e.finally(()=>{this._running.delete(t),this.debug&&console.debug(`[PromiseQueue]: Promise finished now running: ${this._running.size}, waiting: ${this._queue.length}. (finished ${t})`)}),this.debug&&console.debug(`[PromiseQueue]: Added new promise, now running: ${this._running.size}, waiting: ${this._queue.length}. (added ${t})`))}internalUpdate(){const t=this.maxConcurrent-this._running.size;for(let e=0;e<t&&this._queue.length>0;e++){this.debug&&console.debug(`[PromiseQueue]: Running ${this._running.size} promises, waiting for ${this._queue.length} more.`);const{key:r,resolve:s}=this._queue.shift();s({use:o=>this.add(r,o)})}}}function Tt(n){const t=n.image,e=t?.width??0,r=t?.height??0,s=t?.depth??1,o=Math.floor(Math.log2(Math.max(e,r,s)))+1,i=St(n);return e*r*s*i*(1-Math.pow(.25,o))/(1-.25)}function St(n){let t=4;const e=n.format;e===tt||e===rt?t=1:e===st||e===ot?t=2:e===nt||e===1029?t=3:(e===it||e===at)&&(t=4);let r=1;const s=n.type;return s===1009||s===1010?r=1:s===1011||s===1012?r=2:s===1013||s===1014||s===1015?r=4:s===1016&&(r=2),t*r}const Ot=typeof window>"u"&&typeof document>"u",ve=Symbol("needle:raycast-mesh");function Y(n){return n?.[ve]instanceof V?n[ve]:null}function qe(n,t){if((n.type==="Mesh"||n.type==="SkinnedMesh")&&!Y(n)){const e=kt(t);e.userData={isRaycastMesh:!0},n[ve]=e}}function We(n=!0){if(n){if(te)return;const t=te=X.prototype.raycast;X.prototype.raycast=function(e,r){const s=this,o=Y(s);let i;o&&s.isMesh&&(i=s.geometry,s.geometry=o),t.call(this,e,r),i&&(s.geometry=i)}}else{if(!te)return;X.prototype.raycast=te,te=null}}let te=null;function kt(n){const t=new V;for(const e in n.attributes)t.setAttribute(e,n.getAttribute(e));return t.setIndex(n.getIndex()),t}const G=new Array,f=W("debugprogressive");let re,K=-1;if(f&&typeof window<"u"){let n=function(){K+=1,K>=t&&(K=-1),console.log(`Toggle LOD level [${K}]`)};const t=6;window.addEventListener("keyup",e=>{e.key==="p"&&n(),e.key==="w"&&(re=!re,console.log(`Toggle wireframe [${re}]`));const r=parseInt(e.key);!isNaN(r)&&r>=0&&(K=r,console.log(`Set LOD level to [${K}]`))})}function Ne(n){if(f&&re!==void 0)if(Array.isArray(n))for(const t of n)Ne(t);else n&&"wireframe"in n&&(n.wireframe=re===!0)}const se=new Array;let Pt=0;const Rt=ye()?2:10;function Ct(n){if(se.length<Rt){const e=se.length;f&&console.warn(`[Worker] Creating new worker #${e}`);const r=Oe.createWorker(n||{});return se.push(r),r}const t=Pt++%se.length;return se[t]}class Oe{constructor(t,e){this.worker=t,this._debug=e.debug??!1,t.onmessage=r=>{const s=r.data;switch(this._debug&&console.log("[Worker] EVENT",s),s.type){case"loaded-gltf":for(const o of this._running)if(o.url===s.result.url){At(s.result),o.resolve(s.result);const i=o.url;i.startsWith("blob:")&&URL.revokeObjectURL(i)}}},t.onerror=r=>{console.error("[Worker] Error in gltf-progressive worker:",r)},t.postMessage({type:"init"})}static async createWorker(t){const e=new Worker(new URL("/assets/gltf-progressive.worker-CCrD-Ycm.js",import.meta.url),{type:"module"});return new Oe(e,t)}_running=[];_webglRenderer=null;async load(t,e){const r=vt();let s=e?.renderer;s||(this._webglRenderer??=(async()=>{const{WebGLRenderer:a}=await import("three");return new a})(),s=await this._webglRenderer);const o=ie(s).ktx2Loader.workerConfig;if(t instanceof URL)t=t.toString();else if(t.startsWith("file:"))t=URL.createObjectURL(new Blob([t]));else if(!t.startsWith("blob:")&&!t.startsWith("http:")&&!t.startsWith("https:")){const a=globalThis.location?.href;a&&(t=new URL(t,a).toString())}const i={type:"load",url:t,dracoDecoderPath:r.dracoDecoderPath,ktx2TranscoderPath:r.ktx2TranscoderPath,ktx2LoaderConfig:o};return this._debug&&console.debug("[Worker] Sending load request",i),this.worker.postMessage(i),new Promise(a=>{this._running.push({url:t.toString(),resolve:a})})}_debug=!1}function At(n){for(const t of n.geometries){const e=t.geometry,r=new V;if(r.name=e.name||"",e.index){const s=e.index;r.setIndex(we(s))}for(const s in e.attributes){const o=e.attributes[s],i=we(o);r.setAttribute(s,i)}if(e.morphAttributes)for(const s in e.morphAttributes){const o=e.morphAttributes[s].map(i=>we(i));r.morphAttributes[s]=o}if(r.morphTargetsRelative=e.morphTargetsRelative??!1,r.boundingBox=new ne,r.boundingBox.min=new k(e.boundingBox?.min.x,e.boundingBox?.min.y,e.boundingBox?.min.z),r.boundingBox.max=new k(e.boundingBox?.max.x,e.boundingBox?.max.y,e.boundingBox?.max.z),r.boundingSphere=new ke(new k(e.boundingSphere?.center.x,e.boundingSphere?.center.y,e.boundingSphere?.center.z),e.boundingSphere?.radius),e.groups)for(const s of e.groups)r.addGroup(s.start,s.count,s.materialIndex);e.userData&&(r.userData=e.userData),t.geometry=r}for(const t of n.textures){const e=t.texture;let r=null;if(e.isCompressedTexture){const s=e.mipmaps,{width:o,height:i}=xe(e);r=new lt(s,o,i,e.format,e.type,e.mapping,e.wrapS,e.wrapT,e.magFilter,e.minFilter,e.anisotropy,e.colorSpace)}else r=new I(e.image,e.mapping,e.wrapS,e.wrapT,e.magFilter,e.minFilter,e.format,e.type,e.anisotropy,e.colorSpace),r.mipmaps=e.mipmaps,r.channel=e.channel,r.source.data=e.source.data,r.flipY=e.flipY,r.premultiplyAlpha=e.premultiplyAlpha,r.unpackAlignment=e.unpackAlignment,r.matrix=new ut(...e.matrix.elements);if(!r){console.error("[Worker] Failed to create new texture from received data. Texture is not a CompressedTexture or Texture.");continue}t.texture=r}return n}function we(n){let t=n;if("isInterleavedBufferAttribute"in n&&n.isInterleavedBufferAttribute){const e=n.data,r=e.array,s=new ct(r,e.stride);t=new dt(s,n.itemSize,r.byteOffset,n.normalized),t.offset=n.offset}else"isBufferAttribute"in n&&n.isBufferAttribute&&(t=new ht(n.array,n.itemSize,n.normalized),t.usage=n.usage,t.gpuType=n.gpuType,t.updateRanges=n.updateRanges);return t}const Et=W("gltf-progressive-worker");W("gltf-progressive-reduce-mipmaps");const oe=W("gltf-progressive-gc"),Le=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,s="LODS:minmax",o=t[s];if(o!=null)return o;if(e||(e={min_count:1/0,max_count:0,lods:[]}),Array.isArray(t)){for(const a of t)this.getMaterialMinMaxLODsCount(a,e);return t[s]=e,e}if(f==="verbose"&&console.log("getMaterialMinMaxLODsCount",t),t.type==="ShaderMaterial"||t.type==="RawShaderMaterial"){const a=t;for(const l of Object.keys(a.uniforms)){const u=a.uniforms[l].value;u?.isTexture===!0&&i(u,e)}}else if(t.isMaterial)for(const a of Object.keys(t)){const l=t[a];l?.isTexture===!0&&i(l,e)}else f&&console.warn(`[getMaterialMinMaxLODsCount] Unsupported material type: ${t.type}`);return t[s]=e,e;function i(a,l){const u=r.getAssignedLODInformation(a);if(u){const c=r.lodInfos.get(u.key);if(c&&c.lods){l.min_count=Math.min(l.min_count,c.lods.length),l.max_count=Math.max(l.max_count,c.lods.length);for(let d=0;d<c.lods.length;d++){const p=c.lods[d];p.width&&(l.lods[d]=l.lods[d]||{min_height:1/0,max_height:0},l.lods[d].min_height=Math.min(l.lods[d].min_height,p.height),l.lods[d].max_height=Math.max(l.lods[d].max_height,p.height))}}}}}static hasLODLevelAvailable(t,e){if(Array.isArray(t)){for(const o of t)if(this.hasLODLevelAvailable(o,e))return!0;return!1}if(t.isMaterial===!0){for(const o of Object.keys(t)){const i=t[o];if(i&&i.isTexture&&this.hasLODLevelAvailable(i,e))return!0}return!1}else if(t.isGroup===!0){for(const o of t.children)if(o.isMesh===!0&&this.hasLODLevelAvailable(o,e))return!0}let r,s;if(t.isMesh?r=t.geometry:(t.isBufferGeometry||t.isTexture)&&(r=t),r&&r?.userData?.LODS){const o=r.userData.LODS;if(s=this.lodInfos.get(o.key),e===void 0)return s!=null;if(s)return Array.isArray(s.lods)?e<s.lods.length:e===0}return!1}static assignMeshLOD(t,e,r){if(!t)return Promise.resolve(null);if(t instanceof X||t.isMesh===!0){const s=t.geometry,o=this.getAssignedLODInformation(s);if(!o)return Promise.resolve(null);for(const a of G)a.onBeforeGetLODMesh?.(t,e);t["LOD:requested level"]=e;const i=()=>t["LOD:requested level"]===e||this.shouldApplyStaleMeshLOD(t,e);return g.getOrLoadLOD(s,e,{isCurrent:i}).then(a=>{if(Array.isArray(a)){const u=o.index||0;a=a[u]}const l=t["LOD:requested level"]===e;return(l||this.shouldApplyStaleMeshLOD(t,e))&&(l&&delete t["LOD:requested level"],a&&s!=a&&(a?.isBufferGeometry?typeof r?.apply=="function"?r.apply(a,e,t):r?.apply!==!1&&(t.geometry=a):f&&console.error("Invalid LOD geometry",a))),a}).catch(a=>(console.error("Error loading mesh LOD",t,a),null))}else f&&console.error("Invalid call to assignMeshLOD: Request mesh LOD but the object is not a mesh",t);return Promise.resolve(null)}static assignTextureLOD(t,e=0,r){if(!t)return Promise.resolve(null);const s=r?.force===!0;if(t.isMesh===!0){const o=t;if(Array.isArray(o.material)){const i=new Array;for(const a of o.material){const l=this.assignTextureLOD(a,e,r);i.push(l)}return Promise.all(i).then(a=>{const l=new Array;for(const u of a)Array.isArray(u)&&l.push(...u);return l})}else return this.assignTextureLOD(o.material,e,r)}if(t.isMaterial===!0){const o=t,i=[],a=new Array;if(this.trackCurrentMaterialTextureSlots(o),o.uniforms&&(o.isRawShaderMaterial||o.isShaderMaterial===!0)){const l=o;for(const u of Object.keys(l.uniforms)){const c=l.uniforms[u].value;if(c?.isTexture===!0){const d=this.assignTextureLODForSlot(c,e,o,u,s).then(p=>(p&&l.uniforms[u].value!=p&&(l.uniforms[u].value=p,l.uniformsNeedUpdate=!0),p));i.push(d),a.push(u)}}}else for(const l of Object.keys(o)){const u=o[l];if(u?.isTexture===!0){const c=this.assignTextureLODForSlot(u,e,o,l,s);i.push(c),a.push(l)}}return Promise.all(i).then(l=>{const u=new Array;for(let c=0;c<l.length;c++){const d=l[c],p=a[c];d&&d.isTexture===!0?u.push({material:o,slot:p,texture:d,level:e}):u.push({material:o,slot:p,texture:null,level:e})}return u})}if(t instanceof I||t.isTexture===!0){const o=t;return this.assignTextureLODForSlot(o,e,null,null,s)}return Promise.resolve(null)}static set maxConcurrentLoadingTasks(t){g.queue.maxConcurrent=t}static get maxConcurrentLoadingTasks(){return g.queue.maxConcurrent}static assignTextureLODForSlot(t,e,r,s,o){if(t?.isTexture!==!0)return Promise.resolve(null);if(s==="glyphMap")return Promise.resolve(t);const i=this.getAssignedLODInformation(t);if(i&&(i.level===e||!o&&i.level<e))return Promise.resolve(t);if(r&&s){const d=this.getPendingTextureSlotRequest(r,s);if(d&&d.level===e&&d.force===o)return d.promise}const a=r&&s?this.nextTextureSlotRequestId(r,s,e,o):0,l=()=>!r||!s||this.getLatestTextureSlotRequest(r,s)?.id===a,u=()=>l()||this.shouldApplyStaleTextureSlotLOD(r,s,e,o),c=g.getOrLoadLOD(t,e,{isCurrent:u}).then(d=>{if(!l()&&!this.shouldApplyStaleTextureSlotLOD(r,s,e,o))return null;if(Array.isArray(d))return console.warn("Progressive: Got an array of textures for a texture slot, this should not happen..."),null;if(d?.isTexture===!0){if(d!=t&&r&&s){const p=this.getMaterialTextureSlot(r,s)??t;if(p&&!o){const m=this.getAssignedLODInformation(p);if(m&&m?.level<e)return f==="verbose"&&console.warn("Assigned texture level is already higher: ",m.level,e,r,p,d),null}this.assignTrackedTextureSlot(r,s,d)}return d}else f=="verbose"&&console.warn("No LOD found for",t,e);return null}).catch(d=>(console.error("Error loading LOD",t,d),null));return r&&s&&this.setPendingTextureSlotRequest(r,s,e,o,a,c),c}static trackedTextureSlots=new WeakMap;static pendingTextureSlotRequests=new WeakMap;static latestTextureSlotRequests=new WeakMap;static textureSlotRequestId=0;static trackCurrentMaterialTextureSlots(t){if(t.uniforms&&(t.isRawShaderMaterial||t.isShaderMaterial===!0)){const e=t;for(const r of Object.keys(e.uniforms)){const s=e.uniforms[r].value;s?.isTexture===!0&&this.ensureTrackedTextureSlot(t,r,s)}return}for(const e of Object.keys(t)){const r=t[e];r?.isTexture===!0&&this.ensureTrackedTextureSlot(t,e,r)}}static getPendingTextureSlotRequest(t,e){return this.pendingTextureSlotRequests.get(t)?.get(e)}static nextTextureSlotRequestId(t,e,r,s){let o=this.latestTextureSlotRequests.get(t);o||(o=new Map,this.latestTextureSlotRequests.set(t,o));const i=++this.textureSlotRequestId;return o.set(e,{id:i,level:r,force:s}),i}static getLatestTextureSlotRequest(t,e){return this.latestTextureSlotRequests.get(t)?.get(e)}static shouldApplyStaleTextureSlotLOD(t,e,r,s){if(!t||!e)return!1;const o=this.getLatestTextureSlotRequest(t,e),i=this.getMaterialTextureSlot(t,e),a=this.getAssignedLODInformation(i)?.level??1/0;return r>=a?!1:s?o?r>=o.level:!1:!0}static shouldApplyStaleMeshLOD(t,e){const r=t["LOD:requested level"];if(typeof r!="number")return!1;const s=this.getAssignedLODInformation(t.geometry)?.level??1/0;return e<s&&e>=r}static setPendingTextureSlotRequest(t,e,r,s,o,i){let a=this.pendingTextureSlotRequests.get(t);a||(a=new Map,this.pendingTextureSlotRequests.set(t,a));const l={level:r,force:s,id:o,promise:i};a.set(e,l),i.finally(()=>{a.get(e)?.id===o&&a.delete(e)})}static getMaterialTextureSlot(t,e){const r=t.uniforms?.[e];if(r?.value?.isTexture===!0)return r.value;const s=t[e];return s?.isTexture===!0?s:null}static setMaterialTextureSlot(t,e,r){const s=t.uniforms?.[e];if(s?.value?.isTexture===!0){s.value=r,t.uniformsNeedUpdate=!0;return}t[e]=r}static assignTrackedTextureSlot(t,e,r){let s=this.trackedTextureSlots.get(t);s||(s=new Map,this.trackedTextureSlots.set(t,s));const o=this.getMaterialTextureSlot(t,e);let i=s.get(e);!i&&o?i=this.ensureTrackedTextureSlot(t,e,o):i&&o&&i!==o&&(this.releaseTrackedTextureSlot(t,e,i),i=this.ensureTrackedTextureSlot(t,e,o)),!(i===r&&o===r)&&(i&&i!==r&&this.releaseTrackedTextureSlot(t,e,i),i!==r&&(this.trackTextureUsage(r),s.set(e,r)),o!==r&&this.setMaterialTextureSlot(t,e,r))}static ensureTrackedTextureSlot(t,e,r){let s=this.trackedTextureSlots.get(t);s||(s=new Map,this.trackedTextureSlots.set(t,s));const o=s.get(e);return o===r?o:(o&&this.releaseTrackedTextureSlot(t,e,o),this.trackTextureUsage(r),s.set(e,r),r)}static releaseTrackedTextureSlot(t,e,r){const s=this.trackedTextureSlots.get(t);if(s?.get(e)===r&&s.delete(e),this.untrackTextureUsage(r)&&(f||oe)){const o=this.getAssignedLODInformation(r);console.log(`[gltf-progressive] Disposed old texture LOD ${o?.level??"?"} for ${t.name||t.type}.${e}`,r.uuid)}}parser;url;constructor(t){const e=t.options.path;f&&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 f&&console.log("AFTER",this.url,t),this.parser.json.textures?.forEach((e,r)=>{if(e?.extensions){const s=e?.extensions[$];if(s){if(!s.lods){f&&console.warn("Texture has no LODs",s);return}let o=!1;for(const i of this.parser.associations.keys())i.isTexture===!0&&this.parser.associations.get(i)?.textures===r&&(o=!0,g.registerTexture(this.url,i,s.lods?.length,r,s));o||this.parser.getDependency("texture",r).then(i=>{i&&g.registerTexture(this.url,i,s.lods?.length,r,s)})}}}),this.parser.json.meshes?.forEach((e,r)=>{if(e?.extensions){const s=e?.extensions[$];if(s&&s.lods){for(const o of this.parser.associations.keys())if(o.isMesh){const i=this.parser.associations.get(o);i?.meshes===r&&g.registerMesh(this.url,s.guid,o,s.lods.length,i.primitives,s)}}}}),null}static registerTexture=(t,e,r,s,o)=>{if(!e){f&&console.error("!! gltf-progressive: Called register texture without texture");return}if(f){const{width:a,height:l}=xe(e);console.log(`> gltf-progressive: register texture[${s}] "${e.name||e.uuid}", Current: ${a}x${l}, Max: ${o.lods[0]?.width}x${o.lods[0]?.height}, uuid: ${e.uuid}`,o,e)}e.source&&(e.source[Le]=o);const i=o.guid;g.assignLODInformation(t,e,i,r,s),g.lodInfos.set(i,o),g.lowresCache.set(i,new WeakRef(e))};static registerMesh=(t,e,r,s,o,i)=>{const a=r.geometry;if(!a){f&&console.warn("gltf-progressive: Register mesh without geometry");return}a.userData||(a.userData={}),f&&console.log("> Progressive: register mesh "+r.name,{index:o,uuid:r.uuid},i,r),g.assignLODInformation(t,a,e,s,o),g.lodInfos.set(e,i);let l=g.lowresCache.get(e)?.deref();l?l.push(r.geometry):l=[r.geometry],g.lowresCache.set(e,new WeakRef(l)),s>0&&!Y(r)&&qe(r,a);for(const u of G)u.onRegisteredNewMesh?.(r,i)};static dispose(t){if(t){this.lodInfos.delete(t);const e=this.lowresCache.get(t);if(e){const r=e.deref();if(r){if(r.isTexture){const s=r;this.textureRefCounts.delete(s.uuid),s.dispose()}else if(Array.isArray(r))for(const s of r)s.dispose()}this.lowresCache.delete(t)}for(const[r,s]of this.cache)r.includes(t)&&(this._disposeCacheEntry(s),this.cache.delete(r))}else{this.lodInfos.clear();for(const[,e]of this.lowresCache){const r=e.deref();if(r){if(r.isTexture){const s=r;this.textureRefCounts.delete(s.uuid),s.dispose()}else if(Array.isArray(r))for(const s of r)s.dispose()}}this.lowresCache.clear();for(const[,e]of this.cache)this._disposeCacheEntry(e);this.cache.clear(),this.textureRefCounts.clear(),this.trackedTextureSlots=new WeakMap,this.pendingTextureSlotRequests=new WeakMap,this.latestTextureSlotRequests=new WeakMap,this.textureSlotRequestId=0}}static _disposeCacheEntry(t){if(t instanceof WeakRef){const e=t.deref();e&&(e.isTexture&&this.textureRefCounts.delete(e.uuid),e.dispose())}else t.then(e=>{if(e)if(Array.isArray(e))for(const r of e)r.dispose();else e.isTexture&&this.textureRefCounts.delete(e.uuid),e.dispose()}).catch(()=>{})}static lodInfos=new Map;static cache=new Map;static lowresCache=new Map;static textureRefCounts=new Map;static _resourceRegistry=new FinalizationRegistry(t=>{const e=g.cache.get(t);(f||oe)&&console.debug(`[gltf-progressive] Memory: Resource GC'd
|
|
2
|
-
${t}`),e instanceof WeakRef&&(e.deref()||(
|
|
3
|
-
${e}`)}}static workers=[];static _workersIndex=0;static async getOrLoadLOD(t,e,r){const s=
|
|
4
|
-
`,b),M(null)));if(!y)return M(null);const T=y.parser;s&&console.log("Loading finished "+u,w.guid);let S=0;if(y.parser.json.textures){let b=!1;for(const x of y.parser.json.textures){if(x?.extensions){const v=x?.extensions[$];if(v?.guid&&v.guid===w.guid){b=!0;break}}S++}if(b){let x=await T.getDependency("texture",S);return x&&
|
|
5
|
-
`,u),null)),l=!1;return a==null||(a instanceof I&&s instanceof I?Be(a.image)||pe(a)?a=this.copySettings(s,a):(l=!0,this.cache.delete(e)):a instanceof V&&s instanceof V&&(a.attributes.position?.array||(l=!0,this.cache.delete(e)))),l?{found:!1}:{found:!0,value:a}}static _queue;static get queue(){return this._queue??=new _t(ye()?20:50,{debug:
|
|
1
|
+
import*as et from"three";import{RedFormat as tt,RedIntegerFormat as rt,RGFormat as st,RGIntegerFormat as ot,RGBFormat as nt,RGBAFormat as it,RGBAIntegerFormat as at,BufferGeometry as V,Mesh as X,Box3 as ne,Vector3 as k,Sphere as ke,CompressedTexture as lt,Texture as I,Matrix3 as ut,InterleavedBuffer as ct,InterleavedBufferAttribute as dt,BufferAttribute as ht,TextureLoader as gt,Color as Pe,Matrix4 as Re}from"three";import{GLTFLoader as de}from"three/examples/jsm/loaders/GLTFLoader.js";import{MeshoptDecoder as ft}from"three/examples/jsm/libs/meshopt_decoder.module.js";import{DRACOLoader as mt}from"three/examples/jsm/loaders/DRACOLoader.js";import{KTX2Loader as pt}from"three/examples/jsm/loaders/KTX2Loader.js";const Ce="";globalThis.GLTF_PROGRESSIVE_VERSION=Ce,console.debug("[gltf-progressive] version -");let C="https://www.gstatic.com/draco/versioned/decoders/1.5.7/",H="https://cdn.needle.tools/static/three/0.179.1/basis2/";const xt=C,yt=H,Ae=new URL(C+"draco_decoder.js");Ae.searchParams.append("range","true"),fetch(Ae,{method:"GET",headers:{Range:"bytes=0-1"}}).catch(n=>{console.debug(`Failed to fetch remote Draco decoder from ${C} (offline: ${typeof navigator<"u"?navigator.onLine:"unknown"})`),C===xt&&Ee("./include/draco/"),H===yt&&Ie("./include/ktx2/")}).finally(()=>{$e()});const vt=()=>({dracoDecoderPath:C,ktx2TranscoderPath:H});function Ee(n){C=n,O&&O[ge]!=C?(console.debug("Updating Draco decoder path to "+n),O[ge]=C,O.setDecoderPath(C),O.preload()):console.debug("Setting Draco decoder path to "+n)}function Ie(n){H=n,A&&A.transcoderPath!=H?(console.debug("Updating KTX2 transcoder path to "+n),A.setTranscoderPath(H),A.init()):console.debug("Setting KTX2 transcoder path to "+n)}function ie(n){return $e(),n?A.detectSupport(n):n!==null&&console.warn("No renderer provided to detect ktx2 support - loading KTX2 textures might fail"),{dracoLoader:O,ktx2Loader:A,meshoptDecoder:ae}}function he(n){n.dracoLoader||n.setDRACOLoader(O),n.ktx2Loader||n.setKTX2Loader(A),n.meshoptDecoder||n.setMeshoptDecoder(ae)}const ge=Symbol("dracoDecoderPath");let O,ae,A;function $e(){O||(O=new mt,O[ge]=C,O.setDecoderPath(C),O.setDecoderConfig({type:"js"}),O.preload()),A||(A=new pt,A.setTranscoderPath(H),A.init()),ae||(ae=ft)}const fe=new WeakMap;function me(n,t){let e=fe.get(n);e?e=Object.assign(e,t):e=t,fe.set(n,e)}const wt=de.prototype.load;function Lt(...n){const t=fe.get(this);let e=n[0];const r=bt(e);if(r?.hostname.endsWith("needle.tools")){const s=t?.progressive!==void 0?t.progressive:!0,o=t?.usecase?t.usecase:"default";s?this.requestHeader.Accept=`*/*;progressive=allowed;usecase=${o}`:this.requestHeader.Accept=`*/*;usecase=${o}`,e=r.toString()}return n[0]=e,wt?.call(this,...n)}de.prototype.load=Lt;function bt(n){try{if(n instanceof URL)return n;const t=globalThis.location?.href;return t?new URL(n,t):new URL(n)}catch{return null}}function Be(n){return n!=null&&n.data!=null}function pe(n){const t=n.source?.data;return t!=null&&typeof t=="object"?t:null}function Dt(n){const t=n.image;return t!=null&&typeof t=="object"?t:null}function xe(n){const t=Dt(n),e=pe(n);return{width:t?.width||e?.width||0,height:t?.height||e?.height||0}}W("debugprogressive");function W(n){const t=globalThis.location?.href;if(!t)return!1;const e=new URL(t).searchParams.get(n);return e==null||e==="0"||e==="false"?!1:e===""?!0:e}function Mt(n,t){if(t===void 0||n===void 0||t.startsWith("./")||t.startsWith("http")||t.startsWith("data:")||t.startsWith("blob:"))return t;const e=n.lastIndexOf("/");if(e>=0){const r=n.substring(0,e+1);for(;r.endsWith("/")&&t.startsWith("/");)t=t.substring(1);return r+t}return t}function ye(){if(ee!==void 0)return ee;const n=globalThis.navigator?.userAgent||"";return ee=/iPhone|iPad|iPod|Android|IEMobile/i.test(n),W("debugprogressive")&&console.log("[glTF Progressive]: isMobileDevice",ee),ee}let ee;function Ge(){const n=globalThis.location?.href;if(!n)return!1;const t=new URL(n),e=t.hostname==="localhost"||/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(t.hostname);return t.hostname==="127.0.0.1"||e}class _t{constructor(t,e={}){this.maxConcurrent=t,this.debug=e.debug??!1,typeof globalThis.requestAnimationFrame=="function"?globalThis.requestAnimationFrame(this.tick):setTimeout(this.tick,0)}_running=new Map;_queue=[];debug=!1;tick=()=>{this.internalUpdate(),setTimeout(this.tick,10)};slot(t){return this.debug&&console.debug(`[PromiseQueue]: Requesting slot for key ${t}, running: ${this._running.size}, waiting: ${this._queue.length}`),new Promise(e=>{this._queue.push({key:t,resolve:e})})}add(t,e){this._running.has(t)||(this._running.set(t,e),e.finally(()=>{this._running.delete(t),this.debug&&console.debug(`[PromiseQueue]: Promise finished now running: ${this._running.size}, waiting: ${this._queue.length}. (finished ${t})`)}),this.debug&&console.debug(`[PromiseQueue]: Added new promise, now running: ${this._running.size}, waiting: ${this._queue.length}. (added ${t})`))}internalUpdate(){const t=this.maxConcurrent-this._running.size;for(let e=0;e<t&&this._queue.length>0;e++){this.debug&&console.debug(`[PromiseQueue]: Running ${this._running.size} promises, waiting for ${this._queue.length} more.`);const{key:r,resolve:s}=this._queue.shift();s({use:o=>this.add(r,o)})}}}function Tt(n){const t=n.image,e=t?.width??0,r=t?.height??0,s=t?.depth??1,o=Math.floor(Math.log2(Math.max(e,r,s)))+1,i=St(n);return e*r*s*i*(1-Math.pow(.25,o))/(1-.25)}function St(n){let t=4;const e=n.format;e===tt||e===rt?t=1:e===st||e===ot?t=2:e===nt||e===1029?t=3:(e===it||e===at)&&(t=4);let r=1;const s=n.type;return s===1009||s===1010?r=1:s===1011||s===1012?r=2:s===1013||s===1014||s===1015?r=4:s===1016&&(r=2),t*r}const Ot=typeof window>"u"&&typeof document>"u",ve=Symbol("needle:raycast-mesh");function Y(n){return n?.[ve]instanceof V?n[ve]:null}function qe(n,t){if((n.type==="Mesh"||n.type==="SkinnedMesh")&&!Y(n)){const e=kt(t);e.userData={isRaycastMesh:!0},n[ve]=e}}function We(n=!0){if(n){if(te)return;const t=te=X.prototype.raycast;X.prototype.raycast=function(e,r){const s=this,o=Y(s);let i;o&&s.isMesh&&(i=s.geometry,s.geometry=o),t.call(this,e,r),i&&(s.geometry=i)}}else{if(!te)return;X.prototype.raycast=te,te=null}}let te=null;function kt(n){const t=new V;for(const e in n.attributes)t.setAttribute(e,n.getAttribute(e));return t.setIndex(n.getIndex()),t}const G=new Array,g=W("debugprogressive");let re,K=-1;if(g&&typeof window<"u"){let n=function(){K+=1,K>=t&&(K=-1),console.log(`Toggle LOD level [${K}]`)};const t=6;window.addEventListener("keyup",e=>{e.key==="p"&&n(),e.key==="w"&&(re=!re,console.log(`Toggle wireframe [${re}]`));const r=parseInt(e.key);!isNaN(r)&&r>=0&&(K=r,console.log(`Set LOD level to [${K}]`))})}function Ne(n){if(g&&re!==void 0)if(Array.isArray(n))for(const t of n)Ne(t);else n&&"wireframe"in n&&(n.wireframe=re===!0)}const se=new Array;let Pt=0;const Rt=ye()?2:10;function Ct(n){if(se.length<Rt){const e=se.length;g&&console.warn(`[Worker] Creating new worker #${e}`);const r=Oe.createWorker(n||{});return se.push(r),r}const t=Pt++%se.length;return se[t]}class Oe{constructor(t,e){this.worker=t,this._debug=e.debug??!1,t.onmessage=r=>{const s=r.data;switch(this._debug&&console.log("[Worker] EVENT",s),s.type){case"loaded-gltf":for(const o of this._running)if(o.url===s.result.url){At(s.result),o.resolve(s.result);const i=o.url;i.startsWith("blob:")&&URL.revokeObjectURL(i)}}},t.onerror=r=>{console.error("[Worker] Error in gltf-progressive worker:",r)},t.postMessage({type:"init"})}static async createWorker(t){const e=new Worker(new URL("/assets/gltf-progressive.worker-CCrD-Ycm.js",import.meta.url),{type:"module"});return new Oe(e,t)}_running=[];_webglRenderer=null;async load(t,e){const r=vt();let s=e?.renderer;s||(this._webglRenderer??=(async()=>{const{WebGLRenderer:a}=await import("three");return new a})(),s=await this._webglRenderer);const o=ie(s).ktx2Loader.workerConfig;if(t instanceof URL)t=t.toString();else if(t.startsWith("file:"))t=URL.createObjectURL(new Blob([t]));else if(!t.startsWith("blob:")&&!t.startsWith("http:")&&!t.startsWith("https:")){const a=globalThis.location?.href;a&&(t=new URL(t,a).toString())}const i={type:"load",url:t,dracoDecoderPath:r.dracoDecoderPath,ktx2TranscoderPath:r.ktx2TranscoderPath,ktx2LoaderConfig:o};return this._debug&&console.debug("[Worker] Sending load request",i),this.worker.postMessage(i),new Promise(a=>{this._running.push({url:t.toString(),resolve:a})})}_debug=!1}function At(n){for(const t of n.geometries){const e=t.geometry,r=new V;if(r.name=e.name||"",e.index){const s=e.index;r.setIndex(we(s))}for(const s in e.attributes){const o=e.attributes[s],i=we(o);r.setAttribute(s,i)}if(e.morphAttributes)for(const s in e.morphAttributes){const o=e.morphAttributes[s].map(i=>we(i));r.morphAttributes[s]=o}if(r.morphTargetsRelative=e.morphTargetsRelative??!1,r.boundingBox=new ne,r.boundingBox.min=new k(e.boundingBox?.min.x,e.boundingBox?.min.y,e.boundingBox?.min.z),r.boundingBox.max=new k(e.boundingBox?.max.x,e.boundingBox?.max.y,e.boundingBox?.max.z),r.boundingSphere=new ke(new k(e.boundingSphere?.center.x,e.boundingSphere?.center.y,e.boundingSphere?.center.z),e.boundingSphere?.radius),e.groups)for(const s of e.groups)r.addGroup(s.start,s.count,s.materialIndex);e.userData&&(r.userData=e.userData),t.geometry=r}for(const t of n.textures){const e=t.texture;let r=null;if(e.isCompressedTexture){const s=e.mipmaps,{width:o,height:i}=xe(e);r=new lt(s,o,i,e.format,e.type,e.mapping,e.wrapS,e.wrapT,e.magFilter,e.minFilter,e.anisotropy,e.colorSpace)}else r=new I(e.image,e.mapping,e.wrapS,e.wrapT,e.magFilter,e.minFilter,e.format,e.type,e.anisotropy,e.colorSpace),r.mipmaps=e.mipmaps,r.channel=e.channel,r.source.data=e.source.data,r.flipY=e.flipY,r.premultiplyAlpha=e.premultiplyAlpha,r.unpackAlignment=e.unpackAlignment,r.matrix=new ut(...e.matrix.elements);if(!r){console.error("[Worker] Failed to create new texture from received data. Texture is not a CompressedTexture or Texture.");continue}t.texture=r}return n}function we(n){let t=n;if("isInterleavedBufferAttribute"in n&&n.isInterleavedBufferAttribute){const e=n.data,r=e.array,s=new ct(r,e.stride);t=new dt(s,n.itemSize,r.byteOffset,n.normalized),t.offset=n.offset}else"isBufferAttribute"in n&&n.isBufferAttribute&&(t=new ht(n.array,n.itemSize,n.normalized),t.usage=n.usage,t.gpuType=n.gpuType,t.updateRanges=n.updateRanges);return t}const Et=W("gltf-progressive-worker");W("gltf-progressive-reduce-mipmaps");const oe=W("gltf-progressive-gc"),Le=Symbol("needle-progressive-texture"),$="NEEDLE_progressive";class f{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,s="LODS:minmax",o=t[s];if(o!=null)return o;if(e||(e={min_count:1/0,max_count:0,lods:[]}),Array.isArray(t)){for(const a of t)this.getMaterialMinMaxLODsCount(a,e);return t[s]=e,e}if(g==="verbose"&&console.log("getMaterialMinMaxLODsCount",t),t.type==="ShaderMaterial"||t.type==="RawShaderMaterial"){const a=t;for(const l of Object.keys(a.uniforms)){const u=a.uniforms[l].value;u?.isTexture===!0&&i(u,e)}}else if(t.isMaterial)for(const a of Object.keys(t)){const l=t[a];l?.isTexture===!0&&i(l,e)}else g&&console.warn(`[getMaterialMinMaxLODsCount] Unsupported material type: ${t.type}`);return t[s]=e,e;function i(a,l){const u=r.getAssignedLODInformation(a);if(u){const c=r.lodInfos.get(u.key);if(c&&c.lods){l.min_count=Math.min(l.min_count,c.lods.length),l.max_count=Math.max(l.max_count,c.lods.length);for(let d=0;d<c.lods.length;d++){const p=c.lods[d];p.width&&(l.lods[d]=l.lods[d]||{min_height:1/0,max_height:0},l.lods[d].min_height=Math.min(l.lods[d].min_height,p.height),l.lods[d].max_height=Math.max(l.lods[d].max_height,p.height))}}}}}static hasLODLevelAvailable(t,e){if(Array.isArray(t)){for(const o of t)if(this.hasLODLevelAvailable(o,e))return!0;return!1}if(t.isMaterial===!0){for(const o of Object.keys(t)){const i=t[o];if(i&&i.isTexture&&this.hasLODLevelAvailable(i,e))return!0}return!1}else if(t.isGroup===!0){for(const o of t.children)if(o.isMesh===!0&&this.hasLODLevelAvailable(o,e))return!0}let r,s;if(t.isMesh?r=t.geometry:(t.isBufferGeometry||t.isTexture)&&(r=t),r&&r?.userData?.LODS){const o=r.userData.LODS;if(s=this.lodInfos.get(o.key),e===void 0)return s!=null;if(s)return Array.isArray(s.lods)?e<s.lods.length:e===0}return!1}static assignMeshLOD(t,e,r){if(!t)return Promise.resolve(null);if(t instanceof X||t.isMesh===!0){const s=t.geometry,o=this.getAssignedLODInformation(s);if(!o)return Promise.resolve(null);for(const a of G)a.onBeforeGetLODMesh?.(t,e);t["LOD:requested level"]=e;const i=()=>t["LOD:requested level"]===e||this.shouldApplyStaleMeshLOD(t,e);return f.getOrLoadLOD(s,e,{isCurrent:i}).then(a=>{if(Array.isArray(a)){const u=o.index||0;a=a[u]}const l=t["LOD:requested level"]===e;return(l||this.shouldApplyStaleMeshLOD(t,e))&&(l&&delete t["LOD:requested level"],a&&s!=a&&(a?.isBufferGeometry?typeof r?.apply=="function"?r.apply(a,e,t):r?.apply!==!1&&(t.geometry=a):g&&console.error("Invalid LOD geometry",a))),a}).catch(a=>(console.error("Error loading mesh LOD",t,a),null))}else g&&console.error("Invalid call to assignMeshLOD: Request mesh LOD but the object is not a mesh",t);return Promise.resolve(null)}static assignTextureLOD(t,e=0,r){if(!t)return Promise.resolve(null);const s=r?.force===!0;if(t.isMesh===!0){const o=t;if(Array.isArray(o.material)){const i=new Array;for(const a of o.material){const l=this.assignTextureLOD(a,e,r);i.push(l)}return Promise.all(i).then(a=>{const l=new Array;for(const u of a)Array.isArray(u)&&l.push(...u);return l})}else return this.assignTextureLOD(o.material,e,r)}if(t.isMaterial===!0){const o=t,i=[],a=new Array;if(this.trackCurrentMaterialTextureSlots(o),o.uniforms&&(o.isRawShaderMaterial||o.isShaderMaterial===!0)){const l=o;for(const u of Object.keys(l.uniforms)){const c=l.uniforms[u].value;if(c?.isTexture===!0){const d=this.assignTextureLODForSlot(c,e,o,u,s).then(p=>(p&&l.uniforms[u].value!=p&&(l.uniforms[u].value=p,l.uniformsNeedUpdate=!0),p));i.push(d),a.push(u)}}}else for(const l of Object.keys(o)){const u=o[l];if(u?.isTexture===!0){const c=this.assignTextureLODForSlot(u,e,o,l,s);i.push(c),a.push(l)}}return Promise.all(i).then(l=>{const u=new Array;for(let c=0;c<l.length;c++){const d=l[c],p=a[c];d&&d.isTexture===!0?u.push({material:o,slot:p,texture:d,level:e}):u.push({material:o,slot:p,texture:null,level:e})}return u})}if(t instanceof I||t.isTexture===!0){const o=t;return this.assignTextureLODForSlot(o,e,null,null,s)}return Promise.resolve(null)}static set maxConcurrentLoadingTasks(t){f.queue.maxConcurrent=t}static get maxConcurrentLoadingTasks(){return f.queue.maxConcurrent}static assignTextureLODForSlot(t,e,r,s,o){if(t?.isTexture!==!0)return Promise.resolve(null);if(s==="glyphMap")return Promise.resolve(t);const i=this.getAssignedLODInformation(t);if(i&&(i.level===e||!o&&i.level<e))return Promise.resolve(t);if(r&&s){const d=this.getPendingTextureSlotRequest(r,s);if(d&&d.level===e&&d.force===o)return d.promise}const a=r&&s?this.nextTextureSlotRequestId(r,s,e,o):0,l=()=>!r||!s||this.getLatestTextureSlotRequest(r,s)?.id===a,u=()=>l()||this.shouldApplyStaleTextureSlotLOD(r,s,e,o),c=f.getOrLoadLOD(t,e,{isCurrent:u}).then(d=>{if(!l()&&!this.shouldApplyStaleTextureSlotLOD(r,s,e,o))return null;if(Array.isArray(d))return console.warn("Progressive: Got an array of textures for a texture slot, this should not happen..."),null;if(d?.isTexture===!0){if(d!=t&&r&&s){const p=this.getMaterialTextureSlot(r,s)??t;if(p&&!o){const m=this.getAssignedLODInformation(p);if(m&&m?.level<e)return g==="verbose"&&console.warn("Assigned texture level is already higher: ",m.level,e,r,p,d),null}this.assignTrackedTextureSlot(r,s,d)}return d}else g=="verbose"&&console.warn("No LOD found for",t,e);return null}).catch(d=>(console.error("Error loading LOD",t,d),null));return r&&s&&this.setPendingTextureSlotRequest(r,s,e,o,a,c),c}static trackedTextureSlots=new WeakMap;static pendingTextureSlotRequests=new WeakMap;static latestTextureSlotRequests=new WeakMap;static textureSlotRequestId=0;static trackCurrentMaterialTextureSlots(t){if(t.uniforms&&(t.isRawShaderMaterial||t.isShaderMaterial===!0)){const e=t;for(const r of Object.keys(e.uniforms)){const s=e.uniforms[r].value;s?.isTexture===!0&&this.ensureTrackedTextureSlot(t,r,s)}return}for(const e of Object.keys(t)){const r=t[e];r?.isTexture===!0&&this.ensureTrackedTextureSlot(t,e,r)}}static getPendingTextureSlotRequest(t,e){return this.pendingTextureSlotRequests.get(t)?.get(e)}static nextTextureSlotRequestId(t,e,r,s){let o=this.latestTextureSlotRequests.get(t);o||(o=new Map,this.latestTextureSlotRequests.set(t,o));const i=++this.textureSlotRequestId;return o.set(e,{id:i,level:r,force:s}),i}static getLatestTextureSlotRequest(t,e){return this.latestTextureSlotRequests.get(t)?.get(e)}static shouldApplyStaleTextureSlotLOD(t,e,r,s){if(!t||!e)return!1;const o=this.getLatestTextureSlotRequest(t,e),i=this.getMaterialTextureSlot(t,e),a=this.getAssignedLODInformation(i)?.level??1/0;return r>=a?!1:s?o?r>=o.level:!1:!0}static shouldApplyStaleMeshLOD(t,e){const r=t["LOD:requested level"];if(typeof r!="number")return!1;const s=this.getAssignedLODInformation(t.geometry)?.level??1/0;return e<s&&e>=r}static setPendingTextureSlotRequest(t,e,r,s,o,i){let a=this.pendingTextureSlotRequests.get(t);a||(a=new Map,this.pendingTextureSlotRequests.set(t,a));const l={level:r,force:s,id:o,promise:i};a.set(e,l),i.finally(()=>{a.get(e)?.id===o&&a.delete(e)})}static getMaterialTextureSlot(t,e){const r=t.uniforms?.[e];if(r?.value?.isTexture===!0)return r.value;const s=t[e];return s?.isTexture===!0?s:null}static setMaterialTextureSlot(t,e,r){const s=t.uniforms?.[e];if(s?.value?.isTexture===!0){s.value=r,t.uniformsNeedUpdate=!0;return}t[e]=r}static assignTrackedTextureSlot(t,e,r){let s=this.trackedTextureSlots.get(t);s||(s=new Map,this.trackedTextureSlots.set(t,s));const o=this.getMaterialTextureSlot(t,e);let i=s.get(e);!i&&o?i=this.ensureTrackedTextureSlot(t,e,o):i&&o&&i!==o&&(this.releaseTrackedTextureSlot(t,e,i),i=this.ensureTrackedTextureSlot(t,e,o)),!(i===r&&o===r)&&(i&&i!==r&&this.releaseTrackedTextureSlot(t,e,i),i!==r&&(this.trackTextureUsage(r),s.set(e,r)),o!==r&&this.setMaterialTextureSlot(t,e,r))}static ensureTrackedTextureSlot(t,e,r){let s=this.trackedTextureSlots.get(t);s||(s=new Map,this.trackedTextureSlots.set(t,s));const o=s.get(e);return o===r?o:(o&&this.releaseTrackedTextureSlot(t,e,o),this.trackTextureUsage(r),s.set(e,r),r)}static releaseTrackedTextureSlot(t,e,r){const s=this.trackedTextureSlots.get(t);if(s?.get(e)===r&&s.delete(e),this.untrackTextureUsage(r)&&(g||oe)){const o=this.getAssignedLODInformation(r);console.log(`[gltf-progressive] Disposed old texture LOD ${o?.level??"?"} for ${t.name||t.type}.${e}`,r.uuid)}}parser;url;constructor(t){const e=t.options.path;g&&console.log("Progressive extension registered for",e),this.parser=t,this.url=e}_isLoadingMesh;loadMesh=t=>{if(this._isLoadingMesh)return null;const e=this.parser.json.meshes[t]?.extensions?.[$];return e?(this._isLoadingMesh=!0,this.parser.getDependency("mesh",t).then(r=>(this._isLoadingMesh=!1,r&&f.registerMesh(this.url,e.guid,r,e.lods?.length,0,e),r))):null};afterRoot(t){return g&&console.log("AFTER",this.url,t),this.parser.json.textures?.forEach((e,r)=>{if(e?.extensions){const s=e?.extensions[$];if(s){if(!s.lods){g&&console.warn("Texture has no LODs",s);return}let o=!1;for(const i of this.parser.associations.keys())i.isTexture===!0&&this.parser.associations.get(i)?.textures===r&&(o=!0,f.registerTexture(this.url,i,s.lods?.length,r,s));o||this.parser.getDependency("texture",r).then(i=>{i&&f.registerTexture(this.url,i,s.lods?.length,r,s)})}}}),this.parser.json.meshes?.forEach((e,r)=>{if(e?.extensions){const s=e?.extensions[$];if(s&&s.lods){for(const o of this.parser.associations.keys())if(o.isMesh){const i=this.parser.associations.get(o);i?.meshes===r&&f.registerMesh(this.url,s.guid,o,s.lods.length,i.primitives,s)}}}}),null}static registerTexture=(t,e,r,s,o)=>{if(!e){g&&console.error("!! gltf-progressive: Called register texture without texture");return}if(g){const{width:a,height:l}=xe(e);console.log(`> gltf-progressive: register texture[${s}] "${e.name||e.uuid}", Current: ${a}x${l}, Max: ${o.lods[0]?.width}x${o.lods[0]?.height}, uuid: ${e.uuid}`,o,e)}e.source&&(e.source[Le]=o);const i=o.guid;f.assignLODInformation(t,e,i,r,s),f.lodInfos.set(i,o),f.lowresCache.set(i,new WeakRef(e))};static registerMesh=(t,e,r,s,o,i)=>{const a=r.geometry;if(!a){g&&console.warn("gltf-progressive: Register mesh without geometry");return}a.userData||(a.userData={}),g&&console.log("> Progressive: register mesh "+r.name,{index:o,uuid:r.uuid},i,r),f.assignLODInformation(t,a,e,s,o),f.lodInfos.set(e,i);let l=f.lowresCache.get(e)?.deref();l?l.push(r.geometry):l=[r.geometry],f.lowresCache.set(e,new WeakRef(l)),s>0&&!Y(r)&&qe(r,a);for(const u of G)u.onRegisteredNewMesh?.(r,i)};static dispose(t){if(t){this.lodInfos.delete(t);const e=this.lowresCache.get(t);if(e){const r=e.deref();if(r){if(r.isTexture){const s=r;this.textureRefCounts.delete(s.uuid),s.dispose()}else if(Array.isArray(r))for(const s of r)s.dispose()}this.lowresCache.delete(t)}for(const[r,s]of this.cache)r.includes(t)&&(this._disposeCacheEntry(s),this.cache.delete(r))}else{this.lodInfos.clear();for(const[,e]of this.lowresCache){const r=e.deref();if(r){if(r.isTexture){const s=r;this.textureRefCounts.delete(s.uuid),s.dispose()}else if(Array.isArray(r))for(const s of r)s.dispose()}}this.lowresCache.clear();for(const[,e]of this.cache)this._disposeCacheEntry(e);this.cache.clear(),this.textureRefCounts.clear(),this.trackedTextureSlots=new WeakMap,this.pendingTextureSlotRequests=new WeakMap,this.latestTextureSlotRequests=new WeakMap,this.textureSlotRequestId=0}}static _disposeCacheEntry(t){if(t instanceof WeakRef){const e=t.deref();e&&(e.isTexture&&this.textureRefCounts.delete(e.uuid),e.dispose())}else t.then(e=>{if(e)if(Array.isArray(e))for(const r of e)r.dispose();else e.isTexture&&this.textureRefCounts.delete(e.uuid),e.dispose()}).catch(()=>{})}static lodInfos=new Map;static cache=new Map;static lowresCache=new Map;static textureRefCounts=new Map;static _resourceRegistry=new FinalizationRegistry(t=>{const e=f.cache.get(t);(g||oe)&&console.debug(`[gltf-progressive] Memory: Resource GC'd
|
|
2
|
+
${t}`),e instanceof WeakRef&&(e.deref()||(f.cache.delete(t),(g||oe)&&console.log("[gltf-progressive] \u21AA Cache entry deleted (GC)")))});static trackTextureUsage(t){const e=t.uuid,r=this.textureRefCounts.get(e)||0;this.textureRefCounts.set(e,r+1),g==="verbose"&&console.log(`[gltf-progressive] Track texture ${e}, refCount: ${r} \u2192 ${r+1}`)}static untrackTextureUsage(t){const e=t.uuid,r=this.textureRefCounts.get(e);if(!r)return(g==="verbose"||oe)&&o("[gltf-progressive] Memory: Untrack untracked texture (dispose immediately)",0),t.dispose(),!0;const s=r-1;if(s<=0)return this.textureRefCounts.delete(e),(g||oe)&&o("[gltf-progressive] Memory: Dispose texture",s),t.dispose(),!0;return this.textureRefCounts.set(e,s),g==="verbose"&&o("[gltf-progressive] Memory: Untrack texture",s),!1;function o(i,a){let{width:l,height:u}=xe(t);const c=l&&u?`${l}x${u}`:"N/A";let d="N/A";l&&u&&(d=`~${(Tt(t)/(1024*1024)).toFixed(2)} MB`),console.log(`${i} \u2014 ${t.name} ${c} (${d}), refCount: ${r} \u2192 ${a}
|
|
3
|
+
${e}`)}}static workers=[];static _workersIndex=0;static async getOrLoadLOD(t,e,r){const s=g=="verbose",o=this.getAssignedLODInformation(t);if(!o)return g&&console.warn(`[gltf-progressive] No LOD information found: ${t.name}, uuid: ${t.uuid}, type: ${t.type}`,t),null;const i=o?.key;let a;if(t.isTexture===!0){const l=t;l.source&&l.source[Le]&&(a=l.source[Le])}if(a||(a=f.lodInfos.get(i)),!a)g&&console.warn(`Can not load LOD ${e}: no LOD info found for "${i}" ${t.name}`,t.type,f.lodInfos);else{if(e>0){let c=!1;const d=Array.isArray(a.lods);if(d&&e>=a.lods.length?c=!0:d||(c=!0),c){const p=this.lowresCache.get(i);if(p){const m=p.deref();if(m)return m;this.lowresCache.delete(i),g&&console.log(`[gltf-progressive] Lowres cache entry was GC'd: ${i}`)}return null}}const l=Array.isArray(a.lods)?a.lods[e]?.path:a.lods;if(!l)return g&&!a["missing:uri"]&&(a["missing:uri"]=!0,console.warn("Missing uri for progressive asset for LOD "+e,a)),null;const u=Mt(o.url,l);if(u.endsWith(".glb")||u.endsWith(".gltf")){if(!a.guid)return console.warn("missing pointer for glb/gltf texture",a),null;const c=u+"_"+a.guid,d=await this.tryResolveLODCacheEntry(this.cache.get(c),c,u,t,e,s);if(d.found)return d.value;if(r?.isCurrent?.()===!1)return s&&console.log(`Skipping stale LOD ${e} request before queue: ${u}`),null;const p=await this.queue.slot(u);if(r?.isCurrent?.()===!1)return s&&console.log(`Skipping stale LOD ${e} request after queue: ${u}`),null;const m=await this.tryResolveLODCacheEntry(this.cache.get(c),c,u,t,e,s);if(m.found)return m.value;if(!p.use)return g&&console.log(`LOD ${e} was aborted: ${u}`),null;const w=a,h=new Promise(async(M,j)=>{if(Et){const b=await(await Ct({})).load(u);if(b.textures.length>0)for(const x of b.textures){let v=x.texture;return f.assignLODInformation(o.url,v,i,e,void 0),t instanceof I&&(v=this.copySettings(t,v)),v&&(v.guid=w.guid),M(v)}if(b.geometries.length>0){const x=new Array;for(const v of b.geometries){const R=v.geometry;f.assignLODInformation(o.url,R,i,e,v.primitiveIndex),x.push(R)}return M(x)}return M(null)}const B=new de;he(B),g&&(await new Promise(b=>setTimeout(b,1e3)),s&&console.warn("Start loading (delayed) "+u,w.guid));let L=u;if(w&&Array.isArray(w.lods)){const b=w.lods[e];b.hash&&(L+="?v="+b.hash)}const y=await B.loadAsync(L).catch(b=>(console.error(`Error loading LOD ${e} from ${u}
|
|
4
|
+
`,b),M(null)));if(!y)return M(null);const T=y.parser;s&&console.log("Loading finished "+u,w.guid);let S=0;if(y.parser.json.textures){let b=!1;for(const x of y.parser.json.textures){if(x?.extensions){const v=x?.extensions[$];if(v?.guid&&v.guid===w.guid){b=!0;break}}S++}if(b){let x=await T.getDependency("texture",S);return x&&f.assignLODInformation(o.url,x,i,e,void 0),s&&console.log('change "'+t.name+'" \u2192 "'+x.name+'"',u,S,x,c),t instanceof I&&(x=this.copySettings(t,x)),x&&(x.guid=w.guid),M(x)}else g&&console.warn("Could not find texture with guid",w.guid,y.parser.json)}if(S=0,y.parser.json.meshes){let b=!1;for(const x of y.parser.json.meshes){if(x?.extensions){const v=x?.extensions[$];if(v?.guid&&v.guid===w.guid){b=!0;break}}S++}if(b){const x=await T.getDependency("mesh",S);if(s&&console.log(`Loaded Mesh "${x.name}"`,u,S,x,c),x.isMesh===!0){const v=x.geometry;return f.assignLODInformation(o.url,v,i,e,0),M(v)}else{const v=new Array;for(let R=0;R<x.children.length;R++){const F=x.children[R];if(F.isMesh===!0){const z=F.geometry;f.assignLODInformation(o.url,z,i,e,R),v.push(z)}}return M(v)}}else g&&console.warn("Could not find mesh with guid",w.guid,y.parser.json)}return M(null)});this.cache.set(c,h),p.use(h);const D=await h;return D!=null?D instanceof I?(this.cache.set(c,new WeakRef(D)),f._resourceRegistry.register(D,c)):Array.isArray(D)?this.cache.set(c,Promise.resolve(D)):this.cache.set(c,Promise.resolve(D)):this.cache.set(c,Promise.resolve(null)),D}else if(t instanceof I){if(r?.isCurrent?.()===!1)return s&&console.log(`Skipping stale texture LOD ${e} request: ${u}`),null;s&&console.log("Load texture from uri: "+u);const c=await new gt().loadAsync(u);return r?.isCurrent?.()===!1?(c?.dispose(),null):(c?(c.guid=a.guid,c.flipY=!1,c.needsUpdate=!0,c.colorSpace=t.colorSpace,s&&console.log(a,c)):g&&console.warn("failed loading",u),c)}}return null}static async tryResolveLODCacheEntry(t,e,r,s,o,i){if(t===void 0)return{found:!1};if(i&&console.log(`LOD ${o} was already loading/loaded: ${e}`),t instanceof WeakRef){const u=t.deref();if(u){let c=u,d=!1;if(c instanceof I&&s instanceof I?Be(c.image)||pe(c)?c=this.copySettings(s,c):d=!0:c instanceof V&&s instanceof V&&(c.attributes.position?.array||(d=!0)),!d)return{found:!0,value:c}}return this.cache.delete(e),g&&console.log(`[gltf-progressive] Re-loading GC'd/disposed resource: ${e}`),{found:!1}}let a=await t.catch(u=>(console.error(`Error loading LOD ${o} from ${r}
|
|
5
|
+
`,u),null)),l=!1;return a==null||(a instanceof I&&s instanceof I?Be(a.image)||pe(a)?a=this.copySettings(s,a):(l=!0,this.cache.delete(e)):a instanceof V&&s instanceof V&&(a.attributes.position?.array||(l=!0,this.cache.delete(e)))),l?{found:!1}:{found:!0,value:a}}static _queue;static get queue(){return this._queue??=new _t(ye()?20:50,{debug:g!=!1})}static assignLODInformation(t,e,r,s,o){if(!e)return;e.userData||(e.userData={});const i=new It(t,r,s,o);e.userData.LODS=i,"source"in e&&typeof e.source=="object"&&(e.source.LODS=i)}static getAssignedLODInformation(t){return t?t.userData?.LODS?t.userData.LODS:"source"in t&&t.source?.LODS?t.source.LODS:null:null}static copySettings(t,e){return e?(g==="verbose"&&console.debug(`Copy texture settings
|
|
6
6
|
`,t.uuid,`
|
|
7
|
-
`,e.uuid),e=e.clone(),e.offset=t.offset,e.repeat=t.repeat,e.colorSpace=t.colorSpace,e.magFilter=t.magFilter,e.minFilter=t.minFilter,e.wrapS=t.wrapS,e.wrapT=t.wrapT,e.flipY=t.flipY,e.anisotropy=t.anisotropy,e.mipmaps||(e.generateMipmaps=t.generateMipmaps),e):t}}class It{url;key;level;index;constructor(t,e,r,s){this.url=t,this.key=e,this.level=r,s!=null&&(this.index=s)}}class le{static addPromise=(t,e,r,s)=>{s.forEach(o=>{o.add(t,e,r)})};ready;get awaitedCount(){return this._addedCount}get resolvedCount(){return this._resolvedCount}get currentlyAwaiting(){return this._awaiting.length}_resolve;_signal;_frame_start;_frames_to_capture;_resolved=!1;_addedCount=0;_resolvedCount=0;_awaiting=[];_maxPromisesPerObject=1;constructor(t,e){const r=Math.max(e.frames??2,2);this._frame_start=e.waitForFirstCapture?void 0:t,this._frames_to_capture=r,this.ready=new Promise(s=>{this._resolve=s}),this.ready.finally(()=>{this._resolved=!0,this._awaiting.length=0}),this._signal=e.signal,this._signal?.addEventListener("abort",()=>{this.resolveNow()}),this._maxPromisesPerObject=Math.max(1,e.maxPromisesPerObject??1)}_currentFrame=0;update(t){this._currentFrame=t,this._frame_start===void 0&&this._addedCount>0&&(this._frame_start=t),(this._signal?.aborted||this._awaiting.length===0&&this._frame_start!==void 0&&t>this._frame_start+this._frames_to_capture)&&this.resolveNow()}_seen=new WeakMap;add(t,e,r){if(this._resolved){f&&console.warn("PromiseGroup: Trying to add a promise to a resolved group, ignoring.");return}if(!(this._frame_start!==void 0&&this._currentFrame>this._frame_start+this._frames_to_capture)){if(this._maxPromisesPerObject>=1)if(this._seen.has(e)){const s=this._seen.get(e);if(s>=this._maxPromisesPerObject){f&&console.warn("PromiseGroup: Already awaiting object ignoring new promise for it.");return}this._seen.set(e,s+1)}else this._seen.set(e,1);this._awaiting.push(r),this._addedCount++,r.finally(()=>{this._resolvedCount++,this._awaiting.splice(this._awaiting.indexOf(r),1)})}}resolveNow(){this._resolved||this._resolve?.({awaited_count:this._addedCount,resolved_count:this._resolvedCount,cancelled:this._signal?.aborted??!1})}}const E=W("debugprogressive"),$t=E==="colors",Bt=W("noprogressive"),be=Symbol("Needle:LODSManager"),De=Symbol("Needle:LODState"),N=Symbol("Needle:CurrentLOD"),Ue=et,_={mesh_lod:-1,texture_lod:-1},Gt=new Pe,Me=[3526751,11065402,15978811,15897394,15749691,11032304,4827122,3332036,16739229,7306743,14053330,3516499,12035359,14703919,3963096,42662,14100029,8344319,4633680,16229681,3120096,12076434,9083434,2060171,15751837,10182117,48121,62932,16704576,15817653,5083278,5592405];function qt(){const n=Ue.Timer||Ue.Clock;return new n}const _e=new ne,U=new ne,je=new ne,Wt=new k,Nt=new k,Ut=new Re,q=new k,Q=new k,J=new k,Z=new k;function jt(n,t){const e=n.min,r=n.max,s=(e.x+r.x)*.5,o=(e.y+r.y)*.5;return q.set(s,o,e.z).applyMatrix4(t).z<0}function Fe(n){const{geometry:t,matrixWorld:e,camera:r,projectionScreenMatrix:s,desiredDensity:o,canvasHeight:i=0,currentLevel:a=-1,xrEnabled:l=!1,debugDrawLine:u,warnMissingPrimitiveDensities:c=!1}=n,d=g.getMeshLODExtension(t)?.lods,p=g.getPrimitiveIndex(t),m=n.target??{level:a,primitiveIndex:p,screenCoverage:0,screenspaceVolume:new k,centrality:1};m.level=a,m.primitiveIndex=p,m.screenCoverage=0,m.screenspaceVolume.set(0,0,0),m.centrality=1;let w=n.boundingBox??t.boundingBox;if(w||(t.computeBoundingBox(),w=t.boundingBox),!w)return m;if(_e.copy(w).applyMatrix4(e),r.isPerspectiveCamera&&jt(_e,s))return m.level=0,m.screenCoverage=1/0,m.screenspaceVolume.set(1/0,1/0,1/0),m;if(U.copy(_e).applyMatrix4(s),l&&r.isPerspectiveCamera&&r.fov>70){const L=U.min,y=U.max;let T=L.x,S=L.y,b=y.x,x=y.y;const v=2,R=1.5,F=(L.x+y.x)*.5,z=(L.y+y.y)*.5;T=(T-F)*v+F,S=(S-z)*v+z,b=(b-F)*v+F,x=(x-z)*v+z;const Je=T<0&&b>0?0:Math.min(Math.abs(L.x),Math.abs(y.x)),Ze=S<0&&x>0?0:Math.min(Math.abs(L.y),Math.abs(y.y)),ce=Math.max(Je,Ze);m.centrality=(R-ce)*(R-ce)*(R-ce)}const h=U.getSize(Wt);h.multiplyScalar(.5),globalThis.screen?.availHeight>0&&i>0&&h.multiplyScalar(i/globalThis.screen.availHeight),r.isPerspectiveCamera&&(h.x*=r.aspect),je.copy(w).applyMatrix4(e).applyMatrix4(r.matrixWorldInverse);const D=je.getSize(Nt),M=Math.max(h.x,h.y),j=Math.max(D.x,D.y);M!==0&&j!==0&&(h.z=D.z/j*M);const B=Math.max(h.x,h.y,h.z)*m.centrality;if(m.screenCoverage=B,m.screenspaceVolume.copy(h),B<=0)return m;if(u){const L=Ut.copy(s);L.invert(),q.copy(U.min),Q.copy(U.max),Q.x=q.x,J.copy(U.max),J.y=q.y,Z.copy(U.max);const y=(q.z+Z.z)*.5;q.z=Q.z=J.z=Z.z=y,q.applyMatrix4(L),Q.applyMatrix4(L),J.applyMatrix4(L),Z.applyMatrix4(L),u(q,Q,255),u(q,J,255),u(Q,Z,255),u(J,Z,255)}if(d?.length)for(let L=0;L<d.length;L++){const y=d[L],T=y.densities?.[p]||y.density||1e-5;if(p>0&&c&&Ge()&&!y.densities&&!globalThis["NEEDLE:MISSING_LOD_PRIMITIVE_DENSITIES"]&&(globalThis["NEEDLE:MISSING_LOD_PRIMITIVE_DENSITIES"]=!0,console.warn("[Needle Progressive] Detected usage of mesh without primitive densities. This might cause incorrect LOD level selection: Consider re-optimizing your model by updating your Needle Integration, Needle glTF Pipeline or running optimization again on Needle Cloud.")),T/B<o){m.level=L;break}}return m}class P{static debugDrawLine;static getObjectLODState(t){return t[De]}static addPlugin(t){G.push(t)}static removePlugin(t){const e=G.indexOf(t);e>=0&&G.splice(e,1)}static getPlugins(){return G}static get(t,e){if(t[be])return console.debug("[gltf-progressive] LODsManager already exists for this renderer"),t[be];const r=new P(t,{engine:"unknown",...e});return t[be]=r,r}renderer;context;projectionScreenMatrix=new Re;get plugins(){return G}overrideLodLevel=void 0;targetTriangleDensity=2e5;skinnedMeshAutoUpdateBoundsInterval=30;updateInterval="auto";#e=1;pause=!1;manual=!1;_newPromiseGroups=[];_promiseGroupIds=0;awaitLoading(t){const e=this._promiseGroupIds++,r=new le(this.#s,{...t});this._newPromiseGroups.push(r);const s=performance.now();return r.ready.finally(()=>{const o=this._newPromiseGroups.indexOf(r);o>=0&&(this._newPromiseGroups.splice(o,1),Ge()&&performance.measure("LODsManager:awaitLoading",{start:s,detail:{id:e,name:t?.name,awaited:r.awaitedCount,resolved:r.resolvedCount}}))}),r.ready}trackLoadingPromise(t,e,r){return le.addPromise(t,e,r,this._newPromiseGroups),r}_postprocessPromiseGroups(){if(this._newPromiseGroups.length!==0)for(let t=this._newPromiseGroups.length-1;t>=0;t--)this._newPromiseGroups[t].update(this.#s)}_lodchangedlisteners=[];addEventListener(t,e){return t==="changed"?(this._lodchangedlisteners.push(e),()=>{this.removeEventListener(t,e)}):()=>{}}removeEventListener(t,e){let r=!1;if(t==="changed"){const s=this._lodchangedlisteners.indexOf(e);s>=0&&(this._lodchangedlisteners.splice(s,1),r=!0)}return r}constructor(t,e){this.renderer=t,this.context={...e}}#t;#s=0;#o=0;#i=0;#r=0;#n=qt();_fpsBuffer=[60,60,60,60,60];enable(){if(this.#t)return;console.debug("[gltf-progressive] Enabling LODsManager for renderer");let t=0;this.#t=this.renderer.render;const e=this;ie(this.renderer),this.renderer.render=function(r,s){const o=e.renderer.getRenderTarget();(o==null||"isXRRenderTarget"in o&&o.isXRRenderTarget)&&(t=0,e.#s+=1,e.#n.update?.(),e.#o=Math.max(e.#n.getDelta(),1/1e3),e.#i+=e.#o,e._fpsBuffer.shift(),e._fpsBuffer.push(1/e.#o),e.#r=e._fpsBuffer.reduce((a,l)=>a+l)/e._fpsBuffer.length,E&&e.#s%200===0&&console.log("FPS",Math.round(e.#r),"Interval:",e.#e));const i=t++;e.#t.call(this,r,s),e.onAfterRender(r,s,i)}}disable(){this.#t&&(console.debug("[gltf-progressive] Disabling LODsManager for renderer"),this.renderer.render=this.#t,this.#t=void 0)}update(t,e){this.internalUpdate(t,e)}onAfterRender(t,e,r){if(this.pause)return;const s=this.getRenderList(t,e,r);if(!s)return;const o=s.opaque;let i=!0;if(o.length===1){const a=o[0].material;(a.name==="EffectMaterial"||a.name==="CopyShader")&&(i=!1)}if((e.parent&&e.parent.type==="CubeCamera"||r>=1&&e.type==="OrthographicCamera")&&(i=!1),i){if(Bt||(this.updateInterval==="auto"?this.#r<40&&this.#e<10?(this.#e+=1,E&&console.warn("\u2193 Reducing LOD updates",this.#e,this.#r.toFixed(0))):this.#r>=60&&this.#e>1&&(this.#e-=1,E&&console.warn("\u2191 Increasing LOD updates",this.#e,this.#r.toFixed(0))):this.#e=this.updateInterval,this.#e>0&&this.#s%this.#e!=0))return;this.internalUpdate(t,e),this._postprocessPromiseGroups()}}internalUpdate(t,e){const r=this.getRenderList(t,e,0);if(!r)return;const s=r.opaque;this.projectionScreenMatrix.multiplyMatrices(e.projectionMatrix,e.matrixWorldInverse);const o=this.targetTriangleDensity;for(const l of s){if(l.material&&(l.geometry?.type==="BoxGeometry"||l.geometry?.type==="BufferGeometry")&&(l.material.name==="SphericalGaussianBlur"||l.material.name=="BackgroundCubeMaterial"||l.material.name==="CubemapFromEquirect"||l.material.name==="EquirectangularToCubeUV")){E&&(l.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]||(l.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]=!0,console.warn("Ignoring skybox or BLIT object",l,l.material.name,l.material.type)));continue}switch(l.material.type){case"LineBasicMaterial":case"LineDashedMaterial":case"PointsMaterial":case"ShadowMaterial":case"MeshDistanceMaterial":case"MeshDepthMaterial":continue}const u=l.object;(u instanceof X||u.isMesh)&&this.updateLODs(t,e,u,o)}const i=r.transparent;for(const l of i){const u=l.object;(u instanceof X||u.isMesh)&&this.updateLODs(t,e,u,o)}const a=r.transmissive;for(const l of a){const u=l.object;(u instanceof X||u.isMesh)&&this.updateLODs(t,e,u,o)}}getRenderList(t,e,r){const s=this.renderer;let o=null;if(s.isWebGPURenderer===!0){const i=s._renderLists;if(!i)return null;o=i.get(t,e)}else if(s.isWebGLRenderer===!0){const i=s.renderLists;if(!i)return null;o=i.get(t,r)}return o?{opaque:o.opaque||[],transparent:o.transparent||[],transmissive:o.transmissive||o.transparentDoublePass||[],transparentDoublePass:o.transparentDoublePass||[]}:null}updateLODs(t,e,r,s){r.userData||(r.userData={});let o=r[De];if(o||(o=new Ft,r[De]=o),o.frames++<2)return;for(const a of G)a.onBeforeUpdateLOD?.(this.renderer,t,e,r);const i=this.overrideLodLevel!==void 0?this.overrideLodLevel:K;i>=0?(_.mesh_lod=i,_.texture_lod=i):(this.calculateLodLevel(e,r,o,s,_),_.mesh_lod=Math.round(_.mesh_lod),_.texture_lod=Math.round(_.texture_lod)),_.mesh_lod>=0&&this.loadProgressiveMeshes(r,_.mesh_lod),r.material&&_.texture_lod>=0&&this.loadProgressiveTextures(r.material,_.texture_lod,i),f&&r.material&&!r.isGizmo&&Ne(r.material),$t&&r.material&&!r.isGizmo&&!r.isBatchedMesh&&ze(r.material,_.mesh_lod);for(const a of G)a.onAfterUpdatedLOD?.(this.renderer,t,e,r,_);o.lastLodLevel_Mesh=_.mesh_lod,o.lastLodLevel_Texture=_.texture_lod}loadProgressiveTextures(t,e,r){if(!t)return;if(Array.isArray(t)){for(const i of t)this.loadProgressiveTextures(i,e,r);return}let s=!1;(t[N]===void 0||e<t[N])&&(s=!0);const o=r!==void 0&&r>=0;if(o&&(s=t[N]!=r,e=r),s){t[N]=e;const i=o?{force:!0}:void 0,a=g.assignTextureLOD(t,e,i).then(l=>{this._lodchangedlisteners.forEach(u=>u({type:"texture",level:e,object:t}))});le.addPromise("texture",t,a,this._newPromiseGroups)}}loadProgressiveMeshes(t,e){if(!t)return Promise.resolve(null);let r=t[N]!==e;const s=t["DEBUG:LOD"];if(s!=null&&(r=t[N]!=s,e=s),r){t[N]=e;const o=t.geometry,i=g.assignMeshLOD(t,e).then(a=>(a&&t[N]==e&&o!=t.geometry&&this._lodchangedlisteners.forEach(l=>l({type:"mesh",level:e,object:t})),a));return le.addPromise("mesh",t,i,this._newPromiseGroups),i}return Promise.resolve(null)}_sphere=new ke;_tempWorldPosition=new k;static skinnedMeshBoundsFrameOffsetCounter=0;static $skinnedMeshBoundsOffset=Symbol("gltf-progressive-skinnedMeshBoundsOffset");calculateLodLevel(t,e,r,s,o){if(!e){o.mesh_lod=-1,o.texture_lod=-1;return}if(!t){o.mesh_lod=-1,o.texture_lod=-1;return}let i=11,a=!1;if(E&&e["DEBUG:LOD"]!=null)return e["DEBUG:LOD"];const l=g.getMeshLODExtension(e.geometry)?.lods,u=g.getPrimitiveIndex(e.geometry),c=l&&l.length>0,d=g.getMaterialMinMaxLODsCount(e.material),p=d.min_count!==1/0&&d.min_count>=0&&d.max_count>=0;if(!c&&!p){o.mesh_lod=0,o.texture_lod=0;return}c||(a=!0,i=0);const m=this.renderer.domElement.clientHeight||this.renderer.domElement.height;let w=e.geometry.boundingBox;if(e.type==="SkinnedMesh"){const h=e;if(!h.boundingBox)h.computeBoundingBox();else if(this.skinnedMeshAutoUpdateBoundsInterval>0){if(!h[P.$skinnedMeshBoundsOffset]){const M=P.skinnedMeshBoundsFrameOffsetCounter++;h[P.$skinnedMeshBoundsOffset]=M}const D=h[P.$skinnedMeshBoundsOffset];if((r.frames+D)%this.skinnedMeshAutoUpdateBoundsInterval===0){const M=Y(h),j=h.geometry;M&&(h.geometry=M),h.computeBoundingBox(),h.geometry=j}}w=h.boundingBox}if(w){if(e.geometry.attributes.color&&e.geometry.attributes.color.count<100&&e.geometry.boundingSphere){this._sphere.copy(e.geometry.boundingSphere),this._sphere.applyMatrix4(e.matrixWorld);const D=t.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(D)){o.mesh_lod=0,o.texture_lod=0;return}}const h=Fe({geometry:e.geometry,matrixWorld:e.matrixWorld,camera:t,projectionScreenMatrix:this.projectionScreenMatrix,desiredDensity:s,canvasHeight:m,currentLevel:r.lastLodLevel_Mesh,boundingBox:w,xrEnabled:this.renderer.xr.enabled,debugDrawLine:E?P.debugDrawLine:void 0,warnMissingPrimitiveDensities:!0});if(r.lastCentrality=h.centrality,r.lastScreenCoverage=h.screenCoverage,r.lastScreenspaceVolume.copy(h.screenspaceVolume),h.screenCoverage===1/0){o.mesh_lod=0,o.texture_lod=0;return}h.level>=0&&h.level<i&&(i=h.level,a=!0)}if(a?o.mesh_lod=i:o.mesh_lod=r.lastLodLevel_Mesh,E&&o.mesh_lod!=r.lastLodLevel_Mesh){const h=l?.[o.mesh_lod];h&&console.log(`Mesh LOD changed: ${r.lastLodLevel_Mesh} \u2192 ${o.mesh_lod} (density: ${h.densities?.[u].toFixed(0)}) | ${e.name}`)}if(p){const h="saveData"in globalThis.navigator&&globalThis.navigator.saveData===!0;if(r.lastLodLevel_Texture<0){if(o.texture_lod=d.max_count-1,E){const D=d.lods[d.max_count-1];E&&console.log(`First Texture LOD ${o.texture_lod} (${D.max_height}px) - ${e.name}`)}}else{const D=r.lastScreenspaceVolume.x+r.lastScreenspaceVolume.y+r.lastScreenspaceVolume.z;let M=r.lastScreenCoverage*4;this.context?.engine==="model-viewer"&&(M*=1.5);const j=this.renderer.getPixelRatio?.()||globalThis.devicePixelRatio||1,B=m/j*M;let L=!1;for(let y=d.lods.length-1;y>=0;y--){const T=d.lods[y];if(!(h&&T.max_height>=2048)&&!(ye()&&T.max_height>4096)&&(T.max_height>B||!L&&y===0)){if(L=!0,o.texture_lod=y,E&&o.texture_lod<r.lastLodLevel_Texture){const S=T.max_height;console.log(`Texture LOD changed: ${r.lastLodLevel_Texture} \u2192 ${o.texture_lod} = ${S}px
|
|
7
|
+
`,e.uuid),e=e.clone(),e.offset=t.offset,e.repeat=t.repeat,e.colorSpace=t.colorSpace,e.magFilter=t.magFilter,e.minFilter=t.minFilter,e.wrapS=t.wrapS,e.wrapT=t.wrapT,e.flipY=t.flipY,e.anisotropy=t.anisotropy,e.mipmaps||(e.generateMipmaps=t.generateMipmaps),e):t}}class It{url;key;level;index;constructor(t,e,r,s){this.url=t,this.key=e,this.level=r,s!=null&&(this.index=s)}}class le{static addPromise=(t,e,r,s)=>{s.forEach(o=>{o.add(t,e,r)})};ready;get awaitedCount(){return this._addedCount}get resolvedCount(){return this._resolvedCount}get currentlyAwaiting(){return this._awaiting.length}_resolve;_signal;_frame_start;_frames_to_capture;_resolved=!1;_addedCount=0;_resolvedCount=0;_awaiting=[];_maxPromisesPerObject=1;constructor(t,e){const r=Math.max(e.frames??2,2);this._frame_start=e.waitForFirstCapture?void 0:t,this._frames_to_capture=r,this.ready=new Promise(s=>{this._resolve=s}),this.ready.finally(()=>{this._resolved=!0,this._awaiting.length=0}),this._signal=e.signal,this._signal?.addEventListener("abort",()=>{this.resolveNow()}),this._maxPromisesPerObject=Math.max(1,e.maxPromisesPerObject??1)}_currentFrame=0;update(t){this._currentFrame=t,this._frame_start===void 0&&this._addedCount>0&&(this._frame_start=t),(this._signal?.aborted||this._awaiting.length===0&&this._frame_start!==void 0&&t>this._frame_start+this._frames_to_capture)&&this.resolveNow()}_seen=new WeakMap;add(t,e,r){if(this._resolved){g&&console.warn("PromiseGroup: Trying to add a promise to a resolved group, ignoring.");return}if(!(this._frame_start!==void 0&&this._currentFrame>this._frame_start+this._frames_to_capture)){if(this._maxPromisesPerObject>=1)if(this._seen.has(e)){const s=this._seen.get(e);if(s>=this._maxPromisesPerObject){g&&console.warn("PromiseGroup: Already awaiting object ignoring new promise for it.");return}this._seen.set(e,s+1)}else this._seen.set(e,1);this._awaiting.push(r),this._addedCount++,r.finally(()=>{this._resolvedCount++,this._awaiting.splice(this._awaiting.indexOf(r),1)})}}resolveNow(){this._resolved||this._resolve?.({awaited_count:this._addedCount,resolved_count:this._resolvedCount,cancelled:this._signal?.aborted??!1})}}const E=W("debugprogressive"),$t=E==="colors",Bt=W("noprogressive"),be=Symbol("Needle:LODSManager"),De=Symbol("Needle:LODState"),N=Symbol("Needle:CurrentLOD"),Ue=et,_={mesh_lod:-1,texture_lod:-1},Gt=new Pe,Me=[3526751,11065402,15978811,15897394,15749691,11032304,4827122,3332036,16739229,7306743,14053330,3516499,12035359,14703919,3963096,42662,14100029,8344319,4633680,16229681,3120096,12076434,9083434,2060171,15751837,10182117,48121,62932,16704576,15817653,5083278,5592405];function qt(){const n=Ue.Timer||Ue.Clock;return new n}const _e=new ne,U=new ne,je=new ne,Wt=new k,Nt=new k,Ut=new Re,q=new k,Q=new k,J=new k,Z=new k;function jt(n,t){const e=n.min,r=n.max,s=(e.x+r.x)*.5,o=(e.y+r.y)*.5;return q.set(s,o,e.z).applyMatrix4(t).z<0}function Fe(n){const{geometry:t,matrixWorld:e,camera:r,projectionScreenMatrix:s,desiredDensity:o,canvasHeight:i=0,currentLevel:a=-1,xrEnabled:l=!1,debugDrawLine:u,warnMissingPrimitiveDensities:c=!1}=n,d=f.getMeshLODExtension(t)?.lods,p=f.getPrimitiveIndex(t),m=n.target??{level:a,primitiveIndex:p,screenCoverage:0,screenspaceVolume:new k,centrality:1};if(m.level=a,m.primitiveIndex=p,m.screenCoverage=0,m.screenspaceVolume.set(0,0,0),m.centrality=1,!d?.length)return m;let w=n.boundingBox??t.boundingBox;if(w||(t.computeBoundingBox(),w=t.boundingBox),!w)return m;if(_e.copy(w).applyMatrix4(e),r.isPerspectiveCamera&&jt(_e,s))return m.level=0,m.screenCoverage=1/0,m.screenspaceVolume.set(1/0,1/0,1/0),m;if(U.copy(_e).applyMatrix4(s),l&&r.isPerspectiveCamera&&r.fov>70){const L=U.min,y=U.max;let T=L.x,S=L.y,b=y.x,x=y.y;const v=2,R=1.5,F=(L.x+y.x)*.5,z=(L.y+y.y)*.5;T=(T-F)*v+F,S=(S-z)*v+z,b=(b-F)*v+F,x=(x-z)*v+z;const Je=T<0&&b>0?0:Math.min(Math.abs(L.x),Math.abs(y.x)),Ze=S<0&&x>0?0:Math.min(Math.abs(L.y),Math.abs(y.y)),ce=Math.max(Je,Ze);m.centrality=(R-ce)*(R-ce)*(R-ce)}const h=U.getSize(Wt);h.multiplyScalar(.5),globalThis.screen?.availHeight>0&&i>0&&h.multiplyScalar(i/globalThis.screen.availHeight),r.isPerspectiveCamera&&(h.x*=r.aspect),je.copy(w).applyMatrix4(e).applyMatrix4(r.matrixWorldInverse);const D=je.getSize(Nt),M=Math.max(h.x,h.y),j=Math.max(D.x,D.y);M!==0&&j!==0&&(h.z=D.z/j*M);const B=Math.max(h.x,h.y,h.z)*m.centrality;if(m.screenCoverage=B,m.screenspaceVolume.copy(h),B<=0)return m;if(u){const L=Ut.copy(s);L.invert(),q.copy(U.min),Q.copy(U.max),Q.x=q.x,J.copy(U.max),J.y=q.y,Z.copy(U.max);const y=(q.z+Z.z)*.5;q.z=Q.z=J.z=Z.z=y,q.applyMatrix4(L),Q.applyMatrix4(L),J.applyMatrix4(L),Z.applyMatrix4(L),u(q,Q,255),u(q,J,255),u(Q,Z,255),u(J,Z,255)}for(let L=0;L<d.length;L++){const y=d[L],T=y.densities?.[p]||y.density||1e-5;if(p>0&&c&&Ge()&&!y.densities&&!globalThis["NEEDLE:MISSING_LOD_PRIMITIVE_DENSITIES"]&&(globalThis["NEEDLE:MISSING_LOD_PRIMITIVE_DENSITIES"]=!0,console.warn("[Needle Progressive] Detected usage of mesh without primitive densities. This might cause incorrect LOD level selection: Consider re-optimizing your model by updating your Needle Integration, Needle glTF Pipeline or running optimization again on Needle Cloud.")),T/B<o){m.level=L;break}}return m}class P{static debugDrawLine;static getObjectLODState(t){return t[De]}static addPlugin(t){G.push(t)}static removePlugin(t){const e=G.indexOf(t);e>=0&&G.splice(e,1)}static getPlugins(){return G}static get(t,e){if(t[be])return console.debug("[gltf-progressive] LODsManager already exists for this renderer"),t[be];const r=new P(t,{engine:"unknown",...e});return t[be]=r,r}renderer;context;projectionScreenMatrix=new Re;get plugins(){return G}overrideLodLevel=void 0;targetTriangleDensity=2e5;skinnedMeshAutoUpdateBoundsInterval=30;updateInterval="auto";#e=1;pause=!1;manual=!1;_newPromiseGroups=[];_promiseGroupIds=0;awaitLoading(t){const e=this._promiseGroupIds++,r=new le(this.#s,{...t});this._newPromiseGroups.push(r);const s=performance.now();return r.ready.finally(()=>{const o=this._newPromiseGroups.indexOf(r);o>=0&&(this._newPromiseGroups.splice(o,1),Ge()&&performance.measure("LODsManager:awaitLoading",{start:s,detail:{id:e,name:t?.name,awaited:r.awaitedCount,resolved:r.resolvedCount}}))}),r.ready}trackLoadingPromise(t,e,r){return le.addPromise(t,e,r,this._newPromiseGroups),r}_postprocessPromiseGroups(){if(this._newPromiseGroups.length!==0)for(let t=this._newPromiseGroups.length-1;t>=0;t--)this._newPromiseGroups[t].update(this.#s)}_lodchangedlisteners=[];addEventListener(t,e){return t==="changed"?(this._lodchangedlisteners.push(e),()=>{this.removeEventListener(t,e)}):()=>{}}removeEventListener(t,e){let r=!1;if(t==="changed"){const s=this._lodchangedlisteners.indexOf(e);s>=0&&(this._lodchangedlisteners.splice(s,1),r=!0)}return r}constructor(t,e){this.renderer=t,this.context={...e}}#t;#s=0;#o=0;#i=0;#r=0;#n=qt();_fpsBuffer=[60,60,60,60,60];enable(){if(this.#t)return;console.debug("[gltf-progressive] Enabling LODsManager for renderer");let t=0;this.#t=this.renderer.render;const e=this;ie(this.renderer),this.renderer.render=function(r,s){const o=e.renderer.getRenderTarget();(o==null||"isXRRenderTarget"in o&&o.isXRRenderTarget)&&(t=0,e.#s+=1,e.#n.update?.(),e.#o=Math.max(e.#n.getDelta(),1/1e3),e.#i+=e.#o,e._fpsBuffer.shift(),e._fpsBuffer.push(1/e.#o),e.#r=e._fpsBuffer.reduce((a,l)=>a+l)/e._fpsBuffer.length,E&&e.#s%200===0&&console.log("FPS",Math.round(e.#r),"Interval:",e.#e));const i=t++;e.#t.call(this,r,s),e.onAfterRender(r,s,i)}}disable(){this.#t&&(console.debug("[gltf-progressive] Disabling LODsManager for renderer"),this.renderer.render=this.#t,this.#t=void 0)}update(t,e){this.internalUpdate(t,e)}onAfterRender(t,e,r){if(this.pause)return;const s=this.getRenderList(t,e,r);if(!s)return;const o=s.opaque;let i=!0;if(o.length===1){const a=o[0].material;(a.name==="EffectMaterial"||a.name==="CopyShader")&&(i=!1)}if((e.parent&&e.parent.type==="CubeCamera"||r>=1&&e.type==="OrthographicCamera")&&(i=!1),i){if(Bt||(this.updateInterval==="auto"?this.#r<40&&this.#e<10?(this.#e+=1,E&&console.warn("\u2193 Reducing LOD updates",this.#e,this.#r.toFixed(0))):this.#r>=60&&this.#e>1&&(this.#e-=1,E&&console.warn("\u2191 Increasing LOD updates",this.#e,this.#r.toFixed(0))):this.#e=this.updateInterval,this.#e>0&&this.#s%this.#e!=0))return;this.internalUpdate(t,e),this._postprocessPromiseGroups()}}internalUpdate(t,e){const r=this.getRenderList(t,e,0);if(!r)return;const s=r.opaque;this.projectionScreenMatrix.multiplyMatrices(e.projectionMatrix,e.matrixWorldInverse);const o=this.targetTriangleDensity;for(const l of s){if(l.material&&(l.geometry?.type==="BoxGeometry"||l.geometry?.type==="BufferGeometry")&&(l.material.name==="SphericalGaussianBlur"||l.material.name=="BackgroundCubeMaterial"||l.material.name==="CubemapFromEquirect"||l.material.name==="EquirectangularToCubeUV")){E&&(l.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]||(l.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]=!0,console.warn("Ignoring skybox or BLIT object",l,l.material.name,l.material.type)));continue}switch(l.material.type){case"LineBasicMaterial":case"LineDashedMaterial":case"PointsMaterial":case"ShadowMaterial":case"MeshDistanceMaterial":case"MeshDepthMaterial":continue}const u=l.object;(u instanceof X||u.isMesh)&&this.updateLODs(t,e,u,o)}const i=r.transparent;for(const l of i){const u=l.object;(u instanceof X||u.isMesh)&&this.updateLODs(t,e,u,o)}const a=r.transmissive;for(const l of a){const u=l.object;(u instanceof X||u.isMesh)&&this.updateLODs(t,e,u,o)}}getRenderList(t,e,r){const s=this.renderer;let o=null;if(s.isWebGPURenderer===!0){const i=s._renderLists;if(!i)return null;o=i.get(t,e)}else if(s.isWebGLRenderer===!0){const i=s.renderLists;if(!i)return null;o=i.get(t,r)}return o?{opaque:o.opaque||[],transparent:o.transparent||[],transmissive:o.transmissive||o.transparentDoublePass||[],transparentDoublePass:o.transparentDoublePass||[]}:null}updateLODs(t,e,r,s){r.userData||(r.userData={});let o=r[De];if(o||(o=new Ft,r[De]=o),o.frames++<2)return;for(const a of G)a.onBeforeUpdateLOD?.(this.renderer,t,e,r);const i=this.overrideLodLevel!==void 0?this.overrideLodLevel:K;i>=0?(_.mesh_lod=i,_.texture_lod=i):(this.calculateLodLevel(e,r,o,s,_),_.mesh_lod=Math.round(_.mesh_lod),_.texture_lod=Math.round(_.texture_lod)),_.mesh_lod>=0&&this.loadProgressiveMeshes(r,_.mesh_lod),r.material&&_.texture_lod>=0&&this.loadProgressiveTextures(r.material,_.texture_lod,i),g&&r.material&&!r.isGizmo&&Ne(r.material),$t&&r.material&&!r.isGizmo&&!r.isBatchedMesh&&ze(r.material,_.mesh_lod);for(const a of G)a.onAfterUpdatedLOD?.(this.renderer,t,e,r,_);o.lastLodLevel_Mesh=_.mesh_lod,o.lastLodLevel_Texture=_.texture_lod}loadProgressiveTextures(t,e,r){if(!t)return;if(Array.isArray(t)){for(const i of t)this.loadProgressiveTextures(i,e,r);return}let s=!1;(t[N]===void 0||e<t[N])&&(s=!0);const o=r!==void 0&&r>=0;if(o&&(s=t[N]!=r,e=r),s){t[N]=e;const i=o?{force:!0}:void 0,a=f.assignTextureLOD(t,e,i).then(l=>{this._lodchangedlisteners.forEach(u=>u({type:"texture",level:e,object:t}))});le.addPromise("texture",t,a,this._newPromiseGroups)}}loadProgressiveMeshes(t,e){if(!t)return Promise.resolve(null);let r=t[N]!==e;const s=t["DEBUG:LOD"];if(s!=null&&(r=t[N]!=s,e=s),r){t[N]=e;const o=t.geometry,i=f.assignMeshLOD(t,e).then(a=>(a&&t[N]==e&&o!=t.geometry&&this._lodchangedlisteners.forEach(l=>l({type:"mesh",level:e,object:t})),a));return le.addPromise("mesh",t,i,this._newPromiseGroups),i}return Promise.resolve(null)}_sphere=new ke;_tempWorldPosition=new k;static skinnedMeshBoundsFrameOffsetCounter=0;static $skinnedMeshBoundsOffset=Symbol("gltf-progressive-skinnedMeshBoundsOffset");calculateLodLevel(t,e,r,s,o){if(!e){o.mesh_lod=-1,o.texture_lod=-1;return}if(!t){o.mesh_lod=-1,o.texture_lod=-1;return}let i=11,a=!1;if(E&&e["DEBUG:LOD"]!=null)return e["DEBUG:LOD"];const l=f.getMeshLODExtension(e.geometry)?.lods,u=f.getPrimitiveIndex(e.geometry),c=l&&l.length>0,d=f.getMaterialMinMaxLODsCount(e.material),p=d.min_count!==1/0&&d.min_count>=0&&d.max_count>=0;if(!c&&!p){o.mesh_lod=0,o.texture_lod=0;return}c||(a=!0,i=0);const m=this.renderer.domElement.clientHeight||this.renderer.domElement.height;let w=e.geometry.boundingBox;if(e.type==="SkinnedMesh"){const h=e;if(!h.boundingBox)h.computeBoundingBox();else if(this.skinnedMeshAutoUpdateBoundsInterval>0){if(!h[P.$skinnedMeshBoundsOffset]){const M=P.skinnedMeshBoundsFrameOffsetCounter++;h[P.$skinnedMeshBoundsOffset]=M}const D=h[P.$skinnedMeshBoundsOffset];if((r.frames+D)%this.skinnedMeshAutoUpdateBoundsInterval===0){const M=Y(h),j=h.geometry;M&&(h.geometry=M),h.computeBoundingBox(),h.geometry=j}}w=h.boundingBox}if(w){if(e.geometry.attributes.color&&e.geometry.attributes.color.count<100&&e.geometry.boundingSphere){this._sphere.copy(e.geometry.boundingSphere),this._sphere.applyMatrix4(e.matrixWorld);const D=t.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(D)){o.mesh_lod=0,o.texture_lod=0;return}}const h=Fe({geometry:e.geometry,matrixWorld:e.matrixWorld,camera:t,projectionScreenMatrix:this.projectionScreenMatrix,desiredDensity:s,canvasHeight:m,currentLevel:r.lastLodLevel_Mesh,boundingBox:w,xrEnabled:this.renderer.xr.enabled,debugDrawLine:E?P.debugDrawLine:void 0,warnMissingPrimitiveDensities:!0});if(r.lastCentrality=h.centrality,r.lastScreenCoverage=h.screenCoverage,r.lastScreenspaceVolume.copy(h.screenspaceVolume),h.screenCoverage===1/0){o.mesh_lod=0,o.texture_lod=0;return}h.level>=0&&h.level<i&&(i=h.level,a=!0)}if(a?o.mesh_lod=i:o.mesh_lod=r.lastLodLevel_Mesh,E&&o.mesh_lod!=r.lastLodLevel_Mesh){const h=l?.[o.mesh_lod];h&&console.log(`Mesh LOD changed: ${r.lastLodLevel_Mesh} \u2192 ${o.mesh_lod} (density: ${h.densities?.[u].toFixed(0)}) | ${e.name}`)}if(p){const h="saveData"in globalThis.navigator&&globalThis.navigator.saveData===!0;if(r.lastLodLevel_Texture<0){if(o.texture_lod=d.max_count-1,E){const D=d.lods[d.max_count-1];E&&console.log(`First Texture LOD ${o.texture_lod} (${D.max_height}px) - ${e.name}`)}}else{const D=r.lastScreenspaceVolume.x+r.lastScreenspaceVolume.y+r.lastScreenspaceVolume.z;let M=r.lastScreenCoverage*4;this.context?.engine==="model-viewer"&&(M*=1.5);const j=this.renderer.getPixelRatio?.()||globalThis.devicePixelRatio||1,B=m/j*M;let L=!1;for(let y=d.lods.length-1;y>=0;y--){const T=d.lods[y];if(!(h&&T.max_height>=2048)&&!(ye()&&T.max_height>4096)&&(T.max_height>B||!L&&y===0)){if(L=!0,o.texture_lod=y,E&&o.texture_lod<r.lastLodLevel_Texture){const S=T.max_height;console.log(`Texture LOD changed: ${r.lastLodLevel_Texture} \u2192 ${o.texture_lod} = ${S}px
|
|
8
8
|
Screensize: ${B.toFixed(0)}px, Coverage: ${(100*r.lastScreenCoverage).toFixed(2)}%, Volume ${D.toFixed(1)}
|
|
9
9
|
${e.name}`)}break}}}}else o.texture_lod=0}}class Ft{frames=0;lastLodLevel_Mesh=-1;lastLodLevel_Texture=-1;lastScreenCoverage=0;lastScreenspaceVolume=new k;lastCentrality=0}function ze(n,t){if(!(t<0)){if(Array.isArray(n)){for(const e of n)ze(e,t);return}"color"in n&&n.color instanceof Pe&&(n.color.copy(Ve(t,Gt)),n.needsUpdate=!0)}}function Ve(n,t){const e=Math.max(0,Math.min(Me.length-1,Math.floor(n)));return t.setHex(Me[e])}const Xe=Symbol("NEEDLE_mesh_lod"),ue=Symbol("NEEDLE_texture_lod");let Te=null;function Se(){const n=zt();n&&(n.mapURLs(function(t){return He(),t}),He(),Te?.disconnect(),Te=new MutationObserver(t=>{t.forEach(e=>{e.addedNodes.forEach(r=>{r instanceof HTMLElement&&r.tagName.toLowerCase()==="model-viewer"&&Ye(r)})})}),Te.observe(document,{childList:!0,subtree:!0}))}function zt(){return typeof customElements>"u"?null:customElements.get("model-viewer")||(customElements.whenDefined("model-viewer").then(()=>{console.debug("[gltf-progressive] model-viewer defined"),Se()}),null)}function He(){typeof document>"u"||document.querySelectorAll("model-viewer").forEach(n=>{Ye(n)})}const Ke=new WeakSet;let Vt=0;function Ye(n){if(!n||Ke.has(n))return null;Ke.add(n),console.debug("[gltf-progressive] found new model-viewer..."+ ++Vt+`
|
|
10
|
-
`,n.getAttribute("src"));let t=null,e=null,r=null;for(let s=n;s!=null;s=Object.getPrototypeOf(s)){const o=Object.getOwnPropertySymbols(s),i=o.find(u=>u.toString()=="Symbol(renderer)"),a=o.find(u=>u.toString()=="Symbol(scene)"),l=o.find(u=>u.toString()=="Symbol(needsRender)");!t&&i!=null&&(t=n[i].threeRenderer),!e&&a!=null&&(e=n[a]),!r&&l!=null&&(r=n[l])}if(t&&e){let s=function(){if(r){let i=0;const a=setInterval(()=>{if(i++>5){clearInterval(a);return}r?.call(n)},300)}};console.debug("[gltf-progressive] setup model-viewer");const o=P.get(t,{engine:"model-viewer"});return P.addPlugin(new Xt),o.enable(),o.addEventListener("changed",()=>{r?.call(n)}),n.addEventListener("model-visibility",i=>{i.detail.visible&&r?.call(n)}),n.addEventListener("load",()=>{s()}),()=>{o.disable()}}return null}class Xt{_didWarnAboutMissingUrl=!1;onBeforeUpdateLOD(t,e,r,s){this.tryParseMeshLOD(e,s),this.tryParseTextureLOD(e,s)}getUrl(t){if(!t)return null;let e=t.getAttribute("src");return e||(e=t.src),e||(this._didWarnAboutMissingUrl||console.warn("No url found in modelviewer",t),this._didWarnAboutMissingUrl=!0),e}tryGetCurrentGLTF(t){return t._currentGLTF}tryGetCurrentModelViewer(t){return t.element}tryParseTextureLOD(t,e){if(e[ue]==!0)return;e[ue]=!0;const r=this.tryGetCurrentGLTF(t),s=this.tryGetCurrentModelViewer(t),o=this.getUrl(s);if(o&&r&&e.material){let i=function(l){if(l[ue]==!0)return;l[ue]=!0,l.userData&&(l.userData.LOD=-1);const u=Object.keys(l);for(let c=0;c<u.length;c++){const d=u[c],p=l[d];if(p?.isTexture===!0){const m=p.userData?.associations?.textures;if(m==null)continue;const w=r.parser.json.textures[m];if(!w){console.warn("Texture data not found for texture index "+m);continue}if(w?.extensions?.[$]){const h=w.extensions[$];h&&o&&
|
|
10
|
+
`,n.getAttribute("src"));let t=null,e=null,r=null;for(let s=n;s!=null;s=Object.getPrototypeOf(s)){const o=Object.getOwnPropertySymbols(s),i=o.find(u=>u.toString()=="Symbol(renderer)"),a=o.find(u=>u.toString()=="Symbol(scene)"),l=o.find(u=>u.toString()=="Symbol(needsRender)");!t&&i!=null&&(t=n[i].threeRenderer),!e&&a!=null&&(e=n[a]),!r&&l!=null&&(r=n[l])}if(t&&e){let s=function(){if(r){let i=0;const a=setInterval(()=>{if(i++>5){clearInterval(a);return}r?.call(n)},300)}};console.debug("[gltf-progressive] setup model-viewer");const o=P.get(t,{engine:"model-viewer"});return P.addPlugin(new Xt),o.enable(),o.addEventListener("changed",()=>{r?.call(n)}),n.addEventListener("model-visibility",i=>{i.detail.visible&&r?.call(n)}),n.addEventListener("load",()=>{s()}),()=>{o.disable()}}return null}class Xt{_didWarnAboutMissingUrl=!1;onBeforeUpdateLOD(t,e,r,s){this.tryParseMeshLOD(e,s),this.tryParseTextureLOD(e,s)}getUrl(t){if(!t)return null;let e=t.getAttribute("src");return e||(e=t.src),e||(this._didWarnAboutMissingUrl||console.warn("No url found in modelviewer",t),this._didWarnAboutMissingUrl=!0),e}tryGetCurrentGLTF(t){return t._currentGLTF}tryGetCurrentModelViewer(t){return t.element}tryParseTextureLOD(t,e){if(e[ue]==!0)return;e[ue]=!0;const r=this.tryGetCurrentGLTF(t),s=this.tryGetCurrentModelViewer(t),o=this.getUrl(s);if(o&&r&&e.material){let i=function(l){if(l[ue]==!0)return;l[ue]=!0,l.userData&&(l.userData.LOD=-1);const u=Object.keys(l);for(let c=0;c<u.length;c++){const d=u[c],p=l[d];if(p?.isTexture===!0){const m=p.userData?.associations?.textures;if(m==null)continue;const w=r.parser.json.textures[m];if(!w){console.warn("Texture data not found for texture index "+m);continue}if(w?.extensions?.[$]){const h=w.extensions[$];h&&o&&f.registerTexture(o,p,h.lods.length,m,h)}}}};const a=e.material;if(Array.isArray(a))for(const l of a)i(l);else i(a)}}tryParseMeshLOD(t,e){if(e[Xe]==!0)return;e[Xe]=!0;const r=this.tryGetCurrentModelViewer(t),s=this.getUrl(r);if(!s)return;const o=e.userData?.gltfExtensions?.[$];if(o&&s){const i=e.uuid;f.registerMesh(s,i,e,0,o.lods.length,o)}}}function Qe(...n){let t,e,r,s;switch(n.length){case 2:[r,e]=n,s={};break;case 3:[r,e,s]=n;break;case 4:[t,e,r,s]=n;break;default:throw new Error("Invalid arguments")}ie(e),he(r),me(r,{progressive:!0,...s?.hints}),r.register(i=>new f(i));const o=P.get(e);return s?.enableLODsManager!==!1&&o.enable(),o}if(Se(),!Ot){const n={gltfProgressive:{useNeedleProgressive:Qe,LODsManager:P,configureLoader:me,getRaycastMesh:Y,useRaycastMeshes:We}};if(!globalThis.Needle)globalThis.Needle=n;else for(const t in n)globalThis.Needle[t]=n[t]}export{$ as EXTENSION_NAME,P as LODsManager,f as NEEDLE_progressive,Ce as VERSION,he as addDracoAndKTX2Loaders,Fe as calculateMeshLODLevel,me as configureLoader,ie as createLoaders,Ve as getLODColor,Y as getRaycastMesh,Me as lodDebugColors,Se as patchModelViewer,qe as registerRaycastMesh,Ee as setDracoDecoderLocation,Ie as setKTX2TranscoderLocation,Qe as useNeedleProgressive,We as useRaycastMeshes};
|
package/gltf-progressive.umd.cjs
CHANGED
|
@@ -4,7 +4,7 @@ ${e}`)}}static workers=[];static _workersIndex=0;static async getOrLoadLOD(t,e,s
|
|
|
4
4
|
`,_),w(null)));if(!S)return w(null);const k=S.parser;r&&console.log("Loading finished "+f,x.guid);let R=0;if(S.parser.json.textures){let _=!1;for(const y of S.parser.json.textures){if(y?.extensions){const M=y?.extensions[q];if(M?.guid&&M.guid===x.guid){_=!0;break}}R++}if(_){let y=await k.getDependency("texture",R);return y&&p.assignLODInformation(n.url,y,i,e,void 0),r&&console.log('change "'+t.name+'" → "'+y.name+'"',f,R,y,c),t instanceof d.Texture&&(y=this.copySettings(t,y)),y&&(y.guid=x.guid),w(y)}else g&&console.warn("Could not find texture with guid",x.guid,S.parser.json)}if(R=0,S.parser.json.meshes){let _=!1;for(const y of S.parser.json.meshes){if(y?.extensions){const M=y?.extensions[q];if(M?.guid&&M.guid===x.guid){_=!0;break}}R++}if(_){const y=await k.getDependency("mesh",R);if(r&&console.log(`Loaded Mesh "${y.name}"`,f,R,y,c),y.isMesh===!0){const M=y.geometry;return p.assignLODInformation(n.url,M,i,e,0),w(M)}else{const M=new Array;for(let T=0;T<y.children.length;T++){const G=y.children[T];if(G.isMesh===!0){const se=G.geometry;p.assignLODInformation(n.url,se,i,e,T),M.push(se)}}return w(M)}}else g&&console.warn("Could not find mesh with guid",x.guid,S.parser.json)}return w(null)});this.cache.set(c,L),m.use(L);const v=await L;return v!=null?v instanceof d.Texture?(this.cache.set(c,new WeakRef(v)),p._resourceRegistry.register(v,c)):Array.isArray(v)?this.cache.set(c,Promise.resolve(v)):this.cache.set(c,Promise.resolve(v)):this.cache.set(c,Promise.resolve(null)),v}else if(t instanceof d.Texture){if(s?.isCurrent?.()===!1)return r&&console.log(`Skipping stale texture LOD ${e} request: ${f}`),null;r&&console.log("Load texture from uri: "+f);const h=await new d.TextureLoader().loadAsync(f);return s?.isCurrent?.()===!1?(h?.dispose(),null):(h?(h.guid=a.guid,h.flipY=!1,h.needsUpdate=!0,h.colorSpace=t.colorSpace,r&&console.log(a,h)):g&&console.warn("failed loading",f),h)}}return null}static async tryResolveLODCacheEntry(t,e,s,r,n,i){if(t===void 0)return{found:!1};if(i&&console.log(`LOD ${n} was already loading/loaded: ${e}`),t instanceof WeakRef){const u=t.deref();if(u){let f=u,c=!1;if(f instanceof d.Texture&&r instanceof d.Texture?Se(f.image)||ye(f)?f=this.copySettings(r,f):c=!0:f instanceof d.BufferGeometry&&r instanceof d.BufferGeometry&&(f.attributes.position?.array||(c=!0)),!c)return{found:!0,value:f}}return this.cache.delete(e),g&&console.log(`[gltf-progressive] Re-loading GC'd/disposed resource: ${e}`),{found:!1}}let a=await t.catch(u=>(console.error(`Error loading LOD ${n} from ${s}
|
|
5
5
|
`,u),null)),l=!1;return a==null||(a instanceof d.Texture&&r instanceof d.Texture?Se(a.image)||ye(a)?a=this.copySettings(r,a):(l=!0,this.cache.delete(e)):a instanceof d.BufferGeometry&&r instanceof d.BufferGeometry&&(a.attributes.position?.array||(l=!0,this.cache.delete(e)))),l?{found:!1}:{found:!0,value:a}}static _queue;static get queue(){return this._queue??=new pt(_e()?20:50,{debug:g!=!1})}static assignLODInformation(t,e,s,r,n){if(!e)return;e.userData||(e.userData={});const i=new Tt(t,s,r,n);e.userData.LODS=i,"source"in e&&typeof e.source=="object"&&(e.source.LODS=i)}static getAssignedLODInformation(t){return t?t.userData?.LODS?t.userData.LODS:"source"in t&&t.source?.LODS?t.source.LODS:null:null}static copySettings(t,e){return e?(g==="verbose"&&console.debug(`Copy texture settings
|
|
6
6
|
`,t.uuid,`
|
|
7
|
-
`,e.uuid),e=e.clone(),e.offset=t.offset,e.repeat=t.repeat,e.colorSpace=t.colorSpace,e.magFilter=t.magFilter,e.minFilter=t.minFilter,e.wrapS=t.wrapS,e.wrapT=t.wrapT,e.flipY=t.flipY,e.anisotropy=t.anisotropy,e.mipmaps||(e.generateMipmaps=t.generateMipmaps),e):t}}class Tt{url;key;level;index;constructor(t,e,s,r){this.url=t,this.key=e,this.level=s,r!=null&&(this.index=r)}}class re{static addPromise=(t,e,s,r)=>{r.forEach(n=>{n.add(t,e,s)})};ready;get awaitedCount(){return this._addedCount}get resolvedCount(){return this._resolvedCount}get currentlyAwaiting(){return this._awaiting.length}_resolve;_signal;_frame_start;_frames_to_capture;_resolved=!1;_addedCount=0;_resolvedCount=0;_awaiting=[];_maxPromisesPerObject=1;constructor(t,e){const r=Math.max(e.frames??2,2);this._frame_start=e.waitForFirstCapture?void 0:t,this._frames_to_capture=r,this.ready=new Promise(n=>{this._resolve=n}),this.ready.finally(()=>{this._resolved=!0,this._awaiting.length=0}),this._signal=e.signal,this._signal?.addEventListener("abort",()=>{this.resolveNow()}),this._maxPromisesPerObject=Math.max(1,e.maxPromisesPerObject??1)}_currentFrame=0;update(t){this._currentFrame=t,this._frame_start===void 0&&this._addedCount>0&&(this._frame_start=t),(this._signal?.aborted||this._awaiting.length===0&&this._frame_start!==void 0&&t>this._frame_start+this._frames_to_capture)&&this.resolveNow()}_seen=new WeakMap;add(t,e,s){if(this._resolved){g&&console.warn("PromiseGroup: Trying to add a promise to a resolved group, ignoring.");return}if(!(this._frame_start!==void 0&&this._currentFrame>this._frame_start+this._frames_to_capture)){if(this._maxPromisesPerObject>=1)if(this._seen.has(e)){const r=this._seen.get(e);if(r>=this._maxPromisesPerObject){g&&console.warn("PromiseGroup: Already awaiting object ignoring new promise for it.");return}this._seen.set(e,r+1)}else this._seen.set(e,1);this._awaiting.push(s),this._addedCount++,s.finally(()=>{this._resolvedCount++,this._awaiting.splice(this._awaiting.indexOf(s),1)})}}resolveNow(){this._resolved||this._resolve?.({awaited_count:this._addedCount,resolved_count:this._resolvedCount,cancelled:this._signal?.aborted??!1})}}const I=z("debugprogressive"),bt=I==="colors",Ot=z("noprogressive"),fe=Symbol("Needle:LODSManager"),de=Symbol("Needle:LODState"),E=Symbol("Needle:CurrentLOD"),Re=it,C={mesh_lod:-1,texture_lod:-1},St=new d.Color,we=[3526751,11065402,15978811,15897394,15749691,11032304,4827122,3332036,16739229,7306743,14053330,3516499,12035359,14703919,3963096,42662,14100029,8344319,4633680,16229681,3120096,12076434,9083434,2060171,15751837,10182117,48121,62932,16704576,15817653,5083278,5592405];function Rt(){const o=Re.Timer||Re.Clock;return new o}const he=new d.Box3,V=new d.Box3,Ce=new d.Box3,Ct=new d.Vector3,Pt=new d.Vector3,kt=new d.Matrix4,F=new d.Vector3,X=new d.Vector3,K=new d.Vector3,H=new d.Vector3;function At(o,t){const e=o.min,s=o.max,r=(e.x+s.x)*.5,n=(e.y+s.y)*.5;return F.set(r,n,e.z).applyMatrix4(t).z<0}function Ve(o){const{geometry:t,matrixWorld:e,camera:s,projectionScreenMatrix:r,desiredDensity:n,canvasHeight:i=0,currentLevel:a=-1,xrEnabled:l=!1,debugDrawLine:u,warnMissingPrimitiveDensities:f=!1}=o,c=p.getMeshLODExtension(t)?.lods,h=p.getPrimitiveIndex(t),m=o.target??{level:a,primitiveIndex:h,screenCoverage:0,screenspaceVolume:new d.Vector3,centrality:1};m.level=a,m.primitiveIndex=h,m.screenCoverage=0,m.screenspaceVolume.set(0,0,0),m.centrality=1;let b=o.boundingBox??t.boundingBox;if(b||(t.computeBoundingBox(),b=t.boundingBox),!b)return m;if(he.copy(b).applyMatrix4(e),s.isPerspectiveCamera&&At(he,r))return m.level=0,m.screenCoverage=1/0,m.screenspaceVolume.set(1/0,1/0,1/0),m;if(V.copy(he).applyMatrix4(r),l&&s.isPerspectiveCamera&&s.fov>70){const D=V.min,O=V.max;let S=D.x,k=D.y,R=O.x,_=O.y;const y=2,M=1.5,T=(D.x+O.x)*.5,G=(D.y+O.y)*.5;S=(S-T)*y+T,k=(k-G)*y+G,R=(R-T)*y+T,_=(_-G)*y+G;const se=S<0&&R>0?0:Math.min(Math.abs(D.x),Math.abs(O.x)),Ke=k<0&&_>0?0:Math.min(Math.abs(D.y),Math.abs(O.y)),ae=Math.max(se,Ke);m.centrality=(M-ae)*(M-ae)*(M-ae)}const x=V.getSize(Ct);x.multiplyScalar(.5),globalThis.screen?.availHeight>0&&i>0&&x.multiplyScalar(i/globalThis.screen.availHeight),s.isPerspectiveCamera&&(x.x*=s.aspect),Ce.copy(b).applyMatrix4(e).applyMatrix4(s.matrixWorldInverse);const L=Ce.getSize(Pt),v=Math.max(x.x,x.y),w=Math.max(L.x,L.y);v!==0&&w!==0&&(x.z=L.z/w*v);const W=Math.max(x.x,x.y,x.z)*m.centrality;if(m.screenCoverage=W,m.screenspaceVolume.copy(x),W<=0)return m;if(u){const D=kt.copy(r);D.invert(),F.copy(V.min),X.copy(V.max),X.x=F.x,K.copy(V.max),K.y=F.y,H.copy(V.max);const O=(F.z+H.z)*.5;F.z=X.z=K.z=H.z=O,F.applyMatrix4(D),X.applyMatrix4(D),K.applyMatrix4(D),H.applyMatrix4(D),u(F,X,255),u(F,K,255),u(X,H,255),u(K,H,255)}if(c?.length)for(let D=0;D<c.length;D++){const O=c[D],S=O.densities?.[h]||O.density||1e-5;if(h>0&&f&&Fe()&&!O.densities&&!globalThis["NEEDLE:MISSING_LOD_PRIMITIVE_DENSITIES"]&&(globalThis["NEEDLE:MISSING_LOD_PRIMITIVE_DENSITIES"]=!0,console.warn("[Needle Progressive] Detected usage of mesh without primitive densities. This might cause incorrect LOD level selection: Consider re-optimizing your model by updating your Needle Integration, Needle glTF Pipeline or running optimization again on Needle Cloud.")),S/W<n){m.level=D;break}}return m}class A{static debugDrawLine;static getObjectLODState(t){return t[de]}static addPlugin(t){U.push(t)}static removePlugin(t){const e=U.indexOf(t);e>=0&&U.splice(e,1)}static getPlugins(){return U}static get(t,e){if(t[fe])return console.debug("[gltf-progressive] LODsManager already exists for this renderer"),t[fe];const s=new A(t,{engine:"unknown",...e});return t[fe]=s,s}renderer;context;projectionScreenMatrix=new d.Matrix4;get plugins(){return U}overrideLodLevel=void 0;targetTriangleDensity=2e5;skinnedMeshAutoUpdateBoundsInterval=30;updateInterval="auto";#e=1;pause=!1;manual=!1;_newPromiseGroups=[];_promiseGroupIds=0;awaitLoading(t){const e=this._promiseGroupIds++,s=new re(this.#r,{...t});this._newPromiseGroups.push(s);const r=performance.now();return s.ready.finally(()=>{const n=this._newPromiseGroups.indexOf(s);n>=0&&(this._newPromiseGroups.splice(n,1),Fe()&&performance.measure("LODsManager:awaitLoading",{start:r,detail:{id:e,name:t?.name,awaited:s.awaitedCount,resolved:s.resolvedCount}}))}),s.ready}trackLoadingPromise(t,e,s){return re.addPromise(t,e,s,this._newPromiseGroups),s}_postprocessPromiseGroups(){if(this._newPromiseGroups.length!==0)for(let t=this._newPromiseGroups.length-1;t>=0;t--)this._newPromiseGroups[t].update(this.#r)}_lodchangedlisteners=[];addEventListener(t,e){return t==="changed"?(this._lodchangedlisteners.push(e),()=>{this.removeEventListener(t,e)}):()=>{}}removeEventListener(t,e){let s=!1;if(t==="changed"){const r=this._lodchangedlisteners.indexOf(e);r>=0&&(this._lodchangedlisteners.splice(r,1),s=!0)}return s}constructor(t,e){this.renderer=t,this.context={...e}}#t;#r=0;#n=0;#i=0;#s=0;#o=Rt();_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;ie(this.renderer),this.renderer.render=function(s,r){const n=e.renderer.getRenderTarget();(n==null||"isXRRenderTarget"in n&&n.isXRRenderTarget)&&(t=0,e.#r+=1,e.#o.update?.(),e.#n=Math.max(e.#o.getDelta(),1/1e3),e.#i+=e.#n,e._fpsBuffer.shift(),e._fpsBuffer.push(1/e.#n),e.#s=e._fpsBuffer.reduce((a,l)=>a+l)/e._fpsBuffer.length,I&&e.#r%200===0&&console.log("FPS",Math.round(e.#s),"Interval:",e.#e));const i=t++;e.#t.call(this,s,r),e.onAfterRender(s,r,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,s){if(this.pause)return;const r=this.getRenderList(t,e,s);if(!r)return;const n=r.opaque;let i=!0;if(n.length===1){const a=n[0].material;(a.name==="EffectMaterial"||a.name==="CopyShader")&&(i=!1)}if((e.parent&&e.parent.type==="CubeCamera"||s>=1&&e.type==="OrthographicCamera")&&(i=!1),i){if(Ot||(this.updateInterval==="auto"?this.#s<40&&this.#e<10?(this.#e+=1,I&&console.warn("↓ Reducing LOD updates",this.#e,this.#s.toFixed(0))):this.#s>=60&&this.#e>1&&(this.#e-=1,I&&console.warn("↑ Increasing LOD updates",this.#e,this.#s.toFixed(0))):this.#e=this.updateInterval,this.#e>0&&this.#r%this.#e!=0))return;this.internalUpdate(t,e),this._postprocessPromiseGroups()}}internalUpdate(t,e){const s=this.getRenderList(t,e,0);if(!s)return;const r=s.opaque;this.projectionScreenMatrix.multiplyMatrices(e.projectionMatrix,e.matrixWorldInverse);const n=this.targetTriangleDensity;for(const l of r){if(l.material&&(l.geometry?.type==="BoxGeometry"||l.geometry?.type==="BufferGeometry")&&(l.material.name==="SphericalGaussianBlur"||l.material.name=="BackgroundCubeMaterial"||l.material.name==="CubemapFromEquirect"||l.material.name==="EquirectangularToCubeUV")){I&&(l.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]||(l.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]=!0,console.warn("Ignoring skybox or BLIT object",l,l.material.name,l.material.type)));continue}switch(l.material.type){case"LineBasicMaterial":case"LineDashedMaterial":case"PointsMaterial":case"ShadowMaterial":case"MeshDistanceMaterial":case"MeshDepthMaterial":continue}const u=l.object;(u instanceof d.Mesh||u.isMesh)&&this.updateLODs(t,e,u,n)}const i=s.transparent;for(const l of i){const u=l.object;(u instanceof d.Mesh||u.isMesh)&&this.updateLODs(t,e,u,n)}const a=s.transmissive;for(const l of a){const u=l.object;(u instanceof d.Mesh||u.isMesh)&&this.updateLODs(t,e,u,n)}}getRenderList(t,e,s){const r=this.renderer;let n=null;if(r.isWebGPURenderer===!0){const i=r._renderLists;if(!i)return null;n=i.get(t,e)}else if(r.isWebGLRenderer===!0){const i=r.renderLists;if(!i)return null;n=i.get(t,s)}return n?{opaque:n.opaque||[],transparent:n.transparent||[],transmissive:n.transmissive||n.transparentDoublePass||[],transparentDoublePass:n.transparentDoublePass||[]}:null}updateLODs(t,e,s,r){s.userData||(s.userData={});let n=s[de];if(n||(n=new It,s[de]=n),n.frames++<2)return;for(const a of U)a.onBeforeUpdateLOD?.(this.renderer,t,e,s);const i=this.overrideLodLevel!==void 0?this.overrideLodLevel:N;i>=0?(C.mesh_lod=i,C.texture_lod=i):(this.calculateLodLevel(e,s,n,r,C),C.mesh_lod=Math.round(C.mesh_lod),C.texture_lod=Math.round(C.texture_lod)),C.mesh_lod>=0&&this.loadProgressiveMeshes(s,C.mesh_lod),s.material&&C.texture_lod>=0&&this.loadProgressiveTextures(s.material,C.texture_lod,i),g&&s.material&&!s.isGizmo&&Ee(s.material),bt&&s.material&&!s.isGizmo&&!s.isBatchedMesh&&ze(s.material,C.mesh_lod);for(const a of U)a.onAfterUpdatedLOD?.(this.renderer,t,e,s,C);n.lastLodLevel_Mesh=C.mesh_lod,n.lastLodLevel_Texture=C.texture_lod}loadProgressiveTextures(t,e,s){if(!t)return;if(Array.isArray(t)){for(const i of t)this.loadProgressiveTextures(i,e,s);return}let r=!1;(t[E]===void 0||e<t[E])&&(r=!0);const n=s!==void 0&&s>=0;if(n&&(r=t[E]!=s,e=s),r){t[E]=e;const i=n?{force:!0}:void 0,a=p.assignTextureLOD(t,e,i).then(l=>{this._lodchangedlisteners.forEach(u=>u({type:"texture",level:e,object:t}))});re.addPromise("texture",t,a,this._newPromiseGroups)}}loadProgressiveMeshes(t,e){if(!t)return Promise.resolve(null);let s=t[E]!==e;const r=t["DEBUG:LOD"];if(r!=null&&(s=t[E]!=r,e=r),s){t[E]=e;const n=t.geometry,i=p.assignMeshLOD(t,e).then(a=>(a&&t[E]==e&&n!=t.geometry&&this._lodchangedlisteners.forEach(l=>l({type:"mesh",level:e,object:t})),a));return re.addPromise("mesh",t,i,this._newPromiseGroups),i}return Promise.resolve(null)}_sphere=new d.Sphere;_tempWorldPosition=new d.Vector3;static skinnedMeshBoundsFrameOffsetCounter=0;static $skinnedMeshBoundsOffset=Symbol("gltf-progressive-skinnedMeshBoundsOffset");calculateLodLevel(t,e,s,r,n){if(!e){n.mesh_lod=-1,n.texture_lod=-1;return}if(!t){n.mesh_lod=-1,n.texture_lod=-1;return}let a=10+1,l=!1;if(I&&e["DEBUG:LOD"]!=null)return e["DEBUG:LOD"];const u=p.getMeshLODExtension(e.geometry)?.lods,f=p.getPrimitiveIndex(e.geometry),c=u&&u.length>0,h=p.getMaterialMinMaxLODsCount(e.material),m=h.min_count!==1/0&&h.min_count>=0&&h.max_count>=0;if(!c&&!m){n.mesh_lod=0,n.texture_lod=0;return}c||(l=!0,a=0);const b=this.renderer.domElement.clientHeight||this.renderer.domElement.height;let x=e.geometry.boundingBox;if(e.type==="SkinnedMesh"){const L=e;if(!L.boundingBox)L.computeBoundingBox();else if(this.skinnedMeshAutoUpdateBoundsInterval>0){if(!L[A.$skinnedMeshBoundsOffset]){const w=A.skinnedMeshBoundsFrameOffsetCounter++;L[A.$skinnedMeshBoundsOffset]=w}const v=L[A.$skinnedMeshBoundsOffset];if((s.frames+v)%this.skinnedMeshAutoUpdateBoundsInterval===0){const w=Y(L),W=L.geometry;w&&(L.geometry=w),L.computeBoundingBox(),L.geometry=W}}x=L.boundingBox}if(x){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 w=t.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(w)){n.mesh_lod=0,n.texture_lod=0;return}}const L=Ve({geometry:e.geometry,matrixWorld:e.matrixWorld,camera:t,projectionScreenMatrix:this.projectionScreenMatrix,desiredDensity:r,canvasHeight:b,currentLevel:s.lastLodLevel_Mesh,boundingBox:x,xrEnabled:this.renderer.xr.enabled,debugDrawLine:I?A.debugDrawLine:void 0,warnMissingPrimitiveDensities:!0});if(s.lastCentrality=L.centrality,s.lastScreenCoverage=L.screenCoverage,s.lastScreenspaceVolume.copy(L.screenspaceVolume),L.screenCoverage===1/0){n.mesh_lod=0,n.texture_lod=0;return}L.level>=0&&L.level<a&&(a=L.level,l=!0)}if(l?n.mesh_lod=a:n.mesh_lod=s.lastLodLevel_Mesh,I&&n.mesh_lod!=s.lastLodLevel_Mesh){const v=u?.[n.mesh_lod];v&&console.log(`Mesh LOD changed: ${s.lastLodLevel_Mesh} → ${n.mesh_lod} (density: ${v.densities?.[f].toFixed(0)}) | ${e.name}`)}if(m){const L="saveData"in globalThis.navigator&&globalThis.navigator.saveData===!0;if(s.lastLodLevel_Texture<0){if(n.texture_lod=h.max_count-1,I){const v=h.lods[h.max_count-1];I&&console.log(`First Texture LOD ${n.texture_lod} (${v.max_height}px) - ${e.name}`)}}else{const v=s.lastScreenspaceVolume.x+s.lastScreenspaceVolume.y+s.lastScreenspaceVolume.z;let w=s.lastScreenCoverage*4;this.context?.engine==="model-viewer"&&(w*=1.5);const W=this.renderer.getPixelRatio?.()||globalThis.devicePixelRatio||1,O=b/W*w;let S=!1;for(let k=h.lods.length-1;k>=0;k--){const R=h.lods[k];if(!(L&&R.max_height>=2048)&&!(_e()&&R.max_height>4096)&&(R.max_height>O||!S&&k===0)){if(S=!0,n.texture_lod=k,I&&n.texture_lod<s.lastLodLevel_Texture){const _=R.max_height;console.log(`Texture LOD changed: ${s.lastLodLevel_Texture} → ${n.texture_lod} = ${_}px
|
|
7
|
+
`,e.uuid),e=e.clone(),e.offset=t.offset,e.repeat=t.repeat,e.colorSpace=t.colorSpace,e.magFilter=t.magFilter,e.minFilter=t.minFilter,e.wrapS=t.wrapS,e.wrapT=t.wrapT,e.flipY=t.flipY,e.anisotropy=t.anisotropy,e.mipmaps||(e.generateMipmaps=t.generateMipmaps),e):t}}class Tt{url;key;level;index;constructor(t,e,s,r){this.url=t,this.key=e,this.level=s,r!=null&&(this.index=r)}}class re{static addPromise=(t,e,s,r)=>{r.forEach(n=>{n.add(t,e,s)})};ready;get awaitedCount(){return this._addedCount}get resolvedCount(){return this._resolvedCount}get currentlyAwaiting(){return this._awaiting.length}_resolve;_signal;_frame_start;_frames_to_capture;_resolved=!1;_addedCount=0;_resolvedCount=0;_awaiting=[];_maxPromisesPerObject=1;constructor(t,e){const r=Math.max(e.frames??2,2);this._frame_start=e.waitForFirstCapture?void 0:t,this._frames_to_capture=r,this.ready=new Promise(n=>{this._resolve=n}),this.ready.finally(()=>{this._resolved=!0,this._awaiting.length=0}),this._signal=e.signal,this._signal?.addEventListener("abort",()=>{this.resolveNow()}),this._maxPromisesPerObject=Math.max(1,e.maxPromisesPerObject??1)}_currentFrame=0;update(t){this._currentFrame=t,this._frame_start===void 0&&this._addedCount>0&&(this._frame_start=t),(this._signal?.aborted||this._awaiting.length===0&&this._frame_start!==void 0&&t>this._frame_start+this._frames_to_capture)&&this.resolveNow()}_seen=new WeakMap;add(t,e,s){if(this._resolved){g&&console.warn("PromiseGroup: Trying to add a promise to a resolved group, ignoring.");return}if(!(this._frame_start!==void 0&&this._currentFrame>this._frame_start+this._frames_to_capture)){if(this._maxPromisesPerObject>=1)if(this._seen.has(e)){const r=this._seen.get(e);if(r>=this._maxPromisesPerObject){g&&console.warn("PromiseGroup: Already awaiting object ignoring new promise for it.");return}this._seen.set(e,r+1)}else this._seen.set(e,1);this._awaiting.push(s),this._addedCount++,s.finally(()=>{this._resolvedCount++,this._awaiting.splice(this._awaiting.indexOf(s),1)})}}resolveNow(){this._resolved||this._resolve?.({awaited_count:this._addedCount,resolved_count:this._resolvedCount,cancelled:this._signal?.aborted??!1})}}const I=z("debugprogressive"),bt=I==="colors",Ot=z("noprogressive"),fe=Symbol("Needle:LODSManager"),de=Symbol("Needle:LODState"),E=Symbol("Needle:CurrentLOD"),Re=it,C={mesh_lod:-1,texture_lod:-1},St=new d.Color,we=[3526751,11065402,15978811,15897394,15749691,11032304,4827122,3332036,16739229,7306743,14053330,3516499,12035359,14703919,3963096,42662,14100029,8344319,4633680,16229681,3120096,12076434,9083434,2060171,15751837,10182117,48121,62932,16704576,15817653,5083278,5592405];function Rt(){const o=Re.Timer||Re.Clock;return new o}const he=new d.Box3,V=new d.Box3,Ce=new d.Box3,Ct=new d.Vector3,Pt=new d.Vector3,kt=new d.Matrix4,F=new d.Vector3,X=new d.Vector3,K=new d.Vector3,H=new d.Vector3;function At(o,t){const e=o.min,s=o.max,r=(e.x+s.x)*.5,n=(e.y+s.y)*.5;return F.set(r,n,e.z).applyMatrix4(t).z<0}function Ve(o){const{geometry:t,matrixWorld:e,camera:s,projectionScreenMatrix:r,desiredDensity:n,canvasHeight:i=0,currentLevel:a=-1,xrEnabled:l=!1,debugDrawLine:u,warnMissingPrimitiveDensities:f=!1}=o,c=p.getMeshLODExtension(t)?.lods,h=p.getPrimitiveIndex(t),m=o.target??{level:a,primitiveIndex:h,screenCoverage:0,screenspaceVolume:new d.Vector3,centrality:1};if(m.level=a,m.primitiveIndex=h,m.screenCoverage=0,m.screenspaceVolume.set(0,0,0),m.centrality=1,!c?.length)return m;let b=o.boundingBox??t.boundingBox;if(b||(t.computeBoundingBox(),b=t.boundingBox),!b)return m;if(he.copy(b).applyMatrix4(e),s.isPerspectiveCamera&&At(he,r))return m.level=0,m.screenCoverage=1/0,m.screenspaceVolume.set(1/0,1/0,1/0),m;if(V.copy(he).applyMatrix4(r),l&&s.isPerspectiveCamera&&s.fov>70){const D=V.min,O=V.max;let S=D.x,k=D.y,R=O.x,_=O.y;const y=2,M=1.5,T=(D.x+O.x)*.5,G=(D.y+O.y)*.5;S=(S-T)*y+T,k=(k-G)*y+G,R=(R-T)*y+T,_=(_-G)*y+G;const se=S<0&&R>0?0:Math.min(Math.abs(D.x),Math.abs(O.x)),Ke=k<0&&_>0?0:Math.min(Math.abs(D.y),Math.abs(O.y)),ae=Math.max(se,Ke);m.centrality=(M-ae)*(M-ae)*(M-ae)}const x=V.getSize(Ct);x.multiplyScalar(.5),globalThis.screen?.availHeight>0&&i>0&&x.multiplyScalar(i/globalThis.screen.availHeight),s.isPerspectiveCamera&&(x.x*=s.aspect),Ce.copy(b).applyMatrix4(e).applyMatrix4(s.matrixWorldInverse);const L=Ce.getSize(Pt),v=Math.max(x.x,x.y),w=Math.max(L.x,L.y);v!==0&&w!==0&&(x.z=L.z/w*v);const W=Math.max(x.x,x.y,x.z)*m.centrality;if(m.screenCoverage=W,m.screenspaceVolume.copy(x),W<=0)return m;if(u){const D=kt.copy(r);D.invert(),F.copy(V.min),X.copy(V.max),X.x=F.x,K.copy(V.max),K.y=F.y,H.copy(V.max);const O=(F.z+H.z)*.5;F.z=X.z=K.z=H.z=O,F.applyMatrix4(D),X.applyMatrix4(D),K.applyMatrix4(D),H.applyMatrix4(D),u(F,X,255),u(F,K,255),u(X,H,255),u(K,H,255)}for(let D=0;D<c.length;D++){const O=c[D],S=O.densities?.[h]||O.density||1e-5;if(h>0&&f&&Fe()&&!O.densities&&!globalThis["NEEDLE:MISSING_LOD_PRIMITIVE_DENSITIES"]&&(globalThis["NEEDLE:MISSING_LOD_PRIMITIVE_DENSITIES"]=!0,console.warn("[Needle Progressive] Detected usage of mesh without primitive densities. This might cause incorrect LOD level selection: Consider re-optimizing your model by updating your Needle Integration, Needle glTF Pipeline or running optimization again on Needle Cloud.")),S/W<n){m.level=D;break}}return m}class A{static debugDrawLine;static getObjectLODState(t){return t[de]}static addPlugin(t){U.push(t)}static removePlugin(t){const e=U.indexOf(t);e>=0&&U.splice(e,1)}static getPlugins(){return U}static get(t,e){if(t[fe])return console.debug("[gltf-progressive] LODsManager already exists for this renderer"),t[fe];const s=new A(t,{engine:"unknown",...e});return t[fe]=s,s}renderer;context;projectionScreenMatrix=new d.Matrix4;get plugins(){return U}overrideLodLevel=void 0;targetTriangleDensity=2e5;skinnedMeshAutoUpdateBoundsInterval=30;updateInterval="auto";#e=1;pause=!1;manual=!1;_newPromiseGroups=[];_promiseGroupIds=0;awaitLoading(t){const e=this._promiseGroupIds++,s=new re(this.#r,{...t});this._newPromiseGroups.push(s);const r=performance.now();return s.ready.finally(()=>{const n=this._newPromiseGroups.indexOf(s);n>=0&&(this._newPromiseGroups.splice(n,1),Fe()&&performance.measure("LODsManager:awaitLoading",{start:r,detail:{id:e,name:t?.name,awaited:s.awaitedCount,resolved:s.resolvedCount}}))}),s.ready}trackLoadingPromise(t,e,s){return re.addPromise(t,e,s,this._newPromiseGroups),s}_postprocessPromiseGroups(){if(this._newPromiseGroups.length!==0)for(let t=this._newPromiseGroups.length-1;t>=0;t--)this._newPromiseGroups[t].update(this.#r)}_lodchangedlisteners=[];addEventListener(t,e){return t==="changed"?(this._lodchangedlisteners.push(e),()=>{this.removeEventListener(t,e)}):()=>{}}removeEventListener(t,e){let s=!1;if(t==="changed"){const r=this._lodchangedlisteners.indexOf(e);r>=0&&(this._lodchangedlisteners.splice(r,1),s=!0)}return s}constructor(t,e){this.renderer=t,this.context={...e}}#t;#r=0;#n=0;#i=0;#s=0;#o=Rt();_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;ie(this.renderer),this.renderer.render=function(s,r){const n=e.renderer.getRenderTarget();(n==null||"isXRRenderTarget"in n&&n.isXRRenderTarget)&&(t=0,e.#r+=1,e.#o.update?.(),e.#n=Math.max(e.#o.getDelta(),1/1e3),e.#i+=e.#n,e._fpsBuffer.shift(),e._fpsBuffer.push(1/e.#n),e.#s=e._fpsBuffer.reduce((a,l)=>a+l)/e._fpsBuffer.length,I&&e.#r%200===0&&console.log("FPS",Math.round(e.#s),"Interval:",e.#e));const i=t++;e.#t.call(this,s,r),e.onAfterRender(s,r,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,s){if(this.pause)return;const r=this.getRenderList(t,e,s);if(!r)return;const n=r.opaque;let i=!0;if(n.length===1){const a=n[0].material;(a.name==="EffectMaterial"||a.name==="CopyShader")&&(i=!1)}if((e.parent&&e.parent.type==="CubeCamera"||s>=1&&e.type==="OrthographicCamera")&&(i=!1),i){if(Ot||(this.updateInterval==="auto"?this.#s<40&&this.#e<10?(this.#e+=1,I&&console.warn("↓ Reducing LOD updates",this.#e,this.#s.toFixed(0))):this.#s>=60&&this.#e>1&&(this.#e-=1,I&&console.warn("↑ Increasing LOD updates",this.#e,this.#s.toFixed(0))):this.#e=this.updateInterval,this.#e>0&&this.#r%this.#e!=0))return;this.internalUpdate(t,e),this._postprocessPromiseGroups()}}internalUpdate(t,e){const s=this.getRenderList(t,e,0);if(!s)return;const r=s.opaque;this.projectionScreenMatrix.multiplyMatrices(e.projectionMatrix,e.matrixWorldInverse);const n=this.targetTriangleDensity;for(const l of r){if(l.material&&(l.geometry?.type==="BoxGeometry"||l.geometry?.type==="BufferGeometry")&&(l.material.name==="SphericalGaussianBlur"||l.material.name=="BackgroundCubeMaterial"||l.material.name==="CubemapFromEquirect"||l.material.name==="EquirectangularToCubeUV")){I&&(l.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]||(l.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]=!0,console.warn("Ignoring skybox or BLIT object",l,l.material.name,l.material.type)));continue}switch(l.material.type){case"LineBasicMaterial":case"LineDashedMaterial":case"PointsMaterial":case"ShadowMaterial":case"MeshDistanceMaterial":case"MeshDepthMaterial":continue}const u=l.object;(u instanceof d.Mesh||u.isMesh)&&this.updateLODs(t,e,u,n)}const i=s.transparent;for(const l of i){const u=l.object;(u instanceof d.Mesh||u.isMesh)&&this.updateLODs(t,e,u,n)}const a=s.transmissive;for(const l of a){const u=l.object;(u instanceof d.Mesh||u.isMesh)&&this.updateLODs(t,e,u,n)}}getRenderList(t,e,s){const r=this.renderer;let n=null;if(r.isWebGPURenderer===!0){const i=r._renderLists;if(!i)return null;n=i.get(t,e)}else if(r.isWebGLRenderer===!0){const i=r.renderLists;if(!i)return null;n=i.get(t,s)}return n?{opaque:n.opaque||[],transparent:n.transparent||[],transmissive:n.transmissive||n.transparentDoublePass||[],transparentDoublePass:n.transparentDoublePass||[]}:null}updateLODs(t,e,s,r){s.userData||(s.userData={});let n=s[de];if(n||(n=new It,s[de]=n),n.frames++<2)return;for(const a of U)a.onBeforeUpdateLOD?.(this.renderer,t,e,s);const i=this.overrideLodLevel!==void 0?this.overrideLodLevel:N;i>=0?(C.mesh_lod=i,C.texture_lod=i):(this.calculateLodLevel(e,s,n,r,C),C.mesh_lod=Math.round(C.mesh_lod),C.texture_lod=Math.round(C.texture_lod)),C.mesh_lod>=0&&this.loadProgressiveMeshes(s,C.mesh_lod),s.material&&C.texture_lod>=0&&this.loadProgressiveTextures(s.material,C.texture_lod,i),g&&s.material&&!s.isGizmo&&Ee(s.material),bt&&s.material&&!s.isGizmo&&!s.isBatchedMesh&&ze(s.material,C.mesh_lod);for(const a of U)a.onAfterUpdatedLOD?.(this.renderer,t,e,s,C);n.lastLodLevel_Mesh=C.mesh_lod,n.lastLodLevel_Texture=C.texture_lod}loadProgressiveTextures(t,e,s){if(!t)return;if(Array.isArray(t)){for(const i of t)this.loadProgressiveTextures(i,e,s);return}let r=!1;(t[E]===void 0||e<t[E])&&(r=!0);const n=s!==void 0&&s>=0;if(n&&(r=t[E]!=s,e=s),r){t[E]=e;const i=n?{force:!0}:void 0,a=p.assignTextureLOD(t,e,i).then(l=>{this._lodchangedlisteners.forEach(u=>u({type:"texture",level:e,object:t}))});re.addPromise("texture",t,a,this._newPromiseGroups)}}loadProgressiveMeshes(t,e){if(!t)return Promise.resolve(null);let s=t[E]!==e;const r=t["DEBUG:LOD"];if(r!=null&&(s=t[E]!=r,e=r),s){t[E]=e;const n=t.geometry,i=p.assignMeshLOD(t,e).then(a=>(a&&t[E]==e&&n!=t.geometry&&this._lodchangedlisteners.forEach(l=>l({type:"mesh",level:e,object:t})),a));return re.addPromise("mesh",t,i,this._newPromiseGroups),i}return Promise.resolve(null)}_sphere=new d.Sphere;_tempWorldPosition=new d.Vector3;static skinnedMeshBoundsFrameOffsetCounter=0;static $skinnedMeshBoundsOffset=Symbol("gltf-progressive-skinnedMeshBoundsOffset");calculateLodLevel(t,e,s,r,n){if(!e){n.mesh_lod=-1,n.texture_lod=-1;return}if(!t){n.mesh_lod=-1,n.texture_lod=-1;return}let a=10+1,l=!1;if(I&&e["DEBUG:LOD"]!=null)return e["DEBUG:LOD"];const u=p.getMeshLODExtension(e.geometry)?.lods,f=p.getPrimitiveIndex(e.geometry),c=u&&u.length>0,h=p.getMaterialMinMaxLODsCount(e.material),m=h.min_count!==1/0&&h.min_count>=0&&h.max_count>=0;if(!c&&!m){n.mesh_lod=0,n.texture_lod=0;return}c||(l=!0,a=0);const b=this.renderer.domElement.clientHeight||this.renderer.domElement.height;let x=e.geometry.boundingBox;if(e.type==="SkinnedMesh"){const L=e;if(!L.boundingBox)L.computeBoundingBox();else if(this.skinnedMeshAutoUpdateBoundsInterval>0){if(!L[A.$skinnedMeshBoundsOffset]){const w=A.skinnedMeshBoundsFrameOffsetCounter++;L[A.$skinnedMeshBoundsOffset]=w}const v=L[A.$skinnedMeshBoundsOffset];if((s.frames+v)%this.skinnedMeshAutoUpdateBoundsInterval===0){const w=Y(L),W=L.geometry;w&&(L.geometry=w),L.computeBoundingBox(),L.geometry=W}}x=L.boundingBox}if(x){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 w=t.getWorldPosition(this._tempWorldPosition);if(this._sphere.containsPoint(w)){n.mesh_lod=0,n.texture_lod=0;return}}const L=Ve({geometry:e.geometry,matrixWorld:e.matrixWorld,camera:t,projectionScreenMatrix:this.projectionScreenMatrix,desiredDensity:r,canvasHeight:b,currentLevel:s.lastLodLevel_Mesh,boundingBox:x,xrEnabled:this.renderer.xr.enabled,debugDrawLine:I?A.debugDrawLine:void 0,warnMissingPrimitiveDensities:!0});if(s.lastCentrality=L.centrality,s.lastScreenCoverage=L.screenCoverage,s.lastScreenspaceVolume.copy(L.screenspaceVolume),L.screenCoverage===1/0){n.mesh_lod=0,n.texture_lod=0;return}L.level>=0&&L.level<a&&(a=L.level,l=!0)}if(l?n.mesh_lod=a:n.mesh_lod=s.lastLodLevel_Mesh,I&&n.mesh_lod!=s.lastLodLevel_Mesh){const v=u?.[n.mesh_lod];v&&console.log(`Mesh LOD changed: ${s.lastLodLevel_Mesh} → ${n.mesh_lod} (density: ${v.densities?.[f].toFixed(0)}) | ${e.name}`)}if(m){const L="saveData"in globalThis.navigator&&globalThis.navigator.saveData===!0;if(s.lastLodLevel_Texture<0){if(n.texture_lod=h.max_count-1,I){const v=h.lods[h.max_count-1];I&&console.log(`First Texture LOD ${n.texture_lod} (${v.max_height}px) - ${e.name}`)}}else{const v=s.lastScreenspaceVolume.x+s.lastScreenspaceVolume.y+s.lastScreenspaceVolume.z;let w=s.lastScreenCoverage*4;this.context?.engine==="model-viewer"&&(w*=1.5);const W=this.renderer.getPixelRatio?.()||globalThis.devicePixelRatio||1,O=b/W*w;let S=!1;for(let k=h.lods.length-1;k>=0;k--){const R=h.lods[k];if(!(L&&R.max_height>=2048)&&!(_e()&&R.max_height>4096)&&(R.max_height>O||!S&&k===0)){if(S=!0,n.texture_lod=k,I&&n.texture_lod<s.lastLodLevel_Texture){const _=R.max_height;console.log(`Texture LOD changed: ${s.lastLodLevel_Texture} → ${n.texture_lod} = ${_}px
|
|
8
8
|
Screensize: ${O.toFixed(0)}px, Coverage: ${(100*s.lastScreenCoverage).toFixed(2)}%, Volume ${v.toFixed(1)}
|
|
9
9
|
${e.name}`)}break}}}}else n.texture_lod=0}}class It{frames=0;lastLodLevel_Mesh=-1;lastLodLevel_Texture=-1;lastScreenCoverage=0;lastScreenspaceVolume=new d.Vector3;lastCentrality=0}function ze(o,t){if(!(t<0)){if(Array.isArray(o)){for(const e of o)ze(e,t);return}"color"in o&&o.color instanceof d.Color&&(o.color.copy(Ne(t,St)),o.needsUpdate=!0)}}function Ne(o,t){const e=Math.max(0,Math.min(we.length-1,Math.floor(o)));return t.setHex(we[e])}const Pe=Symbol("NEEDLE_mesh_lod"),ne=Symbol("NEEDLE_texture_lod");let ge=null;function be(){const o=$t();o&&(o.mapURLs(function(t){return ke(),t}),ke(),ge?.disconnect(),ge=new MutationObserver(t=>{t.forEach(e=>{e.addedNodes.forEach(s=>{s instanceof HTMLElement&&s.tagName.toLowerCase()==="model-viewer"&&je(s)})})}),ge.observe(document,{childList:!0,subtree:!0}))}function $t(){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"),be()}),null)}function ke(){if(typeof document>"u")return;document.querySelectorAll("model-viewer").forEach(t=>{je(t)})}const Ae=new WeakSet;let Bt=0;function je(o){if(!o||Ae.has(o))return null;Ae.add(o),console.debug("[gltf-progressive] found new model-viewer..."+ ++Bt+`
|
|
10
10
|
`,o.getAttribute("src"));let t=null,e=null,s=null;for(let r=o;r!=null;r=Object.getPrototypeOf(r)){const n=Object.getOwnPropertySymbols(r),i=n.find(u=>u.toString()=="Symbol(renderer)"),a=n.find(u=>u.toString()=="Symbol(scene)"),l=n.find(u=>u.toString()=="Symbol(needsRender)");!t&&i!=null&&(t=o[i].threeRenderer),!e&&a!=null&&(e=o[a]),!s&&l!=null&&(s=o[l])}if(t&&e){let r=function(){if(s){let i=0;const a=setInterval(()=>{if(i++>5){clearInterval(a);return}s?.call(o)},300)}};console.debug("[gltf-progressive] setup model-viewer");const n=A.get(t,{engine:"model-viewer"});return A.addPlugin(new Gt),n.enable(),n.addEventListener("changed",()=>{s?.call(o)}),o.addEventListener("model-visibility",i=>{i.detail.visible&&s?.call(o)}),o.addEventListener("load",()=>{r()}),()=>{n.disable()}}return null}class Gt{_didWarnAboutMissingUrl=!1;onBeforeUpdateLOD(t,e,s,r){this.tryParseMeshLOD(e,r),this.tryParseTextureLOD(e,r)}getUrl(t){if(!t)return null;let e=t.getAttribute("src");return e||(e=t.src),e||(this._didWarnAboutMissingUrl||console.warn("No url found in modelviewer",t),this._didWarnAboutMissingUrl=!0),e}tryGetCurrentGLTF(t){return t._currentGLTF}tryGetCurrentModelViewer(t){return t.element}tryParseTextureLOD(t,e){if(e[ne]==!0)return;e[ne]=!0;const s=this.tryGetCurrentGLTF(t),r=this.tryGetCurrentModelViewer(t),n=this.getUrl(r);if(n&&s&&e.material){let i=function(l){if(l[ne]==!0)return;l[ne]=!0,l.userData&&(l.userData.LOD=-1);const u=Object.keys(l);for(let f=0;f<u.length;f++){const c=u[f],h=l[c];if(h?.isTexture===!0){const m=h.userData?.associations?.textures;if(m==null)continue;const b=s.parser.json.textures[m];if(!b){console.warn("Texture data not found for texture index "+m);continue}if(b?.extensions?.[q]){const x=b.extensions[q];x&&n&&p.registerTexture(n,h,x.lods.length,m,x)}}}};const a=e.material;if(Array.isArray(a))for(const l of a)i(l);else i(a)}}tryParseMeshLOD(t,e){if(e[Pe]==!0)return;e[Pe]=!0;const s=this.tryGetCurrentModelViewer(t),r=this.getUrl(s);if(!r)return;const n=e.userData?.gltfExtensions?.[q];if(n&&r){const i=e.uuid;p.registerMesh(r,i,e,0,n.lods.length,n)}}}function Xe(...o){let t,e,s,r;switch(o.length){case 2:[s,e]=o,r={};break;case 3:[s,e,r]=o;break;case 4:[t,e,s,r]=o;break;default:throw new Error("Invalid arguments")}ie(e),De(s),Me(s,{progressive:!0,...r?.hints}),s.register(i=>new p(i));const n=A.get(e);return r?.enableLODsManager!==!1&&n.enable(),n}be();if(!xt){const o={gltfProgressive:{useNeedleProgressive:Xe,LODsManager:A,configureLoader:Me,getRaycastMesh:Y,useRaycastMeshes:We}};if(!globalThis.Needle)globalThis.Needle=o;else for(const t in o)globalThis.Needle[t]=o[t]}exports.EXTENSION_NAME=q;exports.LODsManager=A;exports.NEEDLE_progressive=p;exports.VERSION=Ie;exports.addDracoAndKTX2Loaders=De;exports.calculateMeshLODLevel=Ve;exports.configureLoader=Me;exports.createLoaders=ie;exports.getLODColor=Ne;exports.getRaycastMesh=Y;exports.lodDebugColors=we;exports.patchModelViewer=be;exports.registerRaycastMesh=Ue;exports.setDracoDecoderLocation=Be;exports.setKTX2TranscoderLocation=Ge;exports.useNeedleProgressive=Xe;exports.useRaycastMeshes=We;
|
package/lib/lods.manager.js
CHANGED
|
@@ -88,10 +88,8 @@ export function calculateMeshLODLevel(options) {
|
|
|
88
88
|
result.screenCoverage = 0;
|
|
89
89
|
result.screenspaceVolume.set(0, 0, 0);
|
|
90
90
|
result.centrality = 1;
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
// the texture LOD selection, which must keep working for meshes that only have
|
|
94
|
-
// texture LODs (no mesh LODs). Only the mesh LOD-level selection loop is skipped.
|
|
91
|
+
if (!meshLods?.length)
|
|
92
|
+
return result;
|
|
95
93
|
let boundingBox = options.boundingBox ?? geometry.boundingBox;
|
|
96
94
|
if (!boundingBox) {
|
|
97
95
|
geometry.computeBoundingBox();
|
|
@@ -167,18 +165,16 @@ export function calculateMeshLODLevel(options) {
|
|
|
167
165
|
debugDrawLine(_meshLODCorner1, _meshLODCorner3, 0x0000ff);
|
|
168
166
|
debugDrawLine(_meshLODCorner2, _meshLODCorner3, 0x0000ff);
|
|
169
167
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
break;
|
|
181
|
-
}
|
|
168
|
+
for (let i = 0; i < meshLods.length; i++) {
|
|
169
|
+
const lod = meshLods[i];
|
|
170
|
+
const density = lod.densities?.[primitiveIndex] || lod.density || .00001;
|
|
171
|
+
if (primitiveIndex > 0 && warnMissingPrimitiveDensities && isDevelopmentServer() && !lod.densities && !globalThis["NEEDLE:MISSING_LOD_PRIMITIVE_DENSITIES"]) {
|
|
172
|
+
globalThis["NEEDLE:MISSING_LOD_PRIMITIVE_DENSITIES"] = true;
|
|
173
|
+
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.`);
|
|
174
|
+
}
|
|
175
|
+
if (density / screenCoverage < desiredDensity) {
|
|
176
|
+
result.level = i;
|
|
177
|
+
break;
|
|
182
178
|
}
|
|
183
179
|
}
|
|
184
180
|
return result;
|
package/lib/version.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@needle-tools/gltf-progressive",
|
|
3
|
-
"version": "3.6.0-
|
|
3
|
+
"version": "3.6.0-canary.51f6448",
|
|
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": {
|
|
@@ -80,4 +80,4 @@
|
|
|
80
80
|
"vitest": "^4.1.8"
|
|
81
81
|
},
|
|
82
82
|
"types": "./lib/index.d.ts"
|
|
83
|
-
}
|
|
83
|
+
}
|