@needle-tools/gltf-build-pipeline 1.4.0-beta → 1.4.1-beta.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/CHANGELOG.md CHANGED
@@ -4,6 +4,11 @@ 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
+ ## [1.4.1-beta] - 2023-03-13
8
+ - change: update gltf-transform to `^3.10.0`
9
+ - change: progressive mesh transform does now set ratio for LODs (see [simplifier issue 661 on github](https://github.com/zeux/meshoptimizer/issues/661))
10
+ - fix: issue where progressive meshes were not generated if a mesh was multiple times in the glTF
11
+
7
12
  ## [1.4.0-beta] - 2023-03-12
8
13
  - add: progressive mesh transform to automatically generate mesh LOD levels (currently we only generate one low poly version)
9
14
 
@@ -1,8 +1,8 @@
1
1
  import { Extension, ExtensionProperty, Graph, IProperty, Property, PropertyType, ReaderContext, WriterContext } from "@gltf-transform/core";
2
2
  export declare const NEEDLE_PROGRESSIVE_EXTENSION_NAME = "NEEDLE_progressive";
3
3
  export declare interface IProgressiveTextureInfo {
4
- uri?: string;
5
4
  guid: string;
5
+ uri?: string | Array<string>;
6
6
  }
7
7
  export declare function generateGuid(): string;
8
8
  export declare class NEEDLE_progressive extends Extension {
@@ -9,31 +9,16 @@ export class NEEDLE_progressive_mesh_settings extends Extension {
9
9
  return this;
10
10
  const root = this.document.getRoot();
11
11
  const meshes = root.listMeshes();
12
- /**
13
- * If a mesh is used in multiple objects it only appears once in the gltf-transform list
14
- * So we have to store the key and the index of the mesh in the list
15
- */
16
- const map = new Map();
17
- let offset = 0;
18
12
  for (let i = 0; i < definitions.length; i++) {
19
13
  const def = definitions[i];
20
14
  if (def.extensions && def.extensions[EXTENSION_NAME]) {
21
15
  const ext = def.extensions[EXTENSION_NAME];
22
16
  const prop = new NEEDLE_progressive_mesh_settings_property(this.document.getGraph(), "");
23
17
  Object.assign(prop, ext);
24
- let index = i - offset;
25
- const key = ext.guid;
26
- if (map.has(key)) {
27
- offset += 1;
28
- console.log(`INFO: Progressive mesh extension was ${i} already found...`);
29
- continue;
30
- }
31
- else {
32
- map.set(key, index);
33
- }
34
- const tex = meshes[index];
35
- if (tex)
36
- tex.setExtension(EXTENSION_NAME, prop);
18
+ let index = i;
19
+ const mesh = meshes[index];
20
+ if (mesh)
21
+ mesh.setExtension(EXTENSION_NAME, prop);
37
22
  else {
38
23
  console.warn(`WARN: Progressive mesh at index ${i} was not found in the glTF file. This might result in falsely loaded textures for ${ext.guid} / name \"${def.name}\"`);
39
24
  }
@@ -150,8 +150,8 @@ export function make_progressive_textures(filePath, _options = TEXTURE_RESIZE_DE
150
150
  const relName = path.relative(directory, outPath);
151
151
  // TODO: this path should be added to the NEEDLE dependencies extension
152
152
  texture.setExtension(NEEDLE_PROGRESSIVE_EXTENSION_NAME, progressiveExt.create({
153
- uri: relName,
154
153
  guid: textureGuid,
154
+ uri: relName,
155
155
  }));
156
156
  const key = getKey(texture);
157
157
  if (opts.useCache) {
@@ -300,51 +300,68 @@ export function make_progressive_meshes(filePath, _options, opts) {
300
300
  }
301
301
  const logger = doc.getLogger();
302
302
  const filename = path.basename(filePath);
303
- console.log("Progressive Mesh Transform: " + filename);
303
+ console.log("> Progressive Mesh Transform: " + filename);
304
304
  let index = 0;
305
305
  // the lowest LOD (last entry) will be saved in the original file
306
306
  const LODS = [
307
307
  { error: 0 },
308
- // { error: 0.05 },
309
- { error: 0.15 },
308
+ { ratio: 0.5, error: 1 },
309
+ { ratio: 0.25, error: 1 },
310
310
  ];
311
- for (const mesh of doc.getRoot().listMeshes()) {
311
+ /** Map of visited GUID -> extension */
312
+ const visited = new Map();
313
+ const meshes = doc.getRoot().listMeshes();
314
+ for (const mesh of meshes) {
312
315
  // TODO: skip meshes that are already below a certain threshold (E.g. just 4 vertices)
313
316
  const settings = mesh.getExtension(NEEDLE_progressive_mesh_settings.EXTENSION_NAME);
314
317
  if (!settings) {
315
- logger.debug(`${NAME}: no ${NEEDLE_progressive_mesh_settings.EXTENSION_NAME} found for mesh at index ${index}`);
318
+ logger.debug(`${NAME}: no ${NEEDLE_progressive_mesh_settings.EXTENSION_NAME} found for mesh at index ${index++}`);
316
319
  continue;
317
320
  }
318
321
  const progressiveExt = doc.createExtension(NEEDLE_progressive);
319
322
  if (!progressiveExt || !progressiveExt.create) {
320
- logger.debug(`${NAME}: Skipping because progressive extension is not available. ${index}`);
323
+ logger.debug(`${NAME}: Skipping because progressive extension is not available. ${index++}`);
321
324
  continue;
322
325
  }
323
- const lod_infos = new Array();
324
- // produce LODS, skip the last level because it will be saved in the original file
325
- for (let level = 0; level < LODS.length - 1; level++) {
326
- const lod = LODS[level];
327
- console.log(`${NAME}: ${filename} LOD ${level} error: ${lod.error}`);
328
- const guid = settings.guid;
329
- const lodPath = createMeshLod(doc, outputDirectory, level, mesh, guid, lod.error);
330
- lod_infos.push({
331
- path: lodPath,
332
- guid: guid,
333
- });
326
+ // check if we have already produced LODs for this mesh
327
+ let needsToGenerateLODs = true;
328
+ const generatedLODs = new Array();
329
+ if (visited.has(settings.guid)) {
330
+ // we can not just set the extension here...
331
+ logger.debug(`${NAME}: Mesh has already been processed ${index} - will use previously generated LODs`);
332
+ needsToGenerateLODs = false;
333
+ const existingPaths = visited.get(settings.guid).paths;
334
+ generatedLODs.push(...existingPaths);
335
+ }
336
+ if (needsToGenerateLODs) {
337
+ // produce LODS, skip the last level because it will be saved in the original file
338
+ for (let level = 0; level < LODS.length - 1; level++) {
339
+ const lod = LODS[level];
340
+ console.log(`${NAME}: Generate LOD ${level}`);
341
+ const guid = settings.guid;
342
+ const lodPath = createMeshLod(doc, outputDirectory, level, mesh, guid, lod);
343
+ generatedLODs.push(lodPath);
344
+ }
334
345
  }
335
346
  for (const prim of mesh.listPrimitives()) {
336
347
  const lastLod = LODS[LODS.length - 1];
337
- simplifyMesh(doc, mesh, prim, lastLod.error);
348
+ console.log(`${NAME}: ${filename} LOD ${LODS.length - 1}`);
349
+ simplifyMesh(doc, mesh, prim, lastLod);
338
350
  }
351
+ const model = progressiveExt.create({
352
+ guid: settings.guid,
353
+ uri: generatedLODs.map(l => l),
354
+ });
355
+ mesh.setExtension(NEEDLE_PROGRESSIVE_EXTENSION_NAME, model);
339
356
  index++;
340
- // TODO: need proper lod scheme, for now we use the same as for textures
341
- const lodlevel = 0;
342
- const lod0 = lod_infos[lodlevel];
343
- mesh.setExtension(NEEDLE_PROGRESSIVE_EXTENSION_NAME, progressiveExt.create({
344
- uri: lod0.path,
345
- guid: lod0.guid,
346
- }));
357
+ if (!visited.has(settings.guid)) {
358
+ visited.set(settings.guid, {
359
+ model: model,
360
+ paths: generatedLODs,
361
+ });
362
+ }
347
363
  }
364
+ console.log("< Progressive Mesh Transform: finished");
348
365
  });
349
366
  }
350
367
  /**
@@ -354,7 +371,7 @@ export function make_progressive_meshes(filePath, _options, opts) {
354
371
  * @param guid a unique identifier for the mesh
355
372
  * @param error the error to use for simplification. Higher means more simplification
356
373
  */
357
- function createMeshLod(doc, basePath, level, mesh, guid, error) {
374
+ function createMeshLod(doc, basePath, level, mesh, guid, lodLevel) {
358
375
  const newDoc = new Document();
359
376
  const newScene = newDoc.createScene();
360
377
  newDoc.createBuffer();
@@ -399,9 +416,9 @@ function createMeshLod(doc, basePath, level, mesh, guid, error) {
399
416
  newPrim.setExtension(name, copy);
400
417
  }
401
418
  }
402
- if (error > 0) {
419
+ if (lodLevel.error != undefined || lodLevel.ratio != undefined) {
403
420
  for (const prim of newMesh.listPrimitives()) {
404
- simplifyMesh(newDoc, newMesh, prim, error);
421
+ simplifyMesh(newDoc, newMesh, prim, lodLevel);
405
422
  }
406
423
  }
407
424
  const progressiveExt = newDoc.createExtension(NEEDLE_progressive);
@@ -427,11 +444,11 @@ function createMeshLod(doc, basePath, level, mesh, guid, error) {
427
444
  const name = "mesh_lod_" + level + "_" + guid;
428
445
  const filename = name + ext;
429
446
  const outName = basePath + filename;
430
- console.log(`${NAME}: Write mesh LOD ${level} to \"${filename}\" with error: ${error}`);
447
+ console.log(`${NAME}: Write mesh LOD ${level} to \"${filename}\" with: ${JSON.stringify(lodLevel)}`);
431
448
  newIo.write(outName, newDoc);
432
449
  return filename;
433
450
  }
434
- function simplifyMesh(doc, mesh, prim, error) {
451
+ function simplifyMesh(doc, mesh, prim, lodinfo) {
435
452
  const weldOptions = {
436
453
  ...WELD_DEFAULTS,
437
454
  overwrite: true,
@@ -439,8 +456,10 @@ function simplifyMesh(doc, mesh, prim, error) {
439
456
  const simplifyOptions = {
440
457
  ...SIMPLIFY_DEFAULTS, simplifier: MeshoptSimplifier,
441
458
  };
442
- if (error != undefined)
443
- simplifyOptions.error = error;
459
+ if (lodinfo.error != undefined)
460
+ simplifyOptions.error = lodinfo.error;
461
+ if (lodinfo.ratio != undefined)
462
+ simplifyOptions.ratio = lodinfo.ratio;
444
463
  compressPrimitive(doc, mesh, prim, weldOptions, simplifyOptions);
445
464
  }
446
465
  function isMeshLOD(filepath) {
@@ -62,7 +62,7 @@ async function testEntry(entry, callback, context) {
62
62
  // Try resolve potential relative path
63
63
  const newPath = context.baseDirectory + "/" + entry;
64
64
  if (existsSync(newPath) === false) {
65
- console.log(`INFO: Resolved path failed: ${entry} -> ${newPath}`);
65
+ console.log(`INFO: Resolved path failed: ${entry} -> ${newPath} (doesnt exist or is locked by another process)`);
66
66
  return;
67
67
  }
68
68
  else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@needle-tools/gltf-build-pipeline",
3
- "version": "1.4.0-beta",
3
+ "version": "1.4.1-beta.1",
4
4
  "description": "Pipeline and tools for optimizing gltf files using gltf-transform and compression settings within glTF extensions",
5
5
  "main": "index.mjs",
6
6
  "type": "module",
@@ -16,9 +16,9 @@
16
16
  "compile": "tsc || exit 1"
17
17
  },
18
18
  "dependencies": {
19
- "@gltf-transform/core": "^3.7.3",
20
- "@gltf-transform/extensions": "^3.7.3",
21
- "@gltf-transform/functions": "^3.7.3",
19
+ "@gltf-transform/core": "^3.10.0",
20
+ "@gltf-transform/extensions": "^3.10.0",
21
+ "@gltf-transform/functions": "^3.10.0",
22
22
  "command-exists": "^1.2.9",
23
23
  "command-line-args": "^5.2.1",
24
24
  "draco3dgltf": "^1.5.3",