@needle-tools/gltf-build-pipeline 2.16.0-next.85db3ce → 2.16.0-next.bc3af72

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.
@@ -15,6 +15,8 @@ export declare function computeJobKey(options: JobCacheOptions, inputFilePath: s
15
15
  /**
16
16
  * Save the result of a completed job to the cache.
17
17
  * Stores the content of each output file and a manifest linking them.
18
+ * The caller should include all output files including external sidecar files
19
+ * (e.g. .pmrem.ktx2) collected via PackGLTFOptions.externalFiles.
18
20
  */
19
21
  export declare function saveJobResult(jobKey: string, outputFiles: string[], logger: ILogger): void;
20
22
  /**
@@ -29,6 +29,8 @@ export function computeJobKey(options, inputFilePath) {
29
29
  /**
30
30
  * Save the result of a completed job to the cache.
31
31
  * Stores the content of each output file and a manifest linking them.
32
+ * The caller should include all output files including external sidecar files
33
+ * (e.g. .pmrem.ktx2) collected via PackGLTFOptions.externalFiles.
32
34
  */
33
35
  export function saveJobResult(jobKey, outputFiles, logger) {
34
36
  const entries = [];
package/dist/cli/index.js CHANGED
@@ -192,6 +192,7 @@ Each version will be compressed and written to the output directory.
192
192
  console.log("Progressive results:\n", progressive_results);
193
193
  }
194
194
  }
195
+ const externalFiles = [];
195
196
  if (options.compress === true) {
196
197
  const opts = {
197
198
  config,
@@ -204,6 +205,7 @@ Each version will be compressed and written to the output directory.
204
205
  // When progressive ran first, inputFile points to the progressive output copy.
205
206
  // Pass the original source path so extensions can resolve sibling assets (e.g. .exr files).
206
207
  sourceFile: progressive_results.length > 0 ? file : undefined,
208
+ externalFiles,
207
209
  };
208
210
  // If we have produced progressive assets then the array already contains the input
209
211
  if (progressive_results.length > 0) {
@@ -228,7 +230,7 @@ Each version will be compressed and written to the output directory.
228
230
  const allOutputFiles = progressive_results.length > 0
229
231
  ? progressive_results
230
232
  : [output || file];
231
- saveJobResult(jobKey, allOutputFiles, logger);
233
+ saveJobResult(jobKey, [...allOutputFiles, ...externalFiles], logger);
232
234
  }
233
235
  });
234
236
  printStats(stats, logger, config);
@@ -11,6 +11,8 @@ export type PackGLTFOptions = {
11
11
  verbose?: boolean;
12
12
  /** Original source file path, used to resolve external assets (e.g. .exr files) when inputFile differs from the source (e.g. after progressive transform) */
13
13
  sourceFile?: string;
14
+ /** Populated by packGLTF with absolute paths to external sidecar files produced during processing (e.g. .pmrem.ktx2) */
15
+ externalFiles?: string[];
14
16
  };
15
17
  /** Compress a glTF file
16
18
  */
@@ -7,7 +7,7 @@ import { MeshoptDecoder, MeshoptEncoder } from 'meshoptimizer';
7
7
  import { dedup, metalRough, prune, resample } from '@gltf-transform/functions';
8
8
  import { ALL_EXTENSIONS as NEEDLE_EXTENSIONS, NEEDLE_compression_texture, NEEDLE_mesh_compression, NEEDLE_pmrem, NEEDLE_lightmaps_ext, setExtensionInputFile } from '../extensions/index.js';
9
9
  import { isLOD, isMeshLOD, needle_animation_transform, needle_asset, needle_mesh_transform, needle_texture_transform, } from '../transforms/index.js';
10
- import { copyExternalResources, getOutputPath, getVersion, ioTryReadWithMissingResources, writeNodeIO } from "../utils/index.js";
10
+ import { copyExternalResources, getOutputPath, getVersion, isMaterialOnlyGLB, ioTryReadWithMissingResources, writeNodeIO } from "../utils/index.js";
11
11
  import { addToCache, getHash, tryGetFromCache, } from "../cache/index.js";
12
12
  import { TestAssertionError } from '../utils/test-assert.js';
13
13
  /** Compress a glTF file
@@ -105,8 +105,11 @@ export async function packGLTF(inputFile, outputFile, options) {
105
105
  dedup({ propertyTypes: [PropertyType.MESH, PropertyType.MATERIAL] }),
106
106
  ];
107
107
  const isProgressiveAsset = isLOD(inputFile);
108
- // TODO: progressive mesh LODs should not be pruned right now because it will remove e.g. UVs from meshes (and probably blend shapes too) (so we just not do it for any progressive asset)
109
- if (!isProgressiveAsset) {
108
+ const materialOnly = isMaterialOnlyGLB(document);
109
+ // TODO: progressive mesh LODs should not be pruned right now because it will remove e.g. UVs from meshes (and probably blend shapes too) (so we just not do it for any progressive asset)
110
+ // Material-only GLBs should not be pruned at all — they have no meshes so
111
+ // prune would strip materials, textures, and buffers, producing an empty file.
112
+ if (!isProgressiveAsset && !materialOnly) {
110
113
  transforms.push(prune({
111
114
  // Pruning animations is not supported yet (e.g. if the animation is only referenced by a component)
112
115
  propertyTypes: [
@@ -123,6 +126,29 @@ export async function packGLTF(inputFile, outputFile, options) {
123
126
  }));
124
127
  }
125
128
  await document.transform(...transforms);
129
+ // Collect external sidecar files (e.g. .pmrem.ktx2) produced by transforms
130
+ if (options.externalFiles) {
131
+ const outDir = path.dirname(outputFile);
132
+ const root = document.getRoot();
133
+ for (const tex of root.listTextures()) {
134
+ const uri = tex.getURI();
135
+ if (uri && !uri.startsWith('data:')) {
136
+ const absPath = path.resolve(outDir, uri);
137
+ if (existsSync(absPath)) {
138
+ options.externalFiles.push(absPath);
139
+ }
140
+ }
141
+ }
142
+ for (const buf of root.listBuffers()) {
143
+ const uri = buf.getURI();
144
+ if (uri && !uri.startsWith('data:')) {
145
+ const absPath = path.resolve(outDir, uri);
146
+ if (existsSync(absPath)) {
147
+ options.externalFiles.push(absPath);
148
+ }
149
+ }
150
+ }
151
+ }
126
152
  logger.debug(`← Writing to ${outputFile}`);
127
153
  await writeNodeIO(io, outputFile, document);
128
154
  if (useCache && cacheKey != undefined && existsSync(outputFile))
@@ -1,4 +1,7 @@
1
- import { ILogger } from "@gltf-transform/core";
1
+ import { Document, ILogger } from "@gltf-transform/core";
2
+ /** Returns true when the document contains materials/textures but no meshes, nodes, or scenes.
3
+ * These are standalone material assets that should not be pruned. */
4
+ export declare function isMaterialOnlyGLB(document: Document): boolean;
2
5
  export declare function validateImageMimeType(mimetype: string, image: Uint8Array, logger: ILogger): {
3
6
  error: boolean;
4
7
  message: string;
@@ -1,4 +1,11 @@
1
1
  import { ImageUtils } from "@gltf-transform/core";
2
+ /** Returns true when the document contains materials/textures but no meshes, nodes, or scenes.
3
+ * These are standalone material assets that should not be pruned. */
4
+ export function isMaterialOnlyGLB(document) {
5
+ const root = document.getRoot();
6
+ return root.listMeshes().length === 0
7
+ && root.listMaterials().length > 0;
8
+ }
2
9
  export function validateImageMimeType(mimetype, image, logger) {
3
10
  const actualMimeType = ImageUtils.getMimeType(image);
4
11
  if (actualMimeType != null && actualMimeType != mimetype) {
@@ -1 +1 @@
1
- export declare const version = "2.16.0-next.85db3ce";
1
+ export declare const version = "2.16.0-next.bc3af72";
@@ -1 +1 @@
1
- export const version = "2.16.0-next.85db3ce";
1
+ export const version = "2.16.0-next.bc3af72";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@needle-tools/gltf-build-pipeline",
3
- "version": "2.16.0-next.85db3ce",
3
+ "version": "2.16.0-next.bc3af72",
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",