@needle-tools/gltf-progressive 3.3.2 → 3.3.3-next.90044cd
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 +3 -0
- package/NEEDLE_progressive/README.md +106 -0
- package/README.md +9 -4
- package/gltf-progressive.js +82 -79
- package/gltf-progressive.min.js +5 -5
- package/gltf-progressive.umd.cjs +6 -6
- package/lib/extension.d.ts +3 -28
- package/lib/extension.js +14 -14
- package/lib/extension.model.d.ts +33 -0
- package/lib/extension.model.js +1 -0
- package/lib/lods.debug.js +10 -8
- package/lib/plugins/modelviewer.js +1 -1
- package/lib/plugins/plugin.d.ts +1 -1
- package/lib/utils.internal.d.ts +11 -1
- package/lib/utils.internal.js +18 -10
- package/lib/version.js +1 -1
- package/package.json +5 -4
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,9 @@ 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.3.3] - 2025-09-08
|
|
8
|
+
- Update documentation and add an initial extension definition
|
|
9
|
+
|
|
7
10
|
## [3.3.2] - 2025-08-20
|
|
8
11
|
- Update KTX2 transcoder
|
|
9
12
|
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# NEEDLE Progressive Extensions Schema
|
|
2
|
+
|
|
3
|
+
This document describes the NEEDLE progressive mesh and texture extensions.
|
|
4
|
+
|
|
5
|
+
## Base Types
|
|
6
|
+
|
|
7
|
+
### NEEDLE_progressive_model_LOD
|
|
8
|
+
|
|
9
|
+
Represents a single Level of Detail (LOD) file reference.
|
|
10
|
+
|
|
11
|
+
| Property | Type | Required | Description |
|
|
12
|
+
|----------|------|----------|-------------|
|
|
13
|
+
| `path` | `string` | Yes | Relative path to the LOD file |
|
|
14
|
+
| `hash` | `string` | No | Optional hash for file verification |
|
|
15
|
+
|
|
16
|
+
### NEEDLE_progressive_ext
|
|
17
|
+
|
|
18
|
+
Base extension format for progressive assets.
|
|
19
|
+
|
|
20
|
+
| Property | Type | Required | Description |
|
|
21
|
+
|----------|------|----------|-------------|
|
|
22
|
+
| `guid` | `string` | Yes | Unique identifier of the object (texture, mesh) the LODs belong to |
|
|
23
|
+
| `lods` | `Array<NEEDLE_progressive_model_LOD>` | Yes | Array of available LOD levels |
|
|
24
|
+
|
|
25
|
+
## Extension Types
|
|
26
|
+
|
|
27
|
+
### NEEDLE_ext_progressive_texture
|
|
28
|
+
|
|
29
|
+
Texture extension for progressive texture loading, inheriting from `NEEDLE_progressive_ext`.
|
|
30
|
+
|
|
31
|
+
| Property | Type | Required | Description |
|
|
32
|
+
|----------|------|----------|-------------|
|
|
33
|
+
| `guid` | `string` | Yes | Inherited from base type |
|
|
34
|
+
| `lods` | `Array<TextureLOD>` | Yes | Array of texture LOD levels with dimensions |
|
|
35
|
+
|
|
36
|
+
**TextureLOD Properties:**
|
|
37
|
+
| Property | Type | Required | Description |
|
|
38
|
+
|----------|------|----------|-------------|
|
|
39
|
+
| `path` | `string` | Yes | Relative path to the texture LOD file |
|
|
40
|
+
| `hash` | `string` | No | Optional hash for file verification |
|
|
41
|
+
| `width` | `number` | Yes | Texture width in pixels |
|
|
42
|
+
| `height` | `number` | Yes | Texture height in pixels |
|
|
43
|
+
|
|
44
|
+
### NEEDLE_ext_progressive_mesh
|
|
45
|
+
|
|
46
|
+
Mesh extension for progressive mesh loading, inheriting from `NEEDLE_progressive_ext`.
|
|
47
|
+
|
|
48
|
+
| Property | Type | Required | Description |
|
|
49
|
+
|----------|------|----------|-------------|
|
|
50
|
+
| `guid` | `string` | Yes | Inherited from base type |
|
|
51
|
+
| `lods` | `Array<MeshLOD>` | Yes | Array of mesh LOD levels with geometry data |
|
|
52
|
+
|
|
53
|
+
**MeshLOD Properties:**
|
|
54
|
+
| Property | Type | Required | Description |
|
|
55
|
+
|----------|------|----------|-------------|
|
|
56
|
+
| `path` | `string` | Yes | Relative path to the mesh LOD file |
|
|
57
|
+
| `hash` | `string` | No | Optional hash for file verification |
|
|
58
|
+
| `densities` | `number[]` | Yes | Density values per primitive |
|
|
59
|
+
| `indexCount` | `number` | No | Number of indices in LOD0 |
|
|
60
|
+
| `vertexCount` | `number` | No | Number of vertices in LOD0 |
|
|
61
|
+
|
|
62
|
+
## Usage Examples
|
|
63
|
+
|
|
64
|
+
### Texture Extension Example
|
|
65
|
+
|
|
66
|
+
```json
|
|
67
|
+
{
|
|
68
|
+
"guid": "texture-asset-123",
|
|
69
|
+
"lods": [
|
|
70
|
+
{
|
|
71
|
+
"path": "./textures/image_diffuse_1024.glb",
|
|
72
|
+
"hash": "abc123",
|
|
73
|
+
"width": 1024,
|
|
74
|
+
"height": 1024
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
"path": "./textures/image_diffuse_512.glb",
|
|
78
|
+
"width": 512,
|
|
79
|
+
"height": 512
|
|
80
|
+
}
|
|
81
|
+
]
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Mesh Extension Example
|
|
86
|
+
|
|
87
|
+
```json
|
|
88
|
+
{
|
|
89
|
+
"guid": "mesh-asset-456",
|
|
90
|
+
"lods": [
|
|
91
|
+
{
|
|
92
|
+
"path": "./meshes/mesh_0_high_detail.glb",
|
|
93
|
+
"hash": "def456",
|
|
94
|
+
"indexCount": 15000,
|
|
95
|
+
"vertexCount": 8000,
|
|
96
|
+
"densities": [100000, 50000]
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"path": "./meshes/mesh_1_low_detail.glb",
|
|
100
|
+
"indexCount": 3000,
|
|
101
|
+
"vertexCount": 1500,
|
|
102
|
+
"densities": [100000, 50000]
|
|
103
|
+
}
|
|
104
|
+
]
|
|
105
|
+
}
|
|
106
|
+
```
|
package/README.md
CHANGED
|
@@ -40,12 +40,12 @@ Examples are in the `/examples` directory. Live versions can be found in the lin
|
|
|
40
40
|
- [Codesandbox](https://codesandbox.io/dashboard/sandboxes/gltf-progressive)
|
|
41
41
|
|
|
42
42
|
|
|
43
|
-
## Videos
|
|
43
|
+
<!-- ## Videos
|
|
44
44
|
<a href="https://youtu.be/7EjL0BRfIp8" target="_blank"></a>
|
|
46
46
|
*Progressive glTF — comparison with traditional three.js optimization*
|
|
47
47
|
|
|
48
|
-
<br/>
|
|
48
|
+
<br/> -->
|
|
49
49
|
|
|
50
50
|
# Usage
|
|
51
51
|
|
|
@@ -132,6 +132,10 @@ Full model-viewer example at: `examples/modelviewer.html`
|
|
|
132
132
|
[Needle Engine](https://needle.tools) natively supports progressive loading of these glTF files! See [docs.needle.tools](https://docs.needle.tools) for more information.
|
|
133
133
|
|
|
134
134
|
|
|
135
|
+
# How can I generate assets for progressive loading
|
|
136
|
+
Use [Needle Cloud](https://cloud.needle.tools) to generate LODs for your assets (includes hosting, global CDN, password protection, versioning, CLI support...) or use one of the [Needle integrations for Unity or Blender](https://engine.needle.tools/docs/getting-started/#choose-your-workflow).
|
|
137
|
+
|
|
138
|
+
|
|
135
139
|
# Advanced
|
|
136
140
|
|
|
137
141
|
### Add a LOD Manager plugin to receive callbacks per object
|
|
@@ -158,8 +162,9 @@ Simply call `useRaycastMeshes(true)` to enable faster raycasting when using the
|
|
|
158
162
|
Call `getRaycastMesh(<your_mesh_object>)`
|
|
159
163
|
|
|
160
164
|
|
|
161
|
-
#
|
|
162
|
-
|
|
165
|
+
# Extension
|
|
166
|
+
Read more about the [NEEDLE_progressive extension](./NEEDLE_progressive/README.md)
|
|
167
|
+
|
|
163
168
|
|
|
164
169
|
# Contact ✒️
|
|
165
170
|
<b>[🌵 needle — tools for creators](https://needle.tools)</b> •
|
package/gltf-progressive.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { BufferGeometry as
|
|
1
|
+
import { BufferGeometry as J, Mesh as q, Box3 as he, Vector3 as A, Sphere as De, CompressedTexture as $e, Texture as E, Matrix3 as Ge, InterleavedBuffer as Fe, InterleavedBufferAttribute as We, BufferAttribute as Ue, TextureLoader as ze, Matrix4 as _e, Clock as Ee, MeshStandardMaterial as Ne } from "three";
|
|
2
2
|
import { GLTFLoader as ye } from "three/examples/jsm/loaders/GLTFLoader.js";
|
|
3
3
|
import { MeshoptDecoder as qe } from "three/examples/jsm/libs/meshopt_decoder.module.js";
|
|
4
4
|
import { DRACOLoader as Ve } from "three/examples/jsm/loaders/DRACOLoader.js";
|
|
5
5
|
import { KTX2Loader as Xe } from "three/examples/jsm/loaders/KTX2Loader.js";
|
|
6
|
-
const
|
|
7
|
-
globalThis.GLTF_PROGRESSIVE_VERSION =
|
|
6
|
+
const je = "";
|
|
7
|
+
globalThis.GLTF_PROGRESSIVE_VERSION = je;
|
|
8
8
|
console.debug("[gltf-progressive] version -");
|
|
9
|
-
let
|
|
10
|
-
const
|
|
9
|
+
let C = "https://www.gstatic.com/draco/versioned/decoders/1.5.7/", V = "https://cdn.needle.tools/static/three/0.179.1/basis2/";
|
|
10
|
+
const Ke = C, Ye = V, Oe = new URL(C + "draco_decoder.js");
|
|
11
11
|
Oe.searchParams.append("range", "true");
|
|
12
12
|
fetch(Oe, {
|
|
13
13
|
method: "GET",
|
|
@@ -15,16 +15,16 @@ fetch(Oe, {
|
|
|
15
15
|
Range: "bytes=0-1"
|
|
16
16
|
}
|
|
17
17
|
}).catch((i) => {
|
|
18
|
-
console.debug(`Failed to fetch remote Draco decoder from ${
|
|
18
|
+
console.debug(`Failed to fetch remote Draco decoder from ${C} (offline: ${typeof navigator < "u" ? navigator.onLine : "unknown"})`), C === Ke && Qe("./include/draco/"), V === Ye && Je("./include/ktx2/");
|
|
19
19
|
}).finally(() => {
|
|
20
20
|
Pe();
|
|
21
21
|
});
|
|
22
22
|
const He = () => ({
|
|
23
|
-
dracoDecoderPath:
|
|
23
|
+
dracoDecoderPath: C,
|
|
24
24
|
ktx2TranscoderPath: V
|
|
25
25
|
});
|
|
26
26
|
function Qe(i) {
|
|
27
|
-
|
|
27
|
+
C = i, T && T[ge] != C ? (console.debug("Updating Draco decoder path to " + i), T[ge] = C, T.setDecoderPath(C), T.preload()) : console.debug("Setting Draco decoder path to " + i);
|
|
28
28
|
}
|
|
29
29
|
function Je(i) {
|
|
30
30
|
V = i, R && R.transcoderPath != V ? (console.debug("Updating KTX2 transcoder path to " + i), R.setTranscoderPath(V), R.init()) : console.debug("Setting KTX2 transcoder path to " + i);
|
|
@@ -38,7 +38,7 @@ function Se(i) {
|
|
|
38
38
|
const ge = Symbol("dracoDecoderPath");
|
|
39
39
|
let T, ne, R;
|
|
40
40
|
function Pe() {
|
|
41
|
-
T || (T = new Ve(), T[ge] =
|
|
41
|
+
T || (T = new Ve(), T[ge] = C, T.setDecoderPath(C), T.setDecoderConfig({ type: "js" }), T.preload()), R || (R = new Xe(), R.setTranscoderPath(V), R.init()), ne || (ne = qe);
|
|
42
42
|
}
|
|
43
43
|
const pe = /* @__PURE__ */ new WeakMap();
|
|
44
44
|
function Te(i, t) {
|
|
@@ -64,7 +64,7 @@ function X(i) {
|
|
|
64
64
|
return e == null || e === "0" || e === "false" ? !1 : e === "" ? !0 : e;
|
|
65
65
|
}
|
|
66
66
|
function tt(i, t) {
|
|
67
|
-
if (t === void 0 || t.startsWith("./") || t.startsWith("http") ||
|
|
67
|
+
if (t === void 0 || i === void 0 || t.startsWith("./") || t.startsWith("http") || t.startsWith("data:") || t.startsWith("blob:"))
|
|
68
68
|
return t;
|
|
69
69
|
const e = i.lastIndexOf("/");
|
|
70
70
|
if (e >= 0) {
|
|
@@ -74,10 +74,10 @@ function tt(i, t) {
|
|
|
74
74
|
}
|
|
75
75
|
return t;
|
|
76
76
|
}
|
|
77
|
-
let j;
|
|
78
77
|
function Ae() {
|
|
79
|
-
return
|
|
78
|
+
return K !== void 0 || (K = /iPhone|iPad|iPod|Android|IEMobile/i.test(navigator.userAgent), X("debugprogressive") && console.log("[glTF Progressive]: isMobileDevice", K)), K;
|
|
80
79
|
}
|
|
80
|
+
let K;
|
|
81
81
|
function Le() {
|
|
82
82
|
if (typeof window > "u") return !1;
|
|
83
83
|
const i = new URL(window.location.href), t = i.hostname === "localhost" || /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(i.hostname);
|
|
@@ -118,11 +118,11 @@ class st {
|
|
|
118
118
|
}
|
|
119
119
|
}
|
|
120
120
|
const rt = typeof window > "u" && typeof document > "u", me = Symbol("needle:raycast-mesh");
|
|
121
|
-
function
|
|
122
|
-
return i?.[me] instanceof
|
|
121
|
+
function Z(i) {
|
|
122
|
+
return i?.[me] instanceof J ? i[me] : null;
|
|
123
123
|
}
|
|
124
124
|
function nt(i, t) {
|
|
125
|
-
if ((i.type === "Mesh" || i.type === "SkinnedMesh") && !
|
|
125
|
+
if ((i.type === "Mesh" || i.type === "SkinnedMesh") && !Z(i)) {
|
|
126
126
|
const s = it(t);
|
|
127
127
|
s.userData = { isRaycastMesh: !0 }, i[me] = s;
|
|
128
128
|
}
|
|
@@ -132,7 +132,7 @@ function ot(i = !0) {
|
|
|
132
132
|
if (Y) return;
|
|
133
133
|
const t = Y = q.prototype.raycast;
|
|
134
134
|
q.prototype.raycast = function(e, s) {
|
|
135
|
-
const n = this, r =
|
|
135
|
+
const n = this, r = Z(n);
|
|
136
136
|
let o;
|
|
137
137
|
r && n.isMesh && (o = n.geometry, n.geometry = r), t.call(this, e, s), o && (n.geometry = o);
|
|
138
138
|
};
|
|
@@ -143,29 +143,29 @@ function ot(i = !0) {
|
|
|
143
143
|
}
|
|
144
144
|
let Y = null;
|
|
145
145
|
function it(i) {
|
|
146
|
-
const t = new
|
|
146
|
+
const t = new J();
|
|
147
147
|
for (const e in i.attributes)
|
|
148
148
|
t.setAttribute(e, i.getAttribute(e));
|
|
149
149
|
return t.setIndex(i.getIndex()), t;
|
|
150
150
|
}
|
|
151
|
-
const
|
|
152
|
-
let
|
|
151
|
+
const U = new Array(), g = X("debugprogressive");
|
|
152
|
+
let Q, N = -1;
|
|
153
153
|
if (g) {
|
|
154
154
|
let i = function() {
|
|
155
155
|
N += 1, N >= t && (N = -1), console.log(`Toggle LOD level [${N}]`);
|
|
156
156
|
}, t = 6;
|
|
157
157
|
window.addEventListener("keyup", (e) => {
|
|
158
|
-
e.key === "p" && i(), e.key === "w" && (
|
|
158
|
+
e.key === "p" && i(), e.key === "w" && (Q = !Q, console.log(`Toggle wireframe [${Q}]`));
|
|
159
159
|
const s = parseInt(e.key);
|
|
160
160
|
!isNaN(s) && s >= 0 && (N = s, console.log(`Set LOD level to [${N}]`));
|
|
161
161
|
});
|
|
162
162
|
}
|
|
163
163
|
function Ie(i) {
|
|
164
|
-
if (g)
|
|
164
|
+
if (g && Q !== void 0)
|
|
165
165
|
if (Array.isArray(i))
|
|
166
166
|
for (const t of i)
|
|
167
167
|
Ie(t);
|
|
168
|
-
else i && "wireframe" in i && (i.wireframe =
|
|
168
|
+
else i && "wireframe" in i && (i.wireframe = Q === !0);
|
|
169
169
|
}
|
|
170
170
|
const H = new Array();
|
|
171
171
|
let at = 0;
|
|
@@ -238,7 +238,7 @@ class we {
|
|
|
238
238
|
}
|
|
239
239
|
function dt(i) {
|
|
240
240
|
for (const t of i.geometries) {
|
|
241
|
-
const e = t.geometry, s = new
|
|
241
|
+
const e = t.geometry, s = new J();
|
|
242
242
|
if (s.name = e.name || "", e.index) {
|
|
243
243
|
const n = e.index;
|
|
244
244
|
s.setIndex(ae(n));
|
|
@@ -316,8 +316,8 @@ function ae(i) {
|
|
|
316
316
|
let t = i;
|
|
317
317
|
if ("isInterleavedBufferAttribute" in i && i.isInterleavedBufferAttribute) {
|
|
318
318
|
const e = i.data, s = e.array, n = new Fe(s, e.stride);
|
|
319
|
-
t = new
|
|
320
|
-
} else "isBufferAttribute" in i && i.isBufferAttribute && (t = new
|
|
319
|
+
t = new We(n, i.itemSize, s.byteOffset, i.normalized), t.offset = i.offset;
|
|
320
|
+
} else "isBufferAttribute" in i && i.isBufferAttribute && (t = new Ue(i.array, i.itemSize, i.normalized), t.usage = i.usage, t.gpuType = i.gpuType, t.updateRanges = i.updateRanges);
|
|
321
321
|
return t;
|
|
322
322
|
}
|
|
323
323
|
const ct = X("gltf-progressive-worker"), ft = X("gltf-progressive-reduce-mipmaps"), le = Symbol("needle-progressive-texture"), F = "NEEDLE_progressive";
|
|
@@ -326,6 +326,7 @@ class y {
|
|
|
326
326
|
get name() {
|
|
327
327
|
return F;
|
|
328
328
|
}
|
|
329
|
+
// #region PUBLIC API
|
|
329
330
|
static getMeshLODExtension(t) {
|
|
330
331
|
const e = this.getAssignedLODInformation(t);
|
|
331
332
|
return e?.key ? this.lodInfos.get(e.key) : null;
|
|
@@ -426,7 +427,7 @@ class y {
|
|
|
426
427
|
const s = t.geometry, n = this.getAssignedLODInformation(s);
|
|
427
428
|
if (!n)
|
|
428
429
|
return Promise.resolve(null);
|
|
429
|
-
for (const r of
|
|
430
|
+
for (const r of U)
|
|
430
431
|
r.onBeforeGetLODMesh?.(t, e);
|
|
431
432
|
return t["LOD:requested level"] = e, y.getOrLoadLOD(s, e).then((r) => {
|
|
432
433
|
if (Array.isArray(r)) {
|
|
@@ -491,6 +492,7 @@ class y {
|
|
|
491
492
|
}
|
|
492
493
|
return Promise.resolve(null);
|
|
493
494
|
}
|
|
495
|
+
// #region INTERNAL
|
|
494
496
|
static assignTextureLODForSlot(t, e, s, n) {
|
|
495
497
|
return t?.isTexture !== !0 ? Promise.resolve(null) : n === "glyphMap" ? Promise.resolve(t) : y.getOrLoadLOD(t, e).then((r) => {
|
|
496
498
|
if (Array.isArray(r))
|
|
@@ -575,12 +577,12 @@ class y {
|
|
|
575
577
|
*/
|
|
576
578
|
static registerTexture = (t, e, s, n, r) => {
|
|
577
579
|
if (!e) {
|
|
578
|
-
g && console.error("gltf-progressive: Called register texture without texture");
|
|
580
|
+
g && console.error("!! gltf-progressive: Called register texture without texture");
|
|
579
581
|
return;
|
|
580
582
|
}
|
|
581
583
|
if (g) {
|
|
582
584
|
const l = e.image?.width || e.source?.data?.width || 0, a = e.image?.height || e.source?.data?.height || 0;
|
|
583
|
-
console.log(`>
|
|
585
|
+
console.log(`> gltf-progressive: register texture[${n}] "${e.name || e.uuid}", Current: ${l}x${a}, Max: ${r.lods[0]?.width}x${r.lods[0]?.height}, uuid: ${e.uuid}`, r, e);
|
|
584
586
|
}
|
|
585
587
|
e.source && (e.source[le] = r);
|
|
586
588
|
const o = r.guid;
|
|
@@ -597,8 +599,8 @@ class y {
|
|
|
597
599
|
}
|
|
598
600
|
l.userData || (l.userData = {}), g && console.log("> Progressive: register mesh " + s.name, { index: r, uuid: s.uuid }, o, s), y.assignLODInformation(t, l, e, n, r), y.lodInfos.set(e, o);
|
|
599
601
|
let a = y.lowresCache.get(e);
|
|
600
|
-
a ? a.push(s.geometry) : a = [s.geometry], y.lowresCache.set(e, a), n > 0 && !
|
|
601
|
-
for (const u of
|
|
602
|
+
a ? a.push(s.geometry) : a = [s.geometry], y.lowresCache.set(e, a), n > 0 && !Z(s) && nt(s, l);
|
|
603
|
+
for (const u of U)
|
|
602
604
|
u.onRegisteredNewMesh?.(s, o);
|
|
603
605
|
};
|
|
604
606
|
/** A map of key = asset uuid and value = LOD information */
|
|
@@ -619,7 +621,9 @@ class y {
|
|
|
619
621
|
const a = t;
|
|
620
622
|
a.source && a.source[le] && (o = a.source[le]);
|
|
621
623
|
}
|
|
622
|
-
if (o || (o = y.lodInfos.get(r)), o)
|
|
624
|
+
if (o || (o = y.lodInfos.get(r)), !o)
|
|
625
|
+
g && console.warn(`Can not load LOD ${e}: no LOD info found for "${r}" ${t.name}`, t.type, y.lodInfos);
|
|
626
|
+
else {
|
|
623
627
|
if (e > 0) {
|
|
624
628
|
let c = !1;
|
|
625
629
|
const p = Array.isArray(o.lods);
|
|
@@ -638,7 +642,7 @@ class y {
|
|
|
638
642
|
s && console.log(`LOD ${e} was already loading/loaded: ${c}`);
|
|
639
643
|
let d = await _.catch((O) => (console.error(`Error loading LOD ${e} from ${u}
|
|
640
644
|
`, O), null)), x = !1;
|
|
641
|
-
if (d == null || (d instanceof E && t instanceof E ? d.image?.data || d.source?.data ? d = this.copySettings(t, d) : (x = !0, this.previouslyLoaded.delete(c)) : d instanceof
|
|
645
|
+
if (d == null || (d instanceof E && t instanceof E ? d.image?.data || d.source?.data ? d = this.copySettings(t, d) : (x = !0, this.previouslyLoaded.delete(c)) : d instanceof J && t instanceof J && (d.attributes.position?.array || (x = !0, this.previouslyLoaded.delete(c)))), !x)
|
|
642
646
|
return d;
|
|
643
647
|
}
|
|
644
648
|
if (!p.use)
|
|
@@ -730,8 +734,7 @@ class y {
|
|
|
730
734
|
const p = await new ze().loadAsync(u);
|
|
731
735
|
return p ? (p.guid = o.guid, p.flipY = !1, p.needsUpdate = !0, p.colorSpace = t.colorSpace, s && console.log(o, p)) : g && console.warn("failed loading", u), p;
|
|
732
736
|
}
|
|
733
|
-
}
|
|
734
|
-
g && console.warn(`Can not load LOD ${e}: no LOD info found for "${r}" ${t.name}`, t.type);
|
|
737
|
+
}
|
|
735
738
|
return null;
|
|
736
739
|
}
|
|
737
740
|
static maxConcurrent = 50;
|
|
@@ -839,7 +842,7 @@ class ue {
|
|
|
839
842
|
});
|
|
840
843
|
}
|
|
841
844
|
}
|
|
842
|
-
const
|
|
845
|
+
const k = X("debugprogressive"), gt = X("noprogressive"), de = Symbol("Needle:LODSManager"), ce = Symbol("Needle:LODState"), W = Symbol("Needle:CurrentLOD"), P = { mesh_lod: -1, texture_lod: -1 };
|
|
843
846
|
class L {
|
|
844
847
|
/**
|
|
845
848
|
* Assign a function to draw debug lines for the LODs. This function will be called with the start and end position of the line and the color of the line when the `debugprogressive` query parameter is set.
|
|
@@ -850,11 +853,11 @@ class L {
|
|
|
850
853
|
return t[ce];
|
|
851
854
|
}
|
|
852
855
|
static addPlugin(t) {
|
|
853
|
-
|
|
856
|
+
U.push(t);
|
|
854
857
|
}
|
|
855
858
|
static removePlugin(t) {
|
|
856
|
-
const e =
|
|
857
|
-
e >= 0 &&
|
|
859
|
+
const e = U.indexOf(t);
|
|
860
|
+
e >= 0 && U.splice(e, 1);
|
|
858
861
|
}
|
|
859
862
|
/**
|
|
860
863
|
* Gets the LODsManager for the given renderer. If the LODsManager does not exist yet, it will be created.
|
|
@@ -875,7 +878,7 @@ class L {
|
|
|
875
878
|
projectionScreenMatrix = new _e();
|
|
876
879
|
/** @deprecated use static `LODsManager.addPlugin()` method. This getter will be removed in later versions */
|
|
877
880
|
get plugins() {
|
|
878
|
-
return
|
|
881
|
+
return U;
|
|
879
882
|
}
|
|
880
883
|
/**
|
|
881
884
|
* Force override the LOD level for all objects (meshes + textures) rendered in the scene
|
|
@@ -964,7 +967,7 @@ class L {
|
|
|
964
967
|
const e = this;
|
|
965
968
|
xe(this.renderer), this.renderer.render = function(s, n) {
|
|
966
969
|
const r = e.renderer.getRenderTarget();
|
|
967
|
-
(r == null || "isXRRenderTarget" in r && r.isXRRenderTarget) && (t = 0, e.#r += 1, e.#n = e.#o.getDelta(), e.#i += e.#n, e._fpsBuffer.shift(), e._fpsBuffer.push(1 / e.#n), e.#s = e._fpsBuffer.reduce((l, a) => l + a) / e._fpsBuffer.length,
|
|
970
|
+
(r == null || "isXRRenderTarget" in r && r.isXRRenderTarget) && (t = 0, e.#r += 1, e.#n = e.#o.getDelta(), e.#i += e.#n, e._fpsBuffer.shift(), e._fpsBuffer.push(1 / e.#n), e.#s = e._fpsBuffer.reduce((l, a) => l + a) / e._fpsBuffer.length, k && e.#r % 200 === 0 && console.log("FPS", Math.round(e.#s), "Interval:", e.#e));
|
|
968
971
|
const o = t++;
|
|
969
972
|
e.#t.call(this, s, n), e.onAfterRender(s, n, o);
|
|
970
973
|
};
|
|
@@ -984,7 +987,7 @@ class L {
|
|
|
984
987
|
(l.name === "EffectMaterial" || l.name === "CopyShader") && (o = !1);
|
|
985
988
|
}
|
|
986
989
|
if ((e.parent && e.parent.type === "CubeCamera" || s >= 1 && e.type === "OrthographicCamera") && (o = !1), o) {
|
|
987
|
-
if (gt || (this.updateInterval === "auto" ? this.#s < 40 && this.#e < 10 ? (this.#e += 1,
|
|
990
|
+
if (gt || (this.updateInterval === "auto" ? this.#s < 40 && this.#e < 10 ? (this.#e += 1, k && console.warn("↓ Reducing LOD updates", this.#e, this.#s.toFixed(0))) : this.#s >= 60 && this.#e > 1 && (this.#e -= 1, k && console.warn("↑ Increasing LOD updates", this.#e, this.#s.toFixed(0))) : this.#e = this.updateInterval, this.#e > 0 && this.#r % this.#e != 0))
|
|
988
991
|
return;
|
|
989
992
|
this.internalUpdate(t, e), this._postprocessPromiseGroups();
|
|
990
993
|
}
|
|
@@ -998,7 +1001,7 @@ class L {
|
|
|
998
1001
|
const r = this.targetTriangleDensity;
|
|
999
1002
|
for (const a of n) {
|
|
1000
1003
|
if (a.material && (a.geometry?.type === "BoxGeometry" || a.geometry?.type === "BufferGeometry") && (a.material.name === "SphericalGaussianBlur" || a.material.name == "BackgroundCubeMaterial" || a.material.name === "CubemapFromEquirect" || a.material.name === "EquirectangularToCubeUV")) {
|
|
1001
|
-
|
|
1004
|
+
k && (a.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"] || (a.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"] = !0, console.warn("Ignoring skybox or BLIT object", a, a.material.name, a.material.type)));
|
|
1002
1005
|
continue;
|
|
1003
1006
|
}
|
|
1004
1007
|
switch (a.material.type) {
|
|
@@ -1010,7 +1013,7 @@ class L {
|
|
|
1010
1013
|
case "MeshDepthMaterial":
|
|
1011
1014
|
continue;
|
|
1012
1015
|
}
|
|
1013
|
-
if (
|
|
1016
|
+
if (k === "color" && a.material && !a.object.progressive_debug_color) {
|
|
1014
1017
|
a.object.progressive_debug_color = !0;
|
|
1015
1018
|
const c = Math.random() * 16777215, p = new Ne({ color: c });
|
|
1016
1019
|
a.object.material = p;
|
|
@@ -1035,11 +1038,11 @@ class L {
|
|
|
1035
1038
|
let r = s[ce];
|
|
1036
1039
|
if (r || (r = new pt(), s[ce] = r), r.frames++ < 2)
|
|
1037
1040
|
return;
|
|
1038
|
-
for (const l of
|
|
1041
|
+
for (const l of U)
|
|
1039
1042
|
l.onBeforeUpdateLOD?.(this.renderer, t, e, s);
|
|
1040
1043
|
const o = this.overrideLodLevel !== void 0 ? this.overrideLodLevel : N;
|
|
1041
1044
|
o >= 0 ? (P.mesh_lod = o, P.texture_lod = o) : (this.calculateLodLevel(e, s, r, n, P), P.mesh_lod = Math.round(P.mesh_lod), P.texture_lod = Math.round(P.texture_lod)), P.mesh_lod >= 0 && this.loadProgressiveMeshes(s, P.mesh_lod), s.material && P.texture_lod >= 0 && this.loadProgressiveTextures(s.material, P.texture_lod, o), g && s.material && !s.isGizmo && Ie(s.material);
|
|
1042
|
-
for (const l of
|
|
1045
|
+
for (const l of U)
|
|
1043
1046
|
l.onAfterUpdatedLOD?.(this.renderer, t, e, s, P);
|
|
1044
1047
|
r.lastLodLevel_Mesh = P.mesh_lod, r.lastLodLevel_Texture = P.texture_lod;
|
|
1045
1048
|
}
|
|
@@ -1056,8 +1059,8 @@ class L {
|
|
|
1056
1059
|
return;
|
|
1057
1060
|
}
|
|
1058
1061
|
let n = !1;
|
|
1059
|
-
if ((t[
|
|
1060
|
-
t[
|
|
1062
|
+
if ((t[W] === void 0 || e < t[W]) && (n = !0), s !== void 0 && s >= 0 && (n = t[W] != s, e = s), n) {
|
|
1063
|
+
t[W] = e;
|
|
1061
1064
|
const r = y.assignTextureLOD(t, e).then((o) => {
|
|
1062
1065
|
this._lodchangedlisteners.forEach((l) => l({ type: "texture", level: e, object: t }));
|
|
1063
1066
|
});
|
|
@@ -1072,11 +1075,11 @@ class L {
|
|
|
1072
1075
|
*/
|
|
1073
1076
|
loadProgressiveMeshes(t, e) {
|
|
1074
1077
|
if (!t) return Promise.resolve(null);
|
|
1075
|
-
let s = t[
|
|
1078
|
+
let s = t[W] !== e;
|
|
1076
1079
|
const n = t["DEBUG:LOD"];
|
|
1077
|
-
if (n != null && (s = t[
|
|
1078
|
-
t[
|
|
1079
|
-
const r = t.geometry, o = y.assignMeshLOD(t, e).then((l) => (l && t[
|
|
1080
|
+
if (n != null && (s = t[W] != n, e = n), s) {
|
|
1081
|
+
t[W] = e;
|
|
1082
|
+
const r = t.geometry, o = y.assignMeshLOD(t, e).then((l) => (l && t[W] == e && r != t.geometry && this._lodchangedlisteners.forEach((a) => a({ type: "mesh", level: e, object: t })), l));
|
|
1080
1083
|
return ue.addPromise("mesh", t, o, this._newPromiseGroups), o;
|
|
1081
1084
|
}
|
|
1082
1085
|
return Promise.resolve(null);
|
|
@@ -1111,7 +1114,7 @@ class L {
|
|
|
1111
1114
|
return;
|
|
1112
1115
|
}
|
|
1113
1116
|
let l = 10 + 1, a = !1;
|
|
1114
|
-
if (
|
|
1117
|
+
if (k && e["DEBUG:LOD"] != null)
|
|
1115
1118
|
return e["DEBUG:LOD"];
|
|
1116
1119
|
const u = y.getMeshLODExtension(e.geometry)?.lods, c = y.getPrimitiveIndex(e.geometry), p = u && u.length > 0, _ = y.getMaterialMinMaxLODsCount(e.material), v = _.min_count !== 1 / 0 && _.min_count >= 0 && _.max_count >= 0;
|
|
1117
1120
|
if (!p && !v) {
|
|
@@ -1132,7 +1135,7 @@ class L {
|
|
|
1132
1135
|
}
|
|
1133
1136
|
const x = d[L.$skinnedMeshBoundsOffset];
|
|
1134
1137
|
if ((s.frames + x) % this.skinnedMeshAutoUpdateBoundsInterval === 0) {
|
|
1135
|
-
const O =
|
|
1138
|
+
const O = Z(d), B = d.geometry;
|
|
1136
1139
|
O && (d.geometry = O), d.computeBoundingBox(), d.geometry = B;
|
|
1137
1140
|
}
|
|
1138
1141
|
}
|
|
@@ -1154,10 +1157,10 @@ class L {
|
|
|
1154
1157
|
}
|
|
1155
1158
|
if (this._tempBox.applyMatrix4(this.projectionScreenMatrix), this.renderer.xr.enabled && d.isPerspectiveCamera && d.fov > 70) {
|
|
1156
1159
|
const f = this._tempBox.min, h = this._tempBox.max;
|
|
1157
|
-
let M = f.x, D = f.y, $ = h.x,
|
|
1158
|
-
const
|
|
1159
|
-
M = (M -
|
|
1160
|
-
const Re = M < 0 && $ > 0 ? 0 : Math.min(Math.abs(f.x), Math.abs(h.x)), Be = D < 0 &&
|
|
1160
|
+
let M = f.x, D = f.y, $ = h.x, j = h.y;
|
|
1161
|
+
const ee = 2, oe = 1.5, te = (f.x + h.x) * 0.5, se = (f.y + h.y) * 0.5;
|
|
1162
|
+
M = (M - te) * ee + te, D = (D - se) * ee + se, $ = ($ - te) * ee + te, j = (j - se) * ee + se;
|
|
1163
|
+
const Re = M < 0 && $ > 0 ? 0 : Math.min(Math.abs(f.x), Math.abs(h.x)), Be = D < 0 && j > 0 ? 0 : Math.min(Math.abs(f.y), Math.abs(h.y)), ie = Math.max(Re, Be);
|
|
1161
1164
|
s.lastCentrality = (oe - ie) * (oe - ie) * (oe - ie);
|
|
1162
1165
|
} else
|
|
1163
1166
|
s.lastCentrality = 1;
|
|
@@ -1166,13 +1169,13 @@ class L {
|
|
|
1166
1169
|
const O = t.matrixWorldInverse, B = this._tempBox2;
|
|
1167
1170
|
B.copy(G), B.applyMatrix4(e.matrixWorld), B.applyMatrix4(O);
|
|
1168
1171
|
const b = B.getSize(this._tempBox2Size), z = Math.max(b.x, b.y);
|
|
1169
|
-
if (Math.max(x.x, x.y) != 0 && z != 0 && (x.z = b.z / Math.max(b.x, b.y) * Math.max(x.x, x.y)), s.lastScreenCoverage = Math.max(x.x, x.y, x.z), s.lastScreenspaceVolume.copy(x), s.lastScreenCoverage *= s.lastCentrality,
|
|
1172
|
+
if (Math.max(x.x, x.y) != 0 && z != 0 && (x.z = b.z / Math.max(b.x, b.y) * Math.max(x.x, x.y)), s.lastScreenCoverage = Math.max(x.x, x.y, x.z), s.lastScreenspaceVolume.copy(x), s.lastScreenCoverage *= s.lastCentrality, k && L.debugDrawLine) {
|
|
1170
1173
|
const f = this.tempMatrix.copy(this.projectionScreenMatrix);
|
|
1171
1174
|
f.invert();
|
|
1172
1175
|
const h = L.corner0, M = L.corner1, D = L.corner2, $ = L.corner3;
|
|
1173
1176
|
h.copy(this._tempBox.min), M.copy(this._tempBox.max), M.x = h.x, D.copy(this._tempBox.max), D.y = h.y, $.copy(this._tempBox.max);
|
|
1174
|
-
const
|
|
1175
|
-
h.z = M.z = D.z = $.z =
|
|
1177
|
+
const j = (h.z + $.z) * 0.5;
|
|
1178
|
+
h.z = M.z = D.z = $.z = j, h.applyMatrix4(f), M.applyMatrix4(f), D.applyMatrix4(f), $.applyMatrix4(f), L.debugDrawLine(h, M, 255), L.debugDrawLine(h, D, 255), L.debugDrawLine(M, $, 255), L.debugDrawLine(D, $, 255);
|
|
1176
1179
|
}
|
|
1177
1180
|
let w = 999;
|
|
1178
1181
|
if (u && s.lastScreenCoverage > 0)
|
|
@@ -1185,16 +1188,16 @@ class L {
|
|
|
1185
1188
|
}
|
|
1186
1189
|
w < l && (l = w, a = !0);
|
|
1187
1190
|
}
|
|
1188
|
-
if (a ? r.mesh_lod = l : r.mesh_lod = s.lastLodLevel_Mesh,
|
|
1191
|
+
if (a ? r.mesh_lod = l : r.mesh_lod = s.lastLodLevel_Mesh, k && r.mesh_lod != s.lastLodLevel_Mesh) {
|
|
1189
1192
|
const x = u?.[r.mesh_lod];
|
|
1190
1193
|
x && console.debug(`Mesh LOD changed: ${s.lastLodLevel_Mesh} → ${r.mesh_lod} (density: ${x.densities?.[c].toFixed(0)}) | ${e.name}`);
|
|
1191
1194
|
}
|
|
1192
1195
|
if (v) {
|
|
1193
1196
|
const d = "saveData" in globalThis.navigator && globalThis.navigator.saveData === !0;
|
|
1194
1197
|
if (s.lastLodLevel_Texture < 0) {
|
|
1195
|
-
if (r.texture_lod = _.max_count - 1,
|
|
1198
|
+
if (r.texture_lod = _.max_count - 1, k) {
|
|
1196
1199
|
const x = _.lods[_.max_count - 1];
|
|
1197
|
-
|
|
1200
|
+
k && console.log(`First Texture LOD ${r.texture_lod} (${x.max_height}px) - ${e.name}`);
|
|
1198
1201
|
}
|
|
1199
1202
|
} else {
|
|
1200
1203
|
const x = s.lastScreenspaceVolume.x + s.lastScreenspaceVolume.y + s.lastScreenspaceVolume.z;
|
|
@@ -1205,7 +1208,7 @@ class L {
|
|
|
1205
1208
|
for (let S = _.lods.length - 1; S >= 0; S--) {
|
|
1206
1209
|
const w = _.lods[S];
|
|
1207
1210
|
if (!(d && w.max_height >= 2048) && !(Ae() && w.max_height > 4096) && (w.max_height > b || !z && S === 0)) {
|
|
1208
|
-
if (z = !0, r.texture_lod = S,
|
|
1211
|
+
if (z = !0, r.texture_lod = S, k && r.texture_lod < s.lastLodLevel_Texture) {
|
|
1209
1212
|
const m = w.max_height;
|
|
1210
1213
|
console.log(`Texture LOD changed: ${s.lastLodLevel_Texture} → ${r.texture_lod} = ${m}px
|
|
1211
1214
|
Screensize: ${b.toFixed(0)}px, Coverage: ${(100 * s.lastScreenCoverage).toFixed(2)}%, Volume ${x.toFixed(1)}
|
|
@@ -1227,16 +1230,16 @@ class pt {
|
|
|
1227
1230
|
lastScreenspaceVolume = new A();
|
|
1228
1231
|
lastCentrality = 0;
|
|
1229
1232
|
}
|
|
1230
|
-
const ve = Symbol("NEEDLE_mesh_lod"),
|
|
1233
|
+
const ve = Symbol("NEEDLE_mesh_lod"), re = Symbol("NEEDLE_texture_lod");
|
|
1231
1234
|
let fe = null;
|
|
1232
|
-
function
|
|
1235
|
+
function ke() {
|
|
1233
1236
|
const i = mt();
|
|
1234
1237
|
i && (i.mapURLs(function(t) {
|
|
1235
1238
|
return be(), t;
|
|
1236
1239
|
}), be(), fe?.disconnect(), fe = new MutationObserver((t) => {
|
|
1237
1240
|
t.forEach((e) => {
|
|
1238
1241
|
e.addedNodes.forEach((s) => {
|
|
1239
|
-
s instanceof HTMLElement && s.tagName.toLowerCase() === "model-viewer" &&
|
|
1242
|
+
s instanceof HTMLElement && s.tagName.toLowerCase() === "model-viewer" && Ce(s);
|
|
1240
1243
|
});
|
|
1241
1244
|
});
|
|
1242
1245
|
}), fe.observe(document, { childList: !0, subtree: !0 }));
|
|
@@ -1245,18 +1248,18 @@ function mt() {
|
|
|
1245
1248
|
if (typeof customElements > "u") return null;
|
|
1246
1249
|
const i = customElements.get("model-viewer");
|
|
1247
1250
|
return i || (customElements.whenDefined("model-viewer").then(() => {
|
|
1248
|
-
console.debug("[gltf-progressive] model-viewer defined"),
|
|
1251
|
+
console.debug("[gltf-progressive] model-viewer defined"), ke();
|
|
1249
1252
|
}), null);
|
|
1250
1253
|
}
|
|
1251
1254
|
function be() {
|
|
1252
1255
|
if (typeof document > "u") return;
|
|
1253
1256
|
document.querySelectorAll("model-viewer").forEach((t) => {
|
|
1254
|
-
|
|
1257
|
+
Ce(t);
|
|
1255
1258
|
});
|
|
1256
1259
|
}
|
|
1257
1260
|
const Me = /* @__PURE__ */ new WeakSet();
|
|
1258
1261
|
let yt = 0;
|
|
1259
|
-
function
|
|
1262
|
+
function Ce(i) {
|
|
1260
1263
|
if (!i || Me.has(i))
|
|
1261
1264
|
return null;
|
|
1262
1265
|
Me.add(i), console.debug("[gltf-progressive] found new model-viewer..." + ++yt + `
|
|
@@ -1310,13 +1313,13 @@ class xt {
|
|
|
1310
1313
|
return t.element;
|
|
1311
1314
|
}
|
|
1312
1315
|
tryParseTextureLOD(t, e) {
|
|
1313
|
-
if (e[
|
|
1314
|
-
e[
|
|
1316
|
+
if (e[re] == !0) return;
|
|
1317
|
+
e[re] = !0;
|
|
1315
1318
|
const s = this.tryGetCurrentGLTF(t), n = this.tryGetCurrentModelViewer(t), r = this.getUrl(n);
|
|
1316
1319
|
if (r && s && e.material) {
|
|
1317
1320
|
let o = function(a) {
|
|
1318
|
-
if (a[
|
|
1319
|
-
a[
|
|
1321
|
+
if (a[re] == !0) return;
|
|
1322
|
+
a[re] = !0, a.userData && (a.userData.LOD = -1);
|
|
1320
1323
|
const u = Object.keys(a);
|
|
1321
1324
|
for (let c = 0; c < u.length; c++) {
|
|
1322
1325
|
const p = u[c], _ = a[p];
|
|
@@ -1375,14 +1378,14 @@ function wt(...i) {
|
|
|
1375
1378
|
const r = L.get(e);
|
|
1376
1379
|
return n?.enableLODsManager !== !1 && r.enable(), r;
|
|
1377
1380
|
}
|
|
1378
|
-
|
|
1381
|
+
ke();
|
|
1379
1382
|
if (!rt) {
|
|
1380
1383
|
const i = {
|
|
1381
1384
|
gltfProgressive: {
|
|
1382
1385
|
useNeedleProgressive: wt,
|
|
1383
1386
|
LODsManager: L,
|
|
1384
1387
|
configureLoader: Te,
|
|
1385
|
-
getRaycastMesh:
|
|
1388
|
+
getRaycastMesh: Z,
|
|
1386
1389
|
useRaycastMeshes: ot
|
|
1387
1390
|
}
|
|
1388
1391
|
};
|
|
@@ -1396,12 +1399,12 @@ export {
|
|
|
1396
1399
|
F as EXTENSION_NAME,
|
|
1397
1400
|
L as LODsManager,
|
|
1398
1401
|
y as NEEDLE_progressive,
|
|
1399
|
-
|
|
1402
|
+
je as VERSION,
|
|
1400
1403
|
Se as addDracoAndKTX2Loaders,
|
|
1401
1404
|
Te as configureLoader,
|
|
1402
1405
|
xe as createLoaders,
|
|
1403
|
-
|
|
1404
|
-
|
|
1406
|
+
Z as getRaycastMesh,
|
|
1407
|
+
ke as patchModelViewer,
|
|
1405
1408
|
nt as registerRaycastMesh,
|
|
1406
1409
|
Qe as setDracoDecoderLocation,
|
|
1407
1410
|
Je as setKTX2TranscoderLocation,
|