@needle-tools/gltf-build-pipeline 2.2.0-alpha.5 → 2.2.0-alpha.7

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
+ ## [2.2.0-alpha.7] - 2023-06-13
8
+ - fix: progressive transform of converted GLB files where meshes don't have indicies
9
+ - fix: skip progressive texture generation for textures for textures that already match the lowest LOD size
10
+ - fix: if compression is set to None the LOD textures should respect that setting and also not be compressed
11
+
7
12
  ## [2.2.0-alpha.5] - 2023-06-06
8
13
  - add: CLI `--progressive` option to `transform` command (default is true)
9
14
  - add: CLI `--compress` option to `transform` command (default is true)
@@ -19,6 +19,7 @@ import { getVersion } from "../utils/version.js";
19
19
  // return 0;
20
20
  // }
21
21
  const buildPipelineVersion = getVersion(false);
22
+ const debugLogs = process.argv.includes("--debug");
22
23
  /**
23
24
  * Clear the cache.
24
25
  * This will remove all files in the cache directory.
@@ -86,7 +87,8 @@ export function limitCacheSize(maxSizeMB = cacheSizeLimit) {
86
87
  export function addToCache(key, bytes) {
87
88
  const dir = getCacheDirectory();
88
89
  const path = `${dir}/${key}.bin`;
89
- console.log(`Added ${(bytes.byteLength / 1024).toFixed(1)} KB to cache at \"${path}\"`);
90
+ if (debugLogs)
91
+ console.log(`Added ${(bytes.byteLength / 1024).toFixed(1)} KB to cache at \"${path}\"`);
90
92
  fs.writeFileSync(path, bytes);
91
93
  }
92
94
  /**
package/dist/cli/index.js CHANGED
@@ -77,7 +77,7 @@ Each version will be compressed and written to the output directory.
77
77
  logger.info("→ Transform " + file);
78
78
  const progressive_results = new Array();
79
79
  if (options.progressive === true) {
80
- await make_progressive({
80
+ if (await make_progressive({
81
81
  path: file,
82
82
  results: progressive_results,
83
83
  config,
@@ -86,7 +86,9 @@ Each version will be compressed and written to the output directory.
86
86
  }).catch(err => {
87
87
  logger.error(err);
88
88
  process.exit(ERROR_CODES.PROGRESSIVE_FAILED);
89
- });
89
+ }) === false) {
90
+ process.exit(ERROR_CODES.PROGRESSIVE_FAILED);
91
+ }
90
92
  }
91
93
  if (options.compress === true) {
92
94
  const opts = {
@@ -7,7 +7,7 @@ declare interface ProgressiveOptions {
7
7
  debug?: boolean;
8
8
  useCache?: boolean;
9
9
  }
10
- export declare function make_progressive(opts: ProgressiveOptions): Promise<void>;
10
+ export declare function make_progressive(opts: ProgressiveOptions): Promise<boolean>;
11
11
  export declare function createTransform(name: string, fn: Transform): Transform;
12
12
  export declare function make_progressive_textures(filePath: string, _options: TextureResizeOptions, opts: ProgressiveOptions): Transform;
13
13
  /** Options for the {@link textureResize} function. */
@@ -14,14 +14,22 @@ import { compressPrimitive } from './needle_mesh_transform.js';
14
14
  import { calculateMeshDensity, getMeshInformation } from "../utils/mesh.js";
15
15
  export async function make_progressive(opts) {
16
16
  const file = opts.path;
17
- return foreachGLTF(file, async (f) => {
17
+ let success = true;
18
+ await foreachGLTF(file, async (f) => {
19
+ // Should we stop if one file fails?
20
+ if (!success) {
21
+ return;
22
+ }
18
23
  // Don't re-process files that are already LODs
19
24
  if (isMeshLOD(f) || isImageLOD(f)) {
20
25
  console.log(`Progressive Transform: Ignore existing LOD file at ${f}`);
21
26
  return;
22
27
  }
23
- await onProcess(f, opts);
28
+ const res = await onProcess(f, opts);
29
+ if (!res)
30
+ success = false;
24
31
  });
32
+ return success;
25
33
  }
26
34
  function registerExtensions(io) {
27
35
  registerDefaultExtensions(io);
@@ -39,10 +47,11 @@ function registerExtensions(io) {
39
47
  async function onProcess(file, opts) {
40
48
  if (!file.endsWith(".glb") && !file.endsWith(".gltf") && !file.endsWith(".vrm")) {
41
49
  console.log("ERR: file is not gltf/glb");
42
- return;
50
+ return false;
43
51
  }
44
52
  if (!existsSync(file)) {
45
53
  console.log("ERR: file does not exist: " + file);
54
+ return false;
46
55
  }
47
56
  try {
48
57
  const io = new NodeIO();
@@ -85,7 +94,9 @@ async function onProcess(file, opts) {
85
94
  }
86
95
  catch (err) {
87
96
  console.log("ERR: failed to process file \"" + file + "\", Reason: " + err.message, "\nStack:\n" + err.stack);
97
+ return false;
88
98
  }
99
+ return true;
89
100
  }
90
101
  // basic idea:
91
102
  // 1. iterate all images that have an extension marking them to be progressively loaded
@@ -161,11 +172,15 @@ export function make_progressive_textures(filePath, _options = TEXTURE_RESIZE_DE
161
172
  logger.debug(`[${NAME}] Skipping because progressive extension is not available. ${textureIndex}`);
162
173
  continue;
163
174
  }
175
+ /** This is the current texture's size */
176
+ const currentSize = Math.min(...texture.getSize());
177
+ if (currentSize <= settings.maxSize) {
178
+ logger.debug(`[${NAME}] Skipping, texture[${textureIndex}] is already smaller than the requested size: ${currentSize}px <= ${settings.maxSize}px`);
179
+ continue;
180
+ }
164
181
  const textureGuid = settings.guid?.length ? settings.guid : generateGuid(filename + "_texture_" + index.toString());
165
182
  /** This is the size of the texture that is embedded in the glTF. No LOD should be smaller than this */
166
183
  const maxSize = settings.maxSize;
167
- /** This is the current texture's size */
168
- const currentSize = Math.min(...texture.getSize());
169
184
  const LOD_LEVEL = new Array();
170
185
  LOD_LEVEL.push({ size: currentSize });
171
186
  let generateLods = true; //opts.config?.usecase === "world";
@@ -302,27 +317,32 @@ async function createTextureLod(args, opts) {
302
317
  // Make sure the material is used
303
318
  createMeshForMaterial(newDoc, newMaterial, slot_index++);
304
319
  }
305
- const compressionSettings = texture.getExtension(NEEDLE_compression_texture.EXTENSION_NAME);
306
- if (compressionSettings !== undefined && compressionSettings !== null && compressionSettings.getExtensionDefinition) {
320
+ const compressionSettingsExt = texture.getExtension(NEEDLE_compression_texture.EXTENSION_NAME);
321
+ if (compressionSettingsExt !== undefined && compressionSettingsExt !== null && compressionSettingsExt.getExtensionDefinition) {
307
322
  logger.info("> Pass compression extension to new texture");
308
323
  const newExt = newDoc.createExtension(NEEDLE_compression_texture);
309
- const def = compressionSettings.getExtensionDefinition();
310
- switch (opts.config.usecase) {
311
- // For world scenarios we use ETC1S
312
- case "world":
313
- def.mode = "ETC1S";
314
- break;
315
- case "product":
316
- def.mode = detectTextureCompressionMode(slots);
317
- def.quality = 100;
318
- break;
319
- default:
320
- def.mode = detectTextureCompressionMode(slots);
321
- break;
324
+ const compressionSettings = compressionSettingsExt.getExtensionDefinition();
325
+ if (compressionSettings?.mode === "none") {
326
+ // if the mode is set to None then we keep that setting for the new texture
327
+ }
328
+ else {
329
+ switch (opts.config.usecase) {
330
+ // For world scenarios we use ETC1S
331
+ case "world":
332
+ compressionSettings.mode = "ETC1S";
333
+ break;
334
+ case "product":
335
+ compressionSettings.mode = detectTextureCompressionMode(slots);
336
+ compressionSettings.quality = 100;
337
+ break;
338
+ default:
339
+ compressionSettings.mode = detectTextureCompressionMode(slots);
340
+ break;
341
+ }
322
342
  }
323
- def.slot = slots[0];
324
- const newProp = newExt.create(def);
325
- newTexture.setExtension(NEEDLE_compression_texture.EXTENSION_NAME, newProp);
343
+ compressionSettings.slot = slots[0];
344
+ const newCompressionSettings = newExt.create(compressionSettings);
345
+ newTexture.setExtension(NEEDLE_compression_texture.EXTENSION_NAME, newCompressionSettings);
326
346
  }
327
347
  // add progressive texture extension
328
348
  const progressiveExt = newDoc.createExtension(NEEDLE_progressive);
@@ -688,7 +708,8 @@ function clonePrimitiveData(prim, logger) {
688
708
  if (semantics.some(s => s.includes("JOINTS"))) {
689
709
  return null;
690
710
  }
691
- const indices = new Uint32Array(prim.getIndices().getArray());
711
+ const prevIndices = prim.getIndices();
712
+ const indices = !prevIndices ? null : new Uint32Array(prevIndices.getArray());
692
713
  const attributes = {};
693
714
  for (const attr of semantics) {
694
715
  logger.info(`[${NAME}] Clone attribute ${attr}`);
@@ -753,7 +774,7 @@ async function createMeshLod(doc, basePath, level, mesh, meshInfo, guid, lodLeve
753
774
  newNode.setName("LOD " + level + "_" + guid);
754
775
  newNode.setTranslation([0, 0, 0]);
755
776
  const newMesh = newDoc.createMesh();
756
- newMesh.setName(mesh.getName() + " LOD " + level);
777
+ newMesh.setName((mesh.getName() || "Unnamed") + " LOD " + level);
757
778
  newNode.setMesh(newMesh);
758
779
  const newMat = newDoc.createMaterial();
759
780
  const weights = mesh.getWeights();
@@ -5,4 +5,9 @@ export declare type MeshInformation = {
5
5
  readonly totalIndexCount: number;
6
6
  };
7
7
  export declare function getMeshInformation(mesh: Mesh): MeshInformation;
8
- export declare function calculateMeshDensity(prim: Primitive): number;
8
+ /**
9
+ * Calculate the density of a mesh.
10
+ * @param prim - The primitive to calculate the density for.
11
+ * @returns The density of the mesh or -1 if the mesh is not indexed.
12
+ */
13
+ export declare function calculateMeshDensity(prim: Primitive): number | -1;
@@ -14,6 +14,11 @@ export function getMeshInformation(mesh) {
14
14
  totalIndexCount,
15
15
  };
16
16
  }
17
+ /**
18
+ * Calculate the density of a mesh.
19
+ * @param prim - The primitive to calculate the density for.
20
+ * @returns The density of the mesh or -1 if the mesh is not indexed.
21
+ */
17
22
  export function calculateMeshDensity(prim) {
18
23
  const bounds = {
19
24
  min: [Infinity, Infinity, Infinity],
@@ -71,16 +76,17 @@ export function calculateMeshDensity(prim) {
71
76
  // Add the area (half the magnitude of the cross product) to the total
72
77
  area += Math.sqrt(cross[0] * cross[0] + cross[1] * cross[1] + cross[2] * cross[2]) / 2;
73
78
  }
79
+ const count = indices.getCount() / 3;
80
+ const density = count / area;
81
+ // const size = [0, 0, 0] as vec3;
82
+ // size[0] = bounds.max[0] - bounds.min[0];
83
+ // size[1] = bounds.max[1] - bounds.min[1];
84
+ // size[2] = bounds.max[2] - bounds.min[2];
85
+ // const volume = size[0] * size[1] * size[2];
86
+ // const density = count / volume;
87
+ return density;
74
88
  }
75
- const count = indices.getCount() / 3;
76
- const density = count / area;
77
- // const size = [0, 0, 0] as vec3;
78
- // size[0] = bounds.max[0] - bounds.min[0];
79
- // size[1] = bounds.max[1] - bounds.min[1];
80
- // size[2] = bounds.max[2] - bounds.min[2];
81
- // const volume = size[0] * size[1] * size[2];
82
- // const density = count / volume;
83
- return density;
89
+ return -1;
84
90
  function expandBounds(point, target) {
85
91
  for (let i = 0; i < 3; i++) {
86
92
  target.min[i] = Math.min(point[i], target.min[i]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@needle-tools/gltf-build-pipeline",
3
- "version": "2.2.0-alpha.5",
3
+ "version": "2.2.0-alpha.7",
4
4
  "description": "Pipeline and tools for optimizing gltf files using gltf-transform and compression settings within glTF extensions",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",