@needle-tools/gltf-build-pipeline 2.15.9-next.e981752 → 2.15.9
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.
|
@@ -8,6 +8,7 @@ import { v4 as uuid } from 'uuid';
|
|
|
8
8
|
import { createHash } from 'crypto';
|
|
9
9
|
import tmp from 'tmp';
|
|
10
10
|
import { addToCache, tryGetFromCache } from '../cache/index.js';
|
|
11
|
+
import { testAssert } from '../utils/test-assert.js';
|
|
11
12
|
/** Resolve the tools/pmrem/ directory relative to this file.
|
|
12
13
|
* At runtime this file is at dist/transforms/needle_pmrem.js,
|
|
13
14
|
* so tools/pmrem/ is at ../../tools/pmrem/ */
|
|
@@ -61,12 +62,14 @@ export class NeedlePmremContext {
|
|
|
61
62
|
const wasmBinPath = join(this.pmremDir, 'pkg', 'pmrem_wasm_bg.wasm');
|
|
62
63
|
if (!existsSync(wasmJsPath) || !existsSync(wasmBinPath)) {
|
|
63
64
|
this.logger.warn('pmrem: WASM not found — EXR textures will not be processed');
|
|
65
|
+
testAssert(false, 'pmrem WASM files are missing. Please run `npm run build:pmrem` to build the tools/pmrem/ WASM module.');
|
|
64
66
|
return false;
|
|
65
67
|
}
|
|
66
68
|
// Check basisu binary
|
|
67
69
|
this.basisuExe = resolveBasisuExe(this.pmremDir);
|
|
68
70
|
if (!existsSync(this.basisuExe)) {
|
|
69
71
|
this.logger.warn(`pmrem: basisu binary not found at ${this.basisuExe} — EXR textures will not be processed`);
|
|
72
|
+
testAssert(false, `basisu executable is missing for platform ${process.platform}. Please run \`npm run build:pmrem\` to build the tools/pmrem/ binaries.`);
|
|
70
73
|
return false;
|
|
71
74
|
}
|
|
72
75
|
// Set executable permission
|
|
@@ -85,6 +88,7 @@ export class NeedlePmremContext {
|
|
|
85
88
|
}
|
|
86
89
|
catch (err) {
|
|
87
90
|
this.logger.warn(`pmrem: Failed to load WASM — ${err.message}`);
|
|
91
|
+
testAssert(false, `Failed to load pmrem WASM module: ${err.message}`);
|
|
88
92
|
return false;
|
|
89
93
|
}
|
|
90
94
|
// Create temp directory (per-instance to avoid parallel cleanup races)
|
|
@@ -109,6 +113,7 @@ export class NeedlePmremContext {
|
|
|
109
113
|
const contentHash = createHash('md5').update(exrBytes).digest('hex');
|
|
110
114
|
const cacheKey = `pmrem-${contentHash}`;
|
|
111
115
|
// Get KTX2 bytes — deduplicated and optionally cached
|
|
116
|
+
const wasDeduplicated = this.processingPromises.has(cacheKey);
|
|
112
117
|
const ktx2Bytes = await this.getOrProcessExr(cacheKey, exrBytes, index, name, context.useCache);
|
|
113
118
|
// Remove EXT_texture_exr property since the texture is now KTX2
|
|
114
119
|
const exrProp = texture.getExtension('EXT_texture_exr');
|
|
@@ -133,20 +138,25 @@ export class NeedlePmremContext {
|
|
|
133
138
|
texture.setName(ktx2FileName);
|
|
134
139
|
// Clear image data so gltf-transform doesn't embed it in the GLB
|
|
135
140
|
texture.setImage(null);
|
|
136
|
-
this.logger.
|
|
141
|
+
this.logger.debug(`NEEDLE_pmrem[${index}] ${name}: external → ${ktx2FileName}`);
|
|
137
142
|
}
|
|
138
143
|
else {
|
|
139
144
|
// Bundled: embed the KTX2 in the GLB
|
|
140
145
|
texture.setImage(new Uint8Array(ktx2Bytes));
|
|
141
146
|
}
|
|
142
|
-
// Report size
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
147
|
+
// Report size — skip info log for deduplicated textures to avoid confusion
|
|
148
|
+
if (wasDeduplicated) {
|
|
149
|
+
this.logger.debug(`NEEDLE_pmrem[${index}] ${name}: reused (identical content) → ${texture.getURI() || texture.getName() || name}`);
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
const sizeStr = image.byteLength < 100 * 1024
|
|
153
|
+
? `${(image.byteLength / 1024).toFixed(0)} KB`
|
|
154
|
+
: `${(image.byteLength / (1024 * 1024)).toFixed(2)} MB`;
|
|
155
|
+
const dstSizestr = ktx2Bytes.length < 100 * 1024
|
|
156
|
+
? `${(ktx2Bytes.length / 1024).toFixed(0)} KB`
|
|
157
|
+
: `${(ktx2Bytes.length / (1024 * 1024)).toFixed(2)} MB`;
|
|
158
|
+
this.logger.info(`NEEDLE_pmrem[${index}] ${name}: ${sizeStr} EXR → ${dstSizestr} KTX2 (${texture.getURI() || texture.getName() || name})`);
|
|
159
|
+
}
|
|
150
160
|
this.convertedTextures.push(texture);
|
|
151
161
|
this.processed++;
|
|
152
162
|
return true;
|
|
@@ -156,7 +166,7 @@ export class NeedlePmremContext {
|
|
|
156
166
|
// In-memory dedup: if we're already processing identical EXR content, reuse the promise
|
|
157
167
|
const existing = this.processingPromises.get(cacheKey);
|
|
158
168
|
if (existing) {
|
|
159
|
-
this.logger.
|
|
169
|
+
this.logger.debug(`NEEDLE_pmrem[${index}] ${name}: reusing in-flight result (identical content)`);
|
|
160
170
|
return existing;
|
|
161
171
|
}
|
|
162
172
|
const promise = this.processExrToKtx2(cacheKey, exrBytes, index, name, useCache);
|
|
@@ -168,7 +178,7 @@ export class NeedlePmremContext {
|
|
|
168
178
|
if (useCache) {
|
|
169
179
|
const cached = tryGetFromCache(cacheKey);
|
|
170
180
|
if (cached && cached.length > 0) {
|
|
171
|
-
this.logger.
|
|
181
|
+
this.logger.debug(`NEEDLE_pmrem[${index}] ${name}: loaded from cache`);
|
|
172
182
|
return cached;
|
|
173
183
|
}
|
|
174
184
|
}
|
|
@@ -176,24 +186,24 @@ export class NeedlePmremContext {
|
|
|
176
186
|
const inputSize = exrBytes.byteLength < 100 * 1024
|
|
177
187
|
? `${(exrBytes.byteLength / 1024).toFixed(0)} KB`
|
|
178
188
|
: `${(exrBytes.byteLength / (1024 * 1024)).toFixed(2)} MB`;
|
|
179
|
-
this.logger.
|
|
189
|
+
this.logger.debug(`NEEDLE_pmrem[${index}] ${name}: Running PMREM WASM (${inputSize} EXR) — this may take a while...`);
|
|
180
190
|
// Yield to allow log to flush before blocking WASM call
|
|
181
191
|
await new Promise(r => setTimeout(r, 0));
|
|
182
192
|
const t0 = Date.now();
|
|
183
193
|
const pmremExrBytes = this.wasmModule.pmrem_exr(exrBytes);
|
|
184
194
|
const elapsed = ((Date.now() - t0) / 1000).toFixed(1);
|
|
185
|
-
this.logger.
|
|
195
|
+
this.logger.debug(`NEEDLE_pmrem[${index}] ${name}: PMREM done in ${elapsed}s (${(pmremExrBytes.byteLength / 1024 / 1024).toFixed(1)} MB)`);
|
|
186
196
|
// 2. Write PMREM EXR to temp file
|
|
187
197
|
const id = uuid();
|
|
188
198
|
const tempExr = join(this.tmpDir, `${id}.pmrem.exr`);
|
|
189
199
|
const tempKtx2 = join(this.tmpDir, `${id}.ktx2`);
|
|
190
200
|
writeFileSync(tempExr, Buffer.from(pmremExrBytes));
|
|
191
201
|
// 3. Encode to KTX2 HDR via basisu
|
|
192
|
-
this.logger.
|
|
202
|
+
this.logger.debug(`NEEDLE_pmrem[${index}] ${name}: Encoding KTX2 HDR...`);
|
|
193
203
|
const variant = 'hdr4x4'; // 6x6 needs an update in Needle
|
|
194
204
|
await encodeKTX2WithBasisu(this.basisuExe, tempExr, tempKtx2, variant, true);
|
|
195
205
|
if (!existsSync(tempKtx2)) {
|
|
196
|
-
throw new Error(`
|
|
206
|
+
throw new Error(`NEEDLE_pmrem[${index}] ${name}: basisu did not produce output`);
|
|
197
207
|
}
|
|
198
208
|
// 4. Read KTX2 result
|
|
199
209
|
const ktx2Bytes = readFileSync(tempKtx2);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const version = "2.15.9
|
|
1
|
+
export declare const version = "2.15.9";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = "2.15.9
|
|
1
|
+
export const version = "2.15.9";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@needle-tools/gltf-build-pipeline",
|
|
3
|
-
"version": "2.15.9
|
|
3
|
+
"version": "2.15.9",
|
|
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",
|