@needle-tools/gltf-progressive 4.0.0-alpha → 4.0.0-alpha.1
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/gltf-progressive.js +1568 -0
- package/gltf-progressive.min.js +10 -0
- package/gltf-progressive.umd.cjs +10 -0
- package/lib/extension.d.ts +142 -0
- package/lib/extension.js +1133 -0
- package/lib/extension.model.d.ts +33 -0
- package/lib/extension.model.js +1 -0
- package/lib/index.d.ts +22 -0
- package/lib/index.js +87 -0
- package/lib/loaders.d.ts +41 -0
- package/lib/loaders.js +162 -0
- package/lib/lods.debug.d.ts +4 -0
- package/lib/lods.debug.js +43 -0
- package/lib/lods.manager.d.ts +165 -0
- package/lib/lods.manager.js +749 -0
- package/lib/lods.promise.d.ts +68 -0
- package/lib/lods.promise.js +108 -0
- package/lib/plugins/index.d.ts +2 -0
- package/lib/plugins/index.js +1 -0
- package/lib/plugins/modelviewer.d.ts +1 -0
- package/lib/plugins/modelviewer.js +223 -0
- package/lib/plugins/plugin.d.ts +23 -0
- package/lib/plugins/plugin.js +5 -0
- package/lib/utils.d.ts +30 -0
- package/lib/utils.internal.d.ts +68 -0
- package/lib/utils.internal.js +239 -0
- package/lib/utils.js +82 -0
- package/lib/version.d.ts +1 -0
- package/lib/version.js +4 -0
- package/lib/worker/gltf-progressive.worker.js +165 -0
- package/lib/worker/loader.mainthread.d.ts +45 -0
- package/lib/worker/loader.mainthread.js +192 -0
- package/package.json +4 -17
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { Box3, BufferAttribute, BufferGeometry, CompressedTexture, InterleavedBuffer, InterleavedBufferAttribute, Matrix3, Sphere, Texture, Vector3 } from "three";
|
|
2
|
+
import { createLoaders, GET_LOADER_LOCATION_CONFIG } from "../loaders.js";
|
|
3
|
+
import { getTextureDimensions, isMobileDevice } from "../utils.internal.js";
|
|
4
|
+
import { debug } from "../lods.debug.js";
|
|
5
|
+
const workers = new Array();
|
|
6
|
+
let getWorkerId = 0;
|
|
7
|
+
const maxWorkers = isMobileDevice() ? 2 : 10;
|
|
8
|
+
export function getWorker(opts) {
|
|
9
|
+
if (workers.length < maxWorkers) {
|
|
10
|
+
const index = workers.length;
|
|
11
|
+
if (debug)
|
|
12
|
+
console.warn(`[Worker] Creating new worker #${index}`);
|
|
13
|
+
const worker = GLTFLoaderWorker.createWorker(opts || {});
|
|
14
|
+
workers.push(worker);
|
|
15
|
+
return worker;
|
|
16
|
+
}
|
|
17
|
+
const index = (getWorkerId++) % workers.length;
|
|
18
|
+
const worker = workers[index];
|
|
19
|
+
return worker;
|
|
20
|
+
}
|
|
21
|
+
class GLTFLoaderWorker {
|
|
22
|
+
worker;
|
|
23
|
+
static async createWorker(opts) {
|
|
24
|
+
const worker = new Worker(new URL(`./gltf-progressive.worker.js`, import.meta.url), {
|
|
25
|
+
type: 'module',
|
|
26
|
+
});
|
|
27
|
+
const instance = new GLTFLoaderWorker(worker, opts);
|
|
28
|
+
return instance;
|
|
29
|
+
}
|
|
30
|
+
_running = [];
|
|
31
|
+
_webglRenderer = null;
|
|
32
|
+
async load(url, opts) {
|
|
33
|
+
const configs = GET_LOADER_LOCATION_CONFIG();
|
|
34
|
+
// Make sure we have a webgl renderer for the KTX transcoder feature detection
|
|
35
|
+
let renderer = opts?.renderer;
|
|
36
|
+
if (!renderer) {
|
|
37
|
+
this._webglRenderer ??= (async () => {
|
|
38
|
+
const { WebGLRenderer } = await import("three");
|
|
39
|
+
return new WebGLRenderer();
|
|
40
|
+
})();
|
|
41
|
+
renderer = await this._webglRenderer;
|
|
42
|
+
}
|
|
43
|
+
const loaders = createLoaders(renderer);
|
|
44
|
+
const ktx2Loader = loaders.ktx2Loader;
|
|
45
|
+
const ktx2LoaderConfig = ktx2Loader.workerConfig;
|
|
46
|
+
if (url instanceof URL) {
|
|
47
|
+
url = url.toString();
|
|
48
|
+
}
|
|
49
|
+
else if (url.startsWith("file:")) {
|
|
50
|
+
// make blob url
|
|
51
|
+
url = URL.createObjectURL(new Blob([url]));
|
|
52
|
+
}
|
|
53
|
+
else if (!url.startsWith("blob:") && !url.startsWith("http:") && !url.startsWith("https:")) {
|
|
54
|
+
url = new URL(url, window.location.href).toString();
|
|
55
|
+
}
|
|
56
|
+
const options = {
|
|
57
|
+
type: "load",
|
|
58
|
+
url: url,
|
|
59
|
+
dracoDecoderPath: configs.dracoDecoderPath,
|
|
60
|
+
ktx2TranscoderPath: configs.ktx2TranscoderPath,
|
|
61
|
+
ktx2LoaderConfig: ktx2LoaderConfig,
|
|
62
|
+
};
|
|
63
|
+
if (this._debug)
|
|
64
|
+
console.debug("[Worker] Sending load request", options);
|
|
65
|
+
this.worker.postMessage(options);
|
|
66
|
+
return new Promise(resolve => {
|
|
67
|
+
this._running.push({
|
|
68
|
+
url: url.toString(),
|
|
69
|
+
resolve,
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
_debug = false;
|
|
74
|
+
constructor(worker, _opts) {
|
|
75
|
+
this.worker = worker;
|
|
76
|
+
this._debug = _opts.debug ?? false;
|
|
77
|
+
worker.onmessage = (event) => {
|
|
78
|
+
const data = event.data;
|
|
79
|
+
if (this._debug)
|
|
80
|
+
console.log("[Worker] EVENT", data);
|
|
81
|
+
switch (data.type) {
|
|
82
|
+
case "loaded-gltf": {
|
|
83
|
+
for (const promise of this._running) {
|
|
84
|
+
if (promise.url === data.result.url) {
|
|
85
|
+
// process received data and resolve
|
|
86
|
+
processReceivedData(data.result);
|
|
87
|
+
promise.resolve(data.result);
|
|
88
|
+
// cleanup
|
|
89
|
+
const url = promise.url;
|
|
90
|
+
if (url.startsWith("blob:")) {
|
|
91
|
+
URL.revokeObjectURL(url);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
worker.onerror = (error) => {
|
|
99
|
+
console.error("[Worker] Error in gltf-progressive worker:", error);
|
|
100
|
+
};
|
|
101
|
+
worker.postMessage({
|
|
102
|
+
type: 'init',
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
function processReceivedData(data) {
|
|
107
|
+
for (const res of data.geometries) {
|
|
108
|
+
const worker_geometry = res.geometry;
|
|
109
|
+
// console.log(worker_geometry)
|
|
110
|
+
const geo = new BufferGeometry();
|
|
111
|
+
geo.name = worker_geometry.name || "";
|
|
112
|
+
if (worker_geometry.index) {
|
|
113
|
+
const index = worker_geometry.index;
|
|
114
|
+
geo.setIndex(cloneAttribute(index));
|
|
115
|
+
}
|
|
116
|
+
// geo.drawRange = receivedGeometry.drawRange || { start: 0, count: Infinity };
|
|
117
|
+
for (const attrName in worker_geometry.attributes) {
|
|
118
|
+
const attribute = worker_geometry.attributes[attrName];
|
|
119
|
+
const clonedAttribute = cloneAttribute(attribute);
|
|
120
|
+
geo.setAttribute(attrName, clonedAttribute);
|
|
121
|
+
}
|
|
122
|
+
// handle morph attributes
|
|
123
|
+
// TODO: one slow aspect that could be moved to the worker is updating the morph target textures
|
|
124
|
+
if (worker_geometry.morphAttributes) {
|
|
125
|
+
for (const morphName in worker_geometry.morphAttributes) {
|
|
126
|
+
const morphAttributes = worker_geometry.morphAttributes[morphName];
|
|
127
|
+
const morphArray = morphAttributes.map(attribute => {
|
|
128
|
+
return cloneAttribute(attribute);
|
|
129
|
+
});
|
|
130
|
+
geo.morphAttributes[morphName] = morphArray;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
geo.morphTargetsRelative = worker_geometry.morphTargetsRelative ?? false;
|
|
134
|
+
geo.boundingBox = new Box3();
|
|
135
|
+
geo.boundingBox.min = new Vector3(worker_geometry.boundingBox?.min.x, worker_geometry.boundingBox?.min.y, worker_geometry.boundingBox?.min.z);
|
|
136
|
+
geo.boundingBox.max = new Vector3(worker_geometry.boundingBox?.max.x, worker_geometry.boundingBox?.max.y, worker_geometry.boundingBox?.max.z);
|
|
137
|
+
geo.boundingSphere = new Sphere(new Vector3(worker_geometry.boundingSphere?.center.x, worker_geometry.boundingSphere?.center.y, worker_geometry.boundingSphere?.center.z), worker_geometry.boundingSphere?.radius);
|
|
138
|
+
// // handle groups
|
|
139
|
+
if (worker_geometry.groups) {
|
|
140
|
+
for (const group of worker_geometry.groups) {
|
|
141
|
+
geo.addGroup(group.start, group.count, group.materialIndex);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
// // handle user data
|
|
145
|
+
if (worker_geometry.userData) {
|
|
146
|
+
geo.userData = worker_geometry.userData;
|
|
147
|
+
}
|
|
148
|
+
res.geometry = geo;
|
|
149
|
+
}
|
|
150
|
+
for (const res of data.textures) {
|
|
151
|
+
const texture = res.texture;
|
|
152
|
+
let newTexture = null;
|
|
153
|
+
if (texture.isCompressedTexture) {
|
|
154
|
+
const mipmaps = texture.mipmaps;
|
|
155
|
+
const { width, height } = getTextureDimensions(texture);
|
|
156
|
+
newTexture = new CompressedTexture(mipmaps, width, height, texture.format, texture.type, texture.mapping, texture.wrapS, texture.wrapT, texture.magFilter, texture.minFilter, texture.anisotropy, texture.colorSpace);
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
newTexture = new Texture(texture.image, texture.mapping, texture.wrapS, texture.wrapT, texture.magFilter, texture.minFilter, texture.format, texture.type, texture.anisotropy, texture.colorSpace);
|
|
160
|
+
newTexture.mipmaps = texture.mipmaps;
|
|
161
|
+
newTexture.channel = texture.channel;
|
|
162
|
+
newTexture.source.data = texture.source.data;
|
|
163
|
+
newTexture.flipY = texture.flipY;
|
|
164
|
+
newTexture.premultiplyAlpha = texture.premultiplyAlpha;
|
|
165
|
+
newTexture.unpackAlignment = texture.unpackAlignment;
|
|
166
|
+
newTexture.matrix = new Matrix3(...texture.matrix.elements);
|
|
167
|
+
}
|
|
168
|
+
if (!newTexture) {
|
|
169
|
+
console.error("[Worker] Failed to create new texture from received data. Texture is not a CompressedTexture or Texture.");
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
res.texture = newTexture;
|
|
173
|
+
}
|
|
174
|
+
return data;
|
|
175
|
+
}
|
|
176
|
+
function cloneAttribute(attribute) {
|
|
177
|
+
let res = attribute;
|
|
178
|
+
if ("isInterleavedBufferAttribute" in attribute && attribute.isInterleavedBufferAttribute) {
|
|
179
|
+
const data = attribute.data;
|
|
180
|
+
const array = data.array;
|
|
181
|
+
const interleavedBuffer = new InterleavedBuffer(array, data.stride);
|
|
182
|
+
res = new InterleavedBufferAttribute(interleavedBuffer, attribute.itemSize, array.byteOffset, attribute.normalized);
|
|
183
|
+
res.offset = attribute.offset;
|
|
184
|
+
}
|
|
185
|
+
else if ("isBufferAttribute" in attribute && attribute.isBufferAttribute) {
|
|
186
|
+
res = new BufferAttribute(attribute.array, attribute.itemSize, attribute.normalized);
|
|
187
|
+
res.usage = attribute.usage;
|
|
188
|
+
res.gpuType = attribute.gpuType;
|
|
189
|
+
res.updateRanges = attribute.updateRanges;
|
|
190
|
+
}
|
|
191
|
+
return res;
|
|
192
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@needle-tools/gltf-progressive",
|
|
3
|
-
"version": "4.0.0-alpha",
|
|
3
|
+
"version": "4.0.0-alpha.1",
|
|
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": {
|
|
@@ -25,23 +25,13 @@
|
|
|
25
25
|
"optimization"
|
|
26
26
|
],
|
|
27
27
|
"type": "module",
|
|
28
|
-
"main": "./
|
|
28
|
+
"main": "./lib/index.js",
|
|
29
29
|
"exports": {
|
|
30
30
|
".": {
|
|
31
|
-
"import": "./
|
|
31
|
+
"import": "./lib/index.js",
|
|
32
32
|
"require": "./gltf-progressive.js"
|
|
33
33
|
}
|
|
34
34
|
},
|
|
35
|
-
"scripts": {
|
|
36
|
-
"dev": "npm-watch build:lib",
|
|
37
|
-
"build": "node tools/build.mjs",
|
|
38
|
-
"build:dry": "node tools/build.mjs --dry-run",
|
|
39
|
-
"build:dist": "vite build",
|
|
40
|
-
"build:lib": "tsc --rootDir ./src --outDir ./dist/lib --noEmit false --declaration --incremental false --sourceMap false && node tools/compile.mjs",
|
|
41
|
-
"test:tsc": "tsc --noEmit",
|
|
42
|
-
"lint": "eslint --ext .ts src",
|
|
43
|
-
"lint:fix": "eslint --ext .ts src --fix"
|
|
44
|
-
},
|
|
45
35
|
"files": [
|
|
46
36
|
"lib",
|
|
47
37
|
"examples",
|
|
@@ -82,8 +72,5 @@
|
|
|
82
72
|
"three": ">= 0.183.0",
|
|
83
73
|
"vite": "7"
|
|
84
74
|
},
|
|
85
|
-
"
|
|
86
|
-
"access": "public",
|
|
87
|
-
"registry": "https://registry.npmjs.org"
|
|
88
|
-
}
|
|
75
|
+
"types": "./lib/index.d.ts"
|
|
89
76
|
}
|