streaming-gltf 1.0.3 → 1.0.4
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/package.json +1 -1
- package/tools/bake-progressive.mjs +59 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "streaming-gltf",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "Streaming progressive glTF LOD renderer (BatchedMesh/InstancedMesh tiers, network-lazy GPU-eager LOD streaming, on-GPU position lerping) plus the local bake/convert + streaming-download pipeline (tools/bake-*.mjs).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -204,6 +204,54 @@ async function rewriteGlbJson(filePath, mutator) {
|
|
|
204
204
|
await writeFile(filePath, out);
|
|
205
205
|
}
|
|
206
206
|
|
|
207
|
+
// Normalize a GLB so @gltf-transform's reader can ingest it. The reader resolves
|
|
208
|
+
// a texture's image via the top-level `texture.source`, but GLBs that use
|
|
209
|
+
// EXT_texture_webp / EXT_texture_avif carry the image index ONLY under
|
|
210
|
+
// `texture.extensions.<ext>.source` and may omit the top-level `source`. The
|
|
211
|
+
// reader then maps a material's textureInfo to a null texture and throws
|
|
212
|
+
// (`setTextureInfo` -> `setMagFilter` on null). We copy the extension's source
|
|
213
|
+
// up to the top level (a valid glTF fallback) so the read succeeds; the webp
|
|
214
|
+
// extension still carries its own source for extension-aware consumers. Returns
|
|
215
|
+
// the path to a normalized temp GLB, or the original path if no change was
|
|
216
|
+
// needed. Caller is responsible for cleaning up the temp file.
|
|
217
|
+
async function normalizeForRead(filePath) {
|
|
218
|
+
const { json } = await readGlbParts(filePath);
|
|
219
|
+
const textures = json.textures || [];
|
|
220
|
+
const haveImage = (json.images || []).length > 0;
|
|
221
|
+
let changed = false;
|
|
222
|
+
for (const tex of textures) {
|
|
223
|
+
if (tex.source !== undefined) continue;
|
|
224
|
+
const extSource = tex.extensions?.EXT_texture_webp?.source
|
|
225
|
+
?? tex.extensions?.EXT_texture_avif?.source
|
|
226
|
+
?? tex.extensions?.KHR_texture_basisu?.source;
|
|
227
|
+
if (extSource !== undefined) {
|
|
228
|
+
tex.source = extSource;
|
|
229
|
+
changed = true;
|
|
230
|
+
} else if (haveImage) {
|
|
231
|
+
// A texture with NO image source anywhere (top-level or extension) makes
|
|
232
|
+
// the reader map a material's textureInfo to a null texture and throw.
|
|
233
|
+
// Point it at image 0 so the read succeeds; such a texture is already
|
|
234
|
+
// broken in the source asset, so the visual impact is nil.
|
|
235
|
+
tex.source = 0;
|
|
236
|
+
changed = true;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
if (!changed) return filePath;
|
|
240
|
+
const tmpPath = filePath + '.normalized.glb';
|
|
241
|
+
// Copy the original then rewrite its JSON chunk in place with the patched defs.
|
|
242
|
+
await writeFile(tmpPath, await readFile(filePath));
|
|
243
|
+
await rewriteGlbJson(tmpPath, (j) => {
|
|
244
|
+
const t = j.textures || [];
|
|
245
|
+
for (let i = 0; i < t.length; i++) {
|
|
246
|
+
if (t[i].source === undefined && textures[i] && textures[i].source !== undefined) {
|
|
247
|
+
t[i].source = textures[i].source;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
console.log(`[bake] normalized ${path.basename(filePath)}: populated top-level texture.source from texture extensions`);
|
|
252
|
+
return tmpPath;
|
|
253
|
+
}
|
|
254
|
+
|
|
207
255
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
208
256
|
const repoRoot = path.resolve(__dirname, '..');
|
|
209
257
|
|
|
@@ -263,7 +311,13 @@ export async function bakeProgressive(INPUT, OUT_DIR) {
|
|
|
263
311
|
'draco3d.decoder': await draco3dgltf.createDecoderModule(),
|
|
264
312
|
'draco3d.encoder': await draco3dgltf.createEncoderModule(),
|
|
265
313
|
});
|
|
266
|
-
const
|
|
314
|
+
const readPath = await normalizeForRead(INPUT);
|
|
315
|
+
let doc;
|
|
316
|
+
try {
|
|
317
|
+
doc = await io.read(readPath);
|
|
318
|
+
} finally {
|
|
319
|
+
if (readPath !== INPUT) { try { await rm(readPath, { force: true }); } catch (_) {} }
|
|
320
|
+
}
|
|
267
321
|
const root = doc.getRoot();
|
|
268
322
|
|
|
269
323
|
console.log(`[bake] meshes=${root.listMeshes().length} textures=${root.listTextures().length}`);
|
|
@@ -454,6 +508,10 @@ export async function bakeProgressive(INPUT, OUT_DIR) {
|
|
|
454
508
|
}
|
|
455
509
|
|
|
456
510
|
const simp = cloneMesh.listPrimitives()[0];
|
|
511
|
+
// A primitive can be emptied/removed by aggressive simplification (or a
|
|
512
|
+
// degenerate source mesh). Skip the vertex-color bake for this LOD
|
|
513
|
+
// rather than crash the whole asset bake.
|
|
514
|
+
if (!simp) { console.warn(`[bake] mesh ${mi} prim ${pi}: no primitive after simplify, skipping LOD stage`); continue; }
|
|
457
515
|
const posAcc = simp.getAttribute('POSITION');
|
|
458
516
|
const uvAcc = simp.getAttribute('TEXCOORD_0');
|
|
459
517
|
const vertCount = posAcc?.getCount() ?? 0;
|