@needle-tools/gltf-build-pipeline 2.15.8 → 2.15.9-next.53d13f4
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/dist/extensions/NEEDLE_lightmaps/index.js +21 -14
- package/dist/extensions/NEEDLE_opaque/NEEDLE_opaque.js +3 -18
- package/dist/extensions/utils.d.ts +3 -0
- package/dist/extensions/utils.js +17 -10
- package/dist/transforms/needle_pmrem.js +12 -4
- package/dist/utils/version.gen.d.ts +1 -1
- package/dist/utils/version.gen.js +1 -1
- package/package.json +1 -1
|
@@ -3,6 +3,8 @@ import { existsSync, readFileSync } from 'fs';
|
|
|
3
3
|
import { join, dirname } from 'path';
|
|
4
4
|
import { isLightmap } from '../../utils/texture.js';
|
|
5
5
|
import { getExtensionInputFile } from '../index.js';
|
|
6
|
+
import { findSource, getTexture } from '../utils.js';
|
|
7
|
+
import { testAssert } from '../../utils/test-assert.js';
|
|
6
8
|
const EXTENSION_NAME = 'NEEDLE_lightmaps';
|
|
7
9
|
/**
|
|
8
10
|
* Opaque-like ExtensionProperty that holds NEEDLE_lightmaps data on the Root.
|
|
@@ -66,7 +68,8 @@ export class NEEDLE_lightmaps_ext extends Extension {
|
|
|
66
68
|
continue;
|
|
67
69
|
const exrPath = inputDir ? join(inputDir, entry.pointer) : '';
|
|
68
70
|
if (!exrPath || !existsSync(exrPath)) {
|
|
69
|
-
this.document.getLogger().
|
|
71
|
+
this.document.getLogger().error(`NEEDLE_lightmaps: External EXR not found: ${exrPath || entry.pointer}`);
|
|
72
|
+
testAssert(false, `NEEDLE_lightmaps: External EXR not found: ${exrPath || entry.pointer}`);
|
|
70
73
|
continue;
|
|
71
74
|
}
|
|
72
75
|
// Read EXR bytes and inject into JSON so the core reader creates a Texture
|
|
@@ -113,12 +116,7 @@ export class NEEDLE_lightmaps_ext extends Extension {
|
|
|
113
116
|
const texDef = textureDefs[texIndex];
|
|
114
117
|
if (texDef) {
|
|
115
118
|
inputTextureDef = texDef;
|
|
116
|
-
|
|
117
|
-
?? texDef.extensions?.['EXT_texture_exr']?.source
|
|
118
|
-
?? texDef.extensions?.['KHR_texture_basisu']?.source;
|
|
119
|
-
if (imageIndex !== undefined) {
|
|
120
|
-
texture = context.textures[imageIndex] ?? null;
|
|
121
|
-
}
|
|
119
|
+
texture = getTexture(context, texDef) ?? null;
|
|
122
120
|
}
|
|
123
121
|
}
|
|
124
122
|
else if (entry.pointer.match(/\.exr$/i)) {
|
|
@@ -126,7 +124,19 @@ export class NEEDLE_lightmaps_ext extends Extension {
|
|
|
126
124
|
const imageIndex = this.injectedImageIndices.get(entry.pointer);
|
|
127
125
|
if (imageIndex !== undefined) {
|
|
128
126
|
texture = context.textures[imageIndex] ?? null;
|
|
129
|
-
inputTextureDef = textureDefs.find(t => t
|
|
127
|
+
inputTextureDef = textureDefs.find(t => findSource(t) === imageIndex);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
else if (!entry.pointer.startsWith('/')) {
|
|
131
|
+
// Already-processed external pointer (e.g. "sunrise.pmrem.ktx2")
|
|
132
|
+
// Find the texture by matching the URI in the existing images
|
|
133
|
+
const images = json.images ?? [];
|
|
134
|
+
for (let i = 0; i < images.length; i++) {
|
|
135
|
+
if (images[i].uri === entry.pointer) {
|
|
136
|
+
texture = context.textures[i] ?? null;
|
|
137
|
+
inputTextureDef = textureDefs.find(t => findSource(t) === i);
|
|
138
|
+
break;
|
|
139
|
+
}
|
|
130
140
|
}
|
|
131
141
|
}
|
|
132
142
|
if (texture) {
|
|
@@ -141,7 +151,8 @@ export class NEEDLE_lightmaps_ext extends Extension {
|
|
|
141
151
|
}
|
|
142
152
|
}
|
|
143
153
|
else {
|
|
144
|
-
logger.
|
|
154
|
+
logger.error(`NEEDLE_lightmaps: Could not resolve texture for pointer "${entry.pointer}"`);
|
|
155
|
+
testAssert(false, `NEEDLE_lightmaps: Could not resolve texture for pointer "${entry.pointer}"`);
|
|
145
156
|
}
|
|
146
157
|
}
|
|
147
158
|
// Lightmap workaround: prevent compression on lightmap textures (non-EXR)
|
|
@@ -217,11 +228,7 @@ export class NEEDLE_lightmaps_ext extends Extension {
|
|
|
217
228
|
const textures = context.jsonDoc.json.textures = context.jsonDoc.json.textures || [];
|
|
218
229
|
// Try to find an existing texture def pointing to the same image
|
|
219
230
|
for (let i = 0; i < textures.length; i++) {
|
|
220
|
-
|
|
221
|
-
const source = texDef.source
|
|
222
|
-
?? texDef.extensions?.['KHR_texture_basisu']?.source
|
|
223
|
-
?? texDef.extensions?.['NEEDLE_pmrem']?.source;
|
|
224
|
-
if (source === imageIndex) {
|
|
231
|
+
if (findSource(textures[i]) === imageIndex) {
|
|
225
232
|
return i;
|
|
226
233
|
}
|
|
227
234
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Extension, ExtensionProperty, PropertyType, Node, TextureChannel } from '@gltf-transform/core';
|
|
2
|
-
import { getTexture, tryGetInArray, writeExtension } from '../utils.js';
|
|
2
|
+
import { findSource, getTexture, tryGetInArray, writeExtension } from '../utils.js';
|
|
3
3
|
import { isLightmap } from '../../utils/texture.js';
|
|
4
4
|
import { NEEDLE_progressive_texture_settings } from '../NEEDLE_progressive_texture_settings/index.js';
|
|
5
5
|
import { testAssert } from '../../utils/test-assert.js';
|
|
@@ -655,7 +655,8 @@ class JsonPointerHandler {
|
|
|
655
655
|
this.externalExrPointers.push({ obj, key, texture });
|
|
656
656
|
}
|
|
657
657
|
else if (exrPath) {
|
|
658
|
-
|
|
658
|
+
this.document.getLogger().error(`NEEDLE_opaque: External EXR not found: ${exrPath}`);
|
|
659
|
+
testAssert(false, `NEEDLE_opaque: External EXR not found: ${exrPath}`);
|
|
659
660
|
}
|
|
660
661
|
}
|
|
661
662
|
if (value.startsWith("/materials/")) {
|
|
@@ -999,22 +1000,6 @@ class TextureIndexResolver extends PointerResolver {
|
|
|
999
1000
|
return json.samplers.length - 1;
|
|
1000
1001
|
}
|
|
1001
1002
|
}
|
|
1002
|
-
function findSource(texDef) {
|
|
1003
|
-
if (texDef.source !== undefined) {
|
|
1004
|
-
return texDef.source;
|
|
1005
|
-
}
|
|
1006
|
-
if (texDef.extensions) {
|
|
1007
|
-
const keys = Object.keys(texDef.extensions);
|
|
1008
|
-
for (let i = 0; i < keys.length; i++) {
|
|
1009
|
-
const key = keys[i];
|
|
1010
|
-
const ext = texDef.extensions[key];
|
|
1011
|
-
if (ext["source"] !== undefined) {
|
|
1012
|
-
return ext["source"];
|
|
1013
|
-
}
|
|
1014
|
-
}
|
|
1015
|
-
}
|
|
1016
|
-
return undefined;
|
|
1017
|
-
}
|
|
1018
1003
|
class MaterialIndexResolver extends PointerResolver {
|
|
1019
1004
|
init(context) {
|
|
1020
1005
|
}
|
|
@@ -3,4 +3,7 @@ import { GLTF } from '@gltf-transform/core';
|
|
|
3
3
|
export declare function tryGetInArray<T>(arr: Array<T> | undefined | null, index: number | undefined): T | null;
|
|
4
4
|
export declare function writeExtension(prop: GLTF.IProperty | null, name: string, ext: any): void;
|
|
5
5
|
export declare function getTexture(context: ReaderContext, tex: GLTF.ITexture): Texture;
|
|
6
|
+
/** Resolve the image source index from a texture definition, checking `texDef.source`
|
|
7
|
+
* and then iterating all extension keys for a `.source` property. */
|
|
8
|
+
export declare function findSource(texDef: GLTF.ITexture): number | undefined;
|
|
6
9
|
export declare function getTextureDefinition(context: WriterContext | ReaderContext, tex: Texture): GLTF.ITexture | null;
|
package/dist/extensions/utils.js
CHANGED
|
@@ -21,19 +21,26 @@ export function writeExtension(prop, name, ext) {
|
|
|
21
21
|
export function getTexture(context, tex) {
|
|
22
22
|
if (!context.jsonDoc.json.images)
|
|
23
23
|
return null;
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
const source = findSource(tex);
|
|
25
|
+
if (source !== undefined) {
|
|
26
|
+
return context.textures[source];
|
|
26
27
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
28
|
+
console.log("Could not find texture", tex);
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
/** Resolve the image source index from a texture definition, checking `texDef.source`
|
|
32
|
+
* and then iterating all extension keys for a `.source` property. */
|
|
33
|
+
export function findSource(texDef) {
|
|
34
|
+
if (texDef.source !== undefined)
|
|
35
|
+
return texDef.source;
|
|
36
|
+
if (texDef.extensions) {
|
|
37
|
+
for (const key of Object.keys(texDef.extensions)) {
|
|
38
|
+
const ext = texDef.extensions[key];
|
|
39
|
+
if (ext && ext["source"] !== undefined)
|
|
40
|
+
return ext["source"];
|
|
33
41
|
}
|
|
34
42
|
}
|
|
35
|
-
|
|
36
|
-
return null;
|
|
43
|
+
return undefined;
|
|
37
44
|
}
|
|
38
45
|
export function getTextureDefinition(context, tex) {
|
|
39
46
|
const textureDefinitions = context.jsonDoc.json.textures;
|
|
@@ -146,7 +146,7 @@ export class NeedlePmremContext {
|
|
|
146
146
|
const dstSizestr = ktx2Bytes.length < 100 * 1024
|
|
147
147
|
? `${(ktx2Bytes.length / 1024).toFixed(0)} KB`
|
|
148
148
|
: `${(ktx2Bytes.length / (1024 * 1024)).toFixed(2)} MB`;
|
|
149
|
-
this.logger.info(`pmrem:texture[${index}] ${name}: ${sizeStr} EXR → ${dstSizestr} KTX2`);
|
|
149
|
+
this.logger.info(`pmrem:texture[${index}] ${name}: ${sizeStr} EXR → ${dstSizestr} KTX2 (${texture.getURI() || texture.getName() || name})`);
|
|
150
150
|
this.convertedTextures.push(texture);
|
|
151
151
|
this.processed++;
|
|
152
152
|
return true;
|
|
@@ -172,16 +172,24 @@ export class NeedlePmremContext {
|
|
|
172
172
|
return cached;
|
|
173
173
|
}
|
|
174
174
|
}
|
|
175
|
-
// 1. Run PMREM on the EXR bytes
|
|
176
|
-
|
|
175
|
+
// 1. Run PMREM on the EXR bytes (synchronous WASM — may take a while)
|
|
176
|
+
const inputSize = exrBytes.byteLength < 100 * 1024
|
|
177
|
+
? `${(exrBytes.byteLength / 1024).toFixed(0)} KB`
|
|
178
|
+
: `${(exrBytes.byteLength / (1024 * 1024)).toFixed(2)} MB`;
|
|
179
|
+
this.logger.info(`pmrem:texture[${index}] ${name}: Running PMREM WASM (${inputSize} EXR) — this may take a while...`);
|
|
180
|
+
// Yield to allow log to flush before blocking WASM call
|
|
181
|
+
await new Promise(r => setTimeout(r, 0));
|
|
182
|
+
const t0 = Date.now();
|
|
177
183
|
const pmremExrBytes = this.wasmModule.pmrem_exr(exrBytes);
|
|
184
|
+
const elapsed = ((Date.now() - t0) / 1000).toFixed(1);
|
|
185
|
+
this.logger.info(`pmrem:texture[${index}] ${name}: PMREM done in ${elapsed}s (${(pmremExrBytes.byteLength / 1024 / 1024).toFixed(1)} MB)`);
|
|
178
186
|
// 2. Write PMREM EXR to temp file
|
|
179
187
|
const id = uuid();
|
|
180
188
|
const tempExr = join(this.tmpDir, `${id}.pmrem.exr`);
|
|
181
189
|
const tempKtx2 = join(this.tmpDir, `${id}.ktx2`);
|
|
182
190
|
writeFileSync(tempExr, Buffer.from(pmremExrBytes));
|
|
183
191
|
// 3. Encode to KTX2 HDR via basisu
|
|
184
|
-
this.logger.
|
|
192
|
+
this.logger.info(`pmrem:texture[${index}] ${name}: Encoding KTX2 HDR...`);
|
|
185
193
|
const variant = 'hdr4x4'; // 6x6 needs an update in Needle
|
|
186
194
|
await encodeKTX2WithBasisu(this.basisuExe, tempExr, tempKtx2, variant, true);
|
|
187
195
|
if (!existsSync(tempKtx2)) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const version = "2.15.
|
|
1
|
+
export declare const version = "2.15.9-next.53d13f4";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = "2.15.
|
|
1
|
+
export const version = "2.15.9-next.53d13f4";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@needle-tools/gltf-build-pipeline",
|
|
3
|
-
"version": "2.15.
|
|
3
|
+
"version": "2.15.9-next.53d13f4",
|
|
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",
|