@needle-tools/materialx 1.4.6 → 1.5.0
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 +51 -6
- package/README.md +3 -0
- package/bin/JsMaterialXCore.js +1 -1
- package/bin/JsMaterialXCore.wasm +0 -0
- package/bin/JsMaterialXGenShader.js +1 -1
- package/bin/JsMaterialXGenShader.wasm +0 -0
- package/bin/revision.json +2 -2
- package/package.json +1 -1
- package/src/loader/loader.three.js +34 -6
- package/src/materialx.helper.js +5 -0
- package/src/materialx.js +1 -1
- package/src/materialx.material.js +21 -13
- package/src/materialx.types.d.ts +9 -0
- /package/bin/{SHA_309ccca5d7788f90d773248c88498ddc203dc260 → SHA_2f154972802486867cddc6a79b7ef86952020d3e} +0 -0
|
Binary file
|
package/bin/revision.json
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@needle-tools/materialx",
|
|
3
3
|
"description": "MaterialX material support for three.js and Needle Engine – render physically based MaterialX shaders in the browser via WebAssembly",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.5.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
7
7
|
"types": "index.d.ts",
|
|
@@ -310,9 +310,21 @@ export async function createMaterialXMaterial(mtlx, materialNodeNameOrIndex, loa
|
|
|
310
310
|
|
|
311
311
|
if (debug) console.log("[MaterialX] Using renderable element for shader generation");
|
|
312
312
|
|
|
313
|
-
// Check transparency and
|
|
314
|
-
|
|
315
|
-
|
|
313
|
+
// Check transparency and alpha mode.
|
|
314
|
+
// getAlphaMode() is the source of truth: it checks both direct gltf_pbr nodes
|
|
315
|
+
// and inner gltf_pbr nodes inside custom shader graph nodegraphs.
|
|
316
|
+
// isTransparentSurface() is a fallback for non-gltf_pbr shaders.
|
|
317
|
+
const target = state.materialXGenerator.getTarget();
|
|
318
|
+
const alphaMode = typeof state.materialXModule.getAlphaMode === "function"
|
|
319
|
+
? state.materialXModule.getAlphaMode(renderableElement, target)
|
|
320
|
+
: (state.materialXModule.isTransparentSurface(renderableElement, target) ? "blend" : "opaque");
|
|
321
|
+
const isMask = alphaMode === "mask";
|
|
322
|
+
const isBlend = alphaMode === "blend";
|
|
323
|
+
// Both MASK and BLEND need alpha handling in the generated shader.
|
|
324
|
+
const needsAlpha = isMask || isBlend;
|
|
325
|
+
|
|
326
|
+
// hwTransparency must be true for both MASK and BLEND so the shader includes alpha handling code.
|
|
327
|
+
state.materialXGenContext.getOptions().hwTransparency = needsAlpha;
|
|
316
328
|
|
|
317
329
|
// Generate shaders using the element's name path
|
|
318
330
|
if (debug) console.log("[MaterialX] Generating MaterialX shaders...");
|
|
@@ -326,7 +338,10 @@ export async function createMaterialXMaterial(mtlx, materialNodeNameOrIndex, loa
|
|
|
326
338
|
shader,
|
|
327
339
|
context: context || {},
|
|
328
340
|
parameters: {
|
|
329
|
-
|
|
341
|
+
// MASK mode: no GL blending (discard handles cutout), BLEND mode: GL blending
|
|
342
|
+
transparent: isBlend,
|
|
343
|
+
// For MASK mode, set alphaTest so Three.js enables alpha testing
|
|
344
|
+
alphaTest: isMask ? 0.0001 : 0,
|
|
330
345
|
...options?.parameters,
|
|
331
346
|
},
|
|
332
347
|
loaders: loaders,
|
|
@@ -337,8 +352,21 @@ export async function createMaterialXMaterial(mtlx, materialNodeNameOrIndex, loa
|
|
|
337
352
|
return shaderMaterial;
|
|
338
353
|
|
|
339
354
|
} catch (error) {
|
|
340
|
-
//
|
|
341
|
-
|
|
355
|
+
// WASM exceptions arrive as integer pointers. Extract the detailed error message
|
|
356
|
+
// which includes shader compilation errors, line numbers, and error logs.
|
|
357
|
+
let errorMessage = error;
|
|
358
|
+
if (typeof error === "number" && state?.materialXModule) {
|
|
359
|
+
try {
|
|
360
|
+
const getDetailed = state.materialXModule.getExceptionDetailedMessage;
|
|
361
|
+
const getBasic = state.materialXModule.getExceptionMessage;
|
|
362
|
+
errorMessage = (typeof getDetailed === "function" ? getDetailed(error) : null)
|
|
363
|
+
|| (typeof getBasic === "function" ? getBasic(error) : null)
|
|
364
|
+
|| `WASM exception code ${error}`;
|
|
365
|
+
} catch (_) {
|
|
366
|
+
errorMessage = `WASM exception code ${error}`;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
console.error(`[MaterialX v${VERSION}] Error creating MaterialX material (${materialNodeNameOrIndex}):\n${errorMessage}\n→ MaterialX source:\n`, mtlx);
|
|
342
370
|
// Return a fallback material with stored MaterialX data
|
|
343
371
|
const fallbackMaterial = new MeshStandardMaterial();
|
|
344
372
|
fallbackMaterial.color.set(0xff00ff);
|
package/src/materialx.helper.js
CHANGED
|
@@ -222,6 +222,11 @@ function toThreeUniform(uniforms, type, value, name, loaders, searchPath) {
|
|
|
222
222
|
break;
|
|
223
223
|
case 'samplerCube':
|
|
224
224
|
case 'string':
|
|
225
|
+
case 'surfaceshader':
|
|
226
|
+
case 'displacementshader':
|
|
227
|
+
case 'volumeshader':
|
|
228
|
+
case 'lightshader':
|
|
229
|
+
// MaterialX closure/shader types — not real uniforms, skip silently
|
|
225
230
|
break;
|
|
226
231
|
default:
|
|
227
232
|
const key = type + ':' + name;
|
package/src/materialx.js
CHANGED
|
@@ -51,7 +51,7 @@ export async function ready() {
|
|
|
51
51
|
|
|
52
52
|
// NOTE: This must be a plain string literal (not a template) so that the
|
|
53
53
|
// makeFilesLocal Vite plugin can statically detect and localize this URL.
|
|
54
|
-
const defaultBaseUrl = "https://cdn.needle.tools/static/materialx/1.
|
|
54
|
+
const defaultBaseUrl = "https://cdn.needle.tools/static/materialx/1.5.0/";
|
|
55
55
|
|
|
56
56
|
/** @type {Array<string>} */
|
|
57
57
|
let urls;
|
|
@@ -122,11 +122,6 @@ export class MaterialXMaterial extends ShaderMaterial {
|
|
|
122
122
|
fragmentShader = fragmentShader.replace(/\bu_envLightIntensity\b/g, 'envMapIntensity');
|
|
123
123
|
|
|
124
124
|
// Capture some vertex shader properties
|
|
125
|
-
const uv_is_vec2 = vertexShader.includes('in vec2 uv;'); // check if uv is vec2; e.g. https://matlib.gpuopen.com/main/materials/all?material=da6ec531-f5c1-4790-ac14-8a5c51d0314e
|
|
126
|
-
const uv1_is_vec2 = vertexShader.includes('in vec2 uv1;');
|
|
127
|
-
const uv2_is_vec2 = vertexShader.includes('in vec2 uv2;');
|
|
128
|
-
const uv3_is_vec2 = vertexShader.includes('in vec2 uv3;');
|
|
129
|
-
|
|
130
125
|
// Remove `in vec3 position;` and so on since they're already declared by ShaderMaterial
|
|
131
126
|
vertexShader = vertexShader.replace(/in\s+vec3\s+position;/g, '');
|
|
132
127
|
vertexShader = vertexShader.replace(/in\s+vec3\s+normal;/g, '');
|
|
@@ -143,12 +138,19 @@ export class MaterialXMaterial extends ShaderMaterial {
|
|
|
143
138
|
var hasColor = vertexShader.includes('in vec4 color;');
|
|
144
139
|
vertexShader = vertexShader.replace(/in\s+vec4\s+color;/g, '');
|
|
145
140
|
|
|
146
|
-
// Patch uv
|
|
147
|
-
//
|
|
148
|
-
|
|
149
|
-
if
|
|
150
|
-
|
|
151
|
-
|
|
141
|
+
// Patch uv vec2→vec3. After removing `in vecN uv;` declarations above,
|
|
142
|
+
// Three.js always provides uv/uv1/uv2/uv3 as vec2 attributes. Any vec3
|
|
143
|
+
// assignment from these must be wrapped. This applies unconditionally —
|
|
144
|
+
// even if MaterialX originally declared them as vec2, compound displacement
|
|
145
|
+
// functions may still reference them as vec3 internally.
|
|
146
|
+
vertexShader = vertexShader.replace(/\bvec3 (\w+) = uv;/g, 'vec3 $1 = vec3(uv, 0.0);');
|
|
147
|
+
vertexShader = vertexShader.replace(/\bvec3 (\w+) = uv1;/g, 'vec3 $1 = vec3(uv1, 0.0);');
|
|
148
|
+
vertexShader = vertexShader.replace(/\bvec3 (\w+) = uv2;/g, 'vec3 $1 = vec3(uv2, 0.0);');
|
|
149
|
+
vertexShader = vertexShader.replace(/\bvec3 (\w+) = uv3;/g, 'vec3 $1 = vec3(uv3, 0.0);');
|
|
150
|
+
// Also handle non-declaration assignments (e.g. `texcoord_0 = uv;`)
|
|
151
|
+
vertexShader = vertexShader.replace(/(\w+) = uv;/g, (match, name) => {
|
|
152
|
+
return match.includes('vec3') ? match : `${name} = vec3(uv, 0.0);`;
|
|
153
|
+
});
|
|
152
154
|
|
|
153
155
|
// Patch units – seems MaterialX uses different units and we end up with wrong light values?
|
|
154
156
|
// result.direction = light.position - position;
|
|
@@ -175,7 +177,12 @@ export class MaterialXMaterial extends ShaderMaterial {
|
|
|
175
177
|
if (hasTangent) defines['USE_TANGENT'] = '';
|
|
176
178
|
if (hasColor) defines['USE_COLOR'] = '';
|
|
177
179
|
|
|
178
|
-
//
|
|
180
|
+
// Detect whether the vertex shader declares the inverse-transpose matrix uniform.
|
|
181
|
+
// Unlit shaders omit this uniform, so shadow code that references it would fail.
|
|
182
|
+
const hasShadowUniforms = vertexShader.includes('u_worldInverseTransposeMatrix');
|
|
183
|
+
|
|
184
|
+
// Add Three.js shadow support (only when the vertex shader has the required uniforms)
|
|
185
|
+
if (hasShadowUniforms) {
|
|
179
186
|
// Insert shadow pars before main() in vertex shader
|
|
180
187
|
vertexShader = vertexShader.replace(
|
|
181
188
|
/void\s+main\s*\(\s*\)\s*\{/,
|
|
@@ -192,7 +199,7 @@ void main() {`
|
|
|
192
199
|
/(\n\s*)\}(\s*)$/,
|
|
193
200
|
`$1 // Three.js shadow support
|
|
194
201
|
$1 vec4 worldPosition = u_worldMatrix * vec4(position, 1.0);
|
|
195
|
-
$1 vec3 transformedNormal = mat3(viewMatrix) *
|
|
202
|
+
$1 vec3 transformedNormal = normalize(mat3(viewMatrix) * mat3(u_worldInverseTransposeMatrix) * normal);
|
|
196
203
|
$1 #include <shadowmap_vertex>
|
|
197
204
|
$1}$2`
|
|
198
205
|
);
|
|
@@ -313,6 +320,7 @@ void sampleLightSource(LightData light, vec3 position, out lightshader result)`
|
|
|
313
320
|
}
|
|
314
321
|
$2`
|
|
315
322
|
);
|
|
323
|
+
} // end hasShadowUniforms
|
|
316
324
|
|
|
317
325
|
const isTransparent = init.parameters?.transparent ?? false;
|
|
318
326
|
materialParameters = {
|
package/src/materialx.types.d.ts
CHANGED
|
@@ -14,6 +14,13 @@ export namespace MaterialX {
|
|
|
14
14
|
readFromXmlString(doc: Document, xml: string, searchPath?: string): void;
|
|
15
15
|
loadStandardLibraries(genContext: GenContext): StandardLibrary;
|
|
16
16
|
isTransparentSurface(renderableElement: any, target: string): boolean;
|
|
17
|
+
/** Returns the alpha mode for a renderable element: "opaque", "mask", or "blend".
|
|
18
|
+
* Inspects the shader node (and its nodegraph implementation) for alpha_mode inputs. */
|
|
19
|
+
getAlphaMode?(renderableElement: any, target: string): string;
|
|
20
|
+
/** Extracts a detailed error message from a WASM exception pointer, including error logs. */
|
|
21
|
+
getExceptionDetailedMessage?(exceptionPtr: number): string;
|
|
22
|
+
/** Extracts a basic error message from a WASM exception pointer. */
|
|
23
|
+
getExceptionMessage?(exceptionPtr: number): string;
|
|
17
24
|
}
|
|
18
25
|
|
|
19
26
|
|
|
@@ -32,6 +39,8 @@ export namespace MaterialX {
|
|
|
32
39
|
export type Document = {
|
|
33
40
|
setDataLibrary(lib: StandardLibrary): void;
|
|
34
41
|
importLibrary(lib: Document): void;
|
|
42
|
+
/** Validates the document and returns validation result with error messages. */
|
|
43
|
+
validate?(): { valid: boolean; message: string };
|
|
35
44
|
|
|
36
45
|
getNodes(): Node[];
|
|
37
46
|
}
|
|
File without changes
|