@needle-tools/gltf-build-pipeline 2.14.0 → 2.15.0-next.a25e7ea

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.
@@ -5,9 +5,9 @@ import path from 'path';
5
5
  import { getVersion } from "../utils/version.js";
6
6
  // import * as CHECKDISCSPACE from 'check-disk-space'
7
7
  import xxhash from "xxhash-wasm";
8
- let hasher = null;
8
+ let xxHashModule = null;
9
9
  xxhash().then(module => {
10
- hasher = module.create32();
10
+ xxHashModule = module;
11
11
  });
12
12
  /**
13
13
  * Get the available space on the cache directory in bytes.
@@ -304,9 +304,9 @@ function hashObject(obj, depth = 0) {
304
304
  }
305
305
  }
306
306
  function hashBuffer(buffer, offset, length) {
307
- if (hasher) {
307
+ if (xxHashModule) {
308
308
  const view = new Uint8Array(buffer, offset, length);
309
- return hasher.update(view).digest();
309
+ return xxHashModule.h32Raw(view);
310
310
  }
311
311
  let hash = 0;
312
312
  const view = new Uint8Array(buffer, offset, length);
package/dist/cli/index.js CHANGED
File without changes
@@ -1,6 +1,7 @@
1
1
  import { Extension, ExtensionProperty, PropertyType, Node, TextureChannel } from '@gltf-transform/core';
2
2
  import { getTexture, tryGetInArray, writeExtension } from '../utils.js';
3
3
  import { isLightmap } from '../../utils/texture.js';
4
+ import { NEEDLE_progressive_texture_settings } from '../NEEDLE_progressive_texture_settings/index.js';
4
5
  const ALL_PROPERTY_TYPES = [];
5
6
  for (const key in PropertyType) {
6
7
  ALL_PROPERTY_TYPES.push(PropertyType[key]);
@@ -123,8 +124,11 @@ export function createOpaqueExtension(name, types, opts) {
123
124
  const prop = this.createExtension();
124
125
  this.jsonPointerHandler.read(context, texture, ext, prop);
125
126
  texture.setExtension(EXTENSION_NAME, prop);
126
- const handler = new TextureHandler(this.document, textureDefinition, EXTENSION_NAME, ext, debugLog);
127
+ const handler = new TextureHandler(this.document, textureDefinition, EXTENSION_NAME, ext, debugLog, texture);
127
128
  prop.add(handler);
129
+ const sourceIndex = textureDefinition.source;
130
+ if (debugLog)
131
+ console.log("Found texture with extension", EXTENSION_NAME, "at index", index, "source image index", sourceIndex, "name", texture.getName() || "<unnamed>");
128
132
  });
129
133
  break;
130
134
  case PropertyType.NODE:
@@ -188,11 +192,11 @@ export function createOpaqueExtension(name, types, opts) {
188
192
  const textures = this.document.getRoot().listTextures();
189
193
  for (let i = 0; i < textures.length; i++) {
190
194
  const tex = textures[i];
191
- const texDef = textureDefinitions[i];
192
- const name = texDef?.name;
193
195
  if (isLightmap(tex) && tex.getMimeType() !== "image/exr") {
194
196
  prop.setReference("lightmapTexture", tex, { channels: TextureChannel.R | TextureChannel.G | TextureChannel.B | TextureChannel.A });
195
- logger.info(`Found lightmap texture ${name}: ${tex.getMimeType()} in ${EXTENSION_NAME}`);
197
+ const texDef = textureDefinitions[i]; // @TODO: textureDefinitions and textures might not be in the same order - we need to find the texture by source index instead of assuming the order is the same
198
+ const name = tex.getName() || texDef?.name;
199
+ logger.info(`Found lightmap texture \"${name}\" (${tex.getMimeType()}) in ${EXTENSION_NAME}`);
196
200
  }
197
201
  }
198
202
  }
@@ -389,30 +393,38 @@ class GenericWriter {
389
393
  class TextureHandler {
390
394
  document;
391
395
  textureDefinition;
392
- index;
393
396
  key;
394
397
  ext;
395
398
  debug = false;
399
+ /** Reference to the gltf-transform Texture object, used to resolve the current image index at write time via imageIndexMap */
400
+ textureRef;
396
401
  assignTo(obj) {
397
402
  Object.assign(obj, this.ext);
398
403
  }
399
- constructor(doc, def, key, ext, debug) {
404
+ constructor(doc, def, key, ext, debug, textureRef) {
400
405
  this.document = doc;
401
406
  this.textureDefinition = def;
402
407
  this.key = key;
403
408
  this.ext = ext;
404
409
  this.debug = debug;
405
- this.index = this.tryGetSourceIndex(this.textureDefinition);
406
- if (debug)
407
- console.log(def);
410
+ this.textureRef = textureRef;
411
+ if (debug) {
412
+ const index = this.tryGetSourceIndex(this.textureDefinition);
413
+ console.log(index, this.textureDefinition);
414
+ }
408
415
  }
409
416
  write(ctx) {
410
- let foundTexture = false;
411
- const textureDefs = ctx.jsonDoc.json.textures || [];
412
- const imageIndex = this.index !== undefined ? this.index : this.tryGetSourceIndex(this.textureDefinition);
417
+ // Resolve the current image index from the gltf-transform graph reference.
418
+ // This is stable even after dedup/prune operations that re-index images.
419
+ const imageIndex = ctx.imageIndexMap.get(this.textureRef);
413
420
  if (imageIndex === undefined) {
414
- console.warn("WARN: no source index found for texture", this.key);
421
+ if (this.key !== "EXT_texture_exr" && this.debug) {
422
+ console.warn("WARN: failed to resolve image index for texture via imageIndexMap:", this.key, this.textureRef.getName() || "<unnamed>");
423
+ }
424
+ return;
415
425
  }
426
+ let foundTexture = false;
427
+ const textureDefs = ctx.jsonDoc.json.textures || [];
416
428
  textureDefs?.forEach((textureDef, i) => {
417
429
  if (foundTexture)
418
430
  return;
@@ -432,17 +444,10 @@ class TextureHandler {
432
444
  }
433
445
  });
434
446
  if (foundTexture === false) {
435
- if (this.key !== "EXT_texture_exr" && this.debug)
436
- console.warn("WARN: failed to re-assign extension:", this.key, this.index, this.tryGetSourceIndex(this.textureDefinition), textureDefs.length);
437
- }
438
- // this.document
439
- // .getRoot()
440
- // .listTextures()
441
- // .forEach((texture, index) => {
442
- // if (foundTexture) return;
443
- // if (!texture.getExtension(this.key)) return;
444
- // const imageIndex = ctx.imageIndexMap.get(texture);
445
- // });
447
+ if (this.key !== "EXT_texture_exr" && this.debug) {
448
+ console.warn("WARN: failed to re-assign extension:", this.key, "imageIndex", imageIndex, textureDefs.length, this.textureRef.getName() || "<unnamed>", this.textureDefinition);
449
+ }
450
+ }
446
451
  }
447
452
  tryGetSourceIndex(def) {
448
453
  let index = def.source;
@@ -878,11 +883,18 @@ class TextureIndexResolver extends PointerResolver {
878
883
  // create a new texture def entry
879
884
  if (this.debug)
880
885
  console.log("Could not find texture, creating a new one: " + this._debugName);
881
- const hasExtensionWithSource = this.inputDefinition.extensions && Object.keys(this.inputDefinition.extensions).some(key => this.inputDefinition.extensions[key]["source"] !== undefined);
886
+ // Remove settings-only extensions that are consumed during processing and should not appear in the output.
887
+ let extensions = this.inputDefinition.extensions ? { ...this.inputDefinition.extensions } : undefined;
888
+ if (extensions) {
889
+ delete extensions[NEEDLE_progressive_texture_settings.EXTENSION_NAME];
890
+ if (Object.keys(extensions).length === 0)
891
+ extensions = undefined;
892
+ }
893
+ const hasExtensionWithSource = extensions && Object.keys(extensions).some(key => extensions[key]?.["source"] !== undefined);
882
894
  const textureDef = {
883
895
  source: hasExtensionWithSource ? undefined : imgIndex,
884
896
  sampler: this.findOrCreateSampler(context.jsonDoc.json),
885
- extensions: this.inputDefinition.extensions,
897
+ extensions: extensions,
886
898
  extras: this.inputDefinition.extras,
887
899
  };
888
900
  context.jsonDoc.json.textures.push(textureDef);
@@ -128,7 +128,8 @@ export async function packGLTF(inputFile, outputFile, options) {
128
128
  return true;
129
129
  }
130
130
  catch (err) {
131
- logger.error(`Packing of "${path.basename(inputFile)}" using version ${getVersion()} failed with error:\n${err.message}`);
131
+ const shortedStackTrace = err instanceof Error && err.stack ? err.stack.split('\n').slice(0, 2).join('\n') : '';
132
+ logger.error(`Packing of "${path.basename(inputFile)}" using version ${getVersion()} failed with error:\n${err.message}\n${shortedStackTrace}`);
132
133
  if (debug)
133
134
  throw err;
134
135
  return false;
@@ -151,6 +151,7 @@ export function make_progressive_textures(filePath, _options = TEXTURE_RESIZE_DE
151
151
  return Promise.resolve();
152
152
  }
153
153
  let index = 0;
154
+ const textureHashCache = new Map();
154
155
  for (const texture of doc.getRoot().listTextures()) {
155
156
  const textureIndex = index++;
156
157
  const name = texture.getName();
@@ -177,8 +178,10 @@ export function make_progressive_textures(filePath, _options = TEXTURE_RESIZE_DE
177
178
  continue;
178
179
  }
179
180
  else {
181
+ if (!textureHashCache.has(texture))
182
+ textureHashCache.set(texture, getHash(texture));
180
183
  settings = {
181
- guid: generateGuid(filename + "_texture_" + index.toString()),
184
+ guid: generateGuid(textureHashCache.get(texture)),
182
185
  maxSize: _options.size[0],
183
186
  };
184
187
  logger.debug(`[${NAME}] No settings found for texture[${index}] - will use default settings (maxSize: ${settings.maxSize}, guid: ${settings.guid})`);
@@ -213,7 +216,13 @@ export function make_progressive_textures(filePath, _options = TEXTURE_RESIZE_DE
213
216
  logger.debug(`[${NAME}] Skipping, texture[${textureIndex}] is already smaller than the requested size: ${currentSize}px <= ${settings.maxSize}px`);
214
217
  continue;
215
218
  }
216
- const textureGuid = settings.guid?.length ? settings.guid : generateGuid(filename + "_texture_" + index.toString());
219
+ if (!settings.guid?.length) {
220
+ if (!textureHashCache.has(texture))
221
+ textureHashCache.set(texture, getHash(texture));
222
+ const hash = textureHashCache.get(texture);
223
+ settings.guid = generateGuid(hash);
224
+ }
225
+ const textureGuid = settings.guid;
217
226
  /** This is the size of the texture that is embedded in the glTF. No LOD should be smaller than this */
218
227
  const maxSize = settings.maxSize;
219
228
  const LOD_LEVEL = new Array();
@@ -524,7 +533,7 @@ async function resizeTexture(options, texture, logger, uri, slots) {
524
533
  }
525
534
  dstWidth = Math.floor(dstWidth);
526
535
  dstHeight = Math.floor(dstHeight);
527
- logger.info(`→ Resize texture (${uri || texture.getName() || slots.join(", ")}) from ${srcWidth}x${srcHeight} to ${dstWidth}x${dstHeight}px`);
536
+ logger.info(`→ Resize texture (${uri || texture.getName() || slots?.join(", ")}) from ${srcWidth}x${srcHeight} to ${dstWidth}x${dstHeight}px`);
528
537
  // https://sharp.pixelplumbing.com/api-resize
529
538
  const buffer = await sharp(texture.getImage())
530
539
  .resize(dstWidth, dstHeight, {
@@ -1 +1 @@
1
- export declare const version = "2.14.0";
1
+ export declare const version = "2.15.0-next.a25e7ea";
@@ -1 +1 @@
1
- export const version = "2.14.0";
1
+ export const version = "2.15.0-next.a25e7ea";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@needle-tools/gltf-build-pipeline",
3
- "version": "2.14.0",
3
+ "version": "2.15.0-next.a25e7ea",
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",