@needle-tools/materialx 1.5.0-next.b9d5f3f → 1.5.1

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 CHANGED
@@ -4,35 +4,70 @@ All notable changes to this package will be documented in this file.
4
4
  The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
5
5
  and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
6
6
 
7
- ## [1.5.0] – 2026-03-19
7
+ ## [1.5.1] – 2026-03-22
8
+
9
+ ### Fixed
10
+ - UV patching: always wrap vec3 targets regardless of source UV declaration type
11
+ - Vertex color: wrap vec3→vec4 for color attributes (Three.js provides vec3)
12
+ - Shader test page uses proper environment/lighting matching other test pages
13
+
14
+ ### Added
15
+ - Playwright e2e tests validating 111+ MaterialX materials for shader compilation
16
+
17
+ ## [1.5.0] – 2026-03-20
8
18
 
9
19
  ### Added
10
20
  - Vertex displacement support (GLSL and ESSL/WebGL 2)
11
- - Multioutput nodedef pattern for shared surface + displacement parameters
12
21
  - Normal recomputation via screen-space derivatives (dFdx/dFdy) for displaced surfaces
13
22
  - Procedural noise displacement (fractal3d, position, math nodes)
14
23
  - Texture-based displacement (image node sampling in vertex shader)
15
24
  - Displacement animation support via Three.js PropertyBinding
25
+ - Three.js shadow support for lit MaterialX shaders (directional, spot, point)
26
+ - Alpha mode detection via `getAlphaMode` for mask/blend transparency
16
27
 
17
28
  ### Fixed
18
- - Skip MaterialX shader closure types (surfaceshader, displacementshader, etc.) in uniform handling instead of logging warnings
19
- - Alpha mode detection improvements for transparent materials
29
+ - Unlit shaders no longer emit shadow uniforms that cause compilation errors
30
+ - UV vec2/vec3 patching for displacement vertex shaders
31
+ - Skip MaterialX shader closure types (surfaceshader, displacementshader, etc.) in uniform handling
20
32
 
21
33
  ### Changed
22
34
  - WASM rebuilt with displacement support (MaterialX 1.39.4, Emscripten 3.1.74)
35
+ - Environment map intensity now combines per-material and scene intensity
36
+
37
+ ## [1.4.6] – 2026-03-17
38
+ - Fix: Compatibility with older Rollup/Vite 4 by replacing `import ... with` syntax
39
+
40
+ ## [1.4.5] – 2026-03-17
41
+ - Fix: Improved error log formatting with package version
42
+
43
+ ## [1.4.4] – 2026-03-17
44
+ - Fix: Minor type fixes and improved debug logging
23
45
 
24
46
  ## [1.4.3] – 2026-02-20
25
- - Feat: Add `globalThis.NEEDLE_MATERIALX_LOCATION` to override WASM location. Use `"package"` for package-local files, or a custom path for self-hosted/CDN.
47
+ - Add: `globalThis.NEEDLE_MATERIALX_LOCATION` to override WASM location. Use `"package"` for package-local files, or a custom path for self-hosted/CDN.
26
48
 
27
49
  ## [1.4.2] – 2026-02-10
28
50
  - Fix: Improve error handling when MaterialX renderable element is not found
29
51
 
30
52
  ## [1.4.1] – 2026-02-10
31
- - Feat: Use MaterialX from CDN by default
53
+ - Change: Use CDN as default WASM source
54
+
55
+ ## [1.4.0] – 2026-02-10
56
+ - Change: Load WASM binaries from Needle CDN by default instead of bundling locally
57
+
58
+ ## [1.3.4] – 2026-02-09
59
+ - Fix: Matrix update for AR sessions
60
+
61
+ ## [1.3.3] – 2026-02-09
62
+ - Add: Support for loading `.mtlx` files by index
63
+ - Fix: Type fixes in loader
32
64
 
33
65
  ## [1.3.2] - 2025-08-12
34
66
  - Fix: Error when MaterialX extension is not present
35
67
 
68
+ ## [1.3.1] - 2025-08-12
69
+ - Docs: README improvements
70
+
36
71
  ## [1.3.0] - 2025-08-12
37
72
  - Change: Refactor extension to use a documents array instead of a single document, backwards compatibility is maintained
38
73
 
@@ -40,7 +75,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
40
75
  - Add: `preloadWasm` function with support to wait for network idle. This is automatically done for Needle Engine projects.
41
76
 
42
77
  ## [1.2.1] - 2025-07-23
43
- - Fix: error caused by scene.environment being null
78
+ - Fix: Error caused by scene.environment being null
44
79
 
45
80
  ## [1.2.0] - 2025-07-23
46
81
  - Add: Support to load raw MaterialX materials (from mtlx as XML)
@@ -52,12 +87,15 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
52
87
  - Add: `useNeedleMaterialX` hooks for vanilla three.js and Needle Engine
53
88
 
54
89
  ## [1.0.6] - 2025-07-15
55
- - Fix: texture/environment sampling on some Android devices
90
+ - Fix: Texture/environment sampling on some Android devices
91
+
92
+ ## [1.0.3] - 2025-07-10
93
+ - Fix: Version bump for npm publish
56
94
 
57
95
  ## [1.0.2] - 2025-07-10
58
96
  - Add: Material extension `doubleSided` support
59
- - Improved lighting support
60
- - Improved texture loading and fix bug where glTF texture index was not resolved properly
97
+ - Fix: Improved lighting support
98
+ - Fix: Texture loading and glTF texture index resolution
61
99
 
62
100
  ## [1.0.1] - 2025-07-08
63
101
  - Initial release
package/README.md CHANGED
@@ -1,9 +1,11 @@
1
1
  # Needle MaterialX – MaterialX Materials for three.js & the Web
2
2
 
3
- Web runtime for [MaterialX](https://materialx.org/) materials in [Needle Engine](https://needle.tools) and [three.js](https://threejs.org/). Renders physically based MaterialX shaders in the browser using WebAssembly — load `.mtlx` files or glTF assets with the `NEEDLE_materials_mtlx` extension.
3
+ Web runtime for [MaterialX](https://materialx.org/) materials in [Needle Engine](https://needle.tools) and [three.js](https://threejs.org/). Renders physically based MaterialX shaders in the browser using WebAssembly — load `.mtlx` files or glTF assets with the `NEEDLE_materials_mtlx` extension (created with Needle Engine's Unity integration and ShaderGraph).
4
4
 
5
5
  - MaterialX to WebGL/WebGPU shader generation via WASM
6
6
  - Vertex displacement support (procedural noise, texture-based, animatable)
7
+ - Three.js shadow support (directional, spot, point lights)
8
+ - Alpha mask and blend transparency modes
7
9
  - glTF extension for embedding MaterialX materials in `.glb`/`.gltf` files
8
10
  - Experimental support for loading raw MaterialX XML (`.mtlx`) files
9
11
  - Works standalone with three.js or as a Needle Engine module
@@ -23,16 +25,22 @@ Web runtime for [MaterialX](https://materialx.org/) materials in [Needle Engine]
23
25
 
24
26
  ### Use with Needle Engine
25
27
 
26
- **Needle Engine has built in support for MaterialX shaders.** No changes to your code are necessary. The MaterialX module will import lazily when needed.
28
+ **Needle Engine has built in support for MaterialX shaders.**
29
+ No changes to your code are necessary. The MaterialX module will import lazily when needed.
27
30
 
28
31
  ### Use with three.js
29
32
 
30
33
  ```ts
31
34
  import { useNeedleMaterialX } from "@needle-tools/materialx";
32
- // Call the function with your GLTFLoader instance
35
+ // Call the function with your GLTFLoader instance to add the GLTFLoader plugin
33
36
  useNeedleMaterialX(<yourGltfLoaderInstance>);
34
37
  ```
35
38
 
39
+ ### Accessing Materials
40
+ MaterialX shaders will create `MaterialXMaterial` materials at runtime. These are custom three.js ShaderMaterials and assigned to objects like any other three.js material.
41
+
42
+ [Learn more in the Needle Engine documentation](https://engine.needle.tools/docs/how-to-guides/export/materialx.html)
43
+
36
44
  ## WASM
37
45
 
38
46
  ### Default (CDN)
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.5.0-next.b9d5f3f",
4
+ "version": "1.5.1",
5
5
  "type": "module",
6
6
  "main": "index.js",
7
7
  "types": "index.d.ts",
@@ -122,7 +122,9 @@ 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
125
+ // Detect whether each UV was originally vec2 or vec3 before removing declarations.
126
+ // Three.js always provides vec2 attributes, so vec3 assignments need wrapping.
127
+ const uv_is_vec2 = vertexShader.includes('in vec2 uv;');
126
128
  const uv1_is_vec2 = vertexShader.includes('in vec2 uv1;');
127
129
  const uv2_is_vec2 = vertexShader.includes('in vec2 uv2;');
128
130
  const uv3_is_vec2 = vertexShader.includes('in vec2 uv3;');
@@ -142,20 +144,41 @@ export class MaterialXMaterial extends ShaderMaterial {
142
144
  vertexShader = vertexShader.replace(/in\s+vec4\s+tangent;/g, '');
143
145
  var hasColor = vertexShader.includes('in vec4 color;');
144
146
  vertexShader = vertexShader.replace(/in\s+vec4\s+color;/g, '');
147
+ // Three.js provides `color` as vec3 but MaterialX declares it as vec4.
148
+ // Wrap assignments to vec4 targets: `color_0 = color;` → `color_0 = vec4(color, 1.0);`
149
+ if (hasColor) {
150
+ vertexShader = vertexShader.replace(/\bvec4 (\w+) = color;/g, 'vec4 $1 = vec4(color, 1.0);');
151
+ vertexShader = vertexShader.replace(/(\w+) = color;/g, (match, name) => {
152
+ if (match.includes('vec4')) return match;
153
+ const isVec4 = new RegExp(`\\bvec4\\s+${name}\\b`).test(vertexShader);
154
+ return isVec4 ? `${name} = vec4(color, 1.0);` : match;
155
+ });
156
+ }
145
157
 
146
- // Patch uv 2-component to 3-component. Three.js UVs are vec2 but MaterialX
147
- // texcoords may be vec3. Replace all `= uv;` assignments where the type is vec3.
148
- // This covers both vertex data connectors (texcoord_0 = uv) and displacement
149
- // dependency outputs (uv_0_out = uv).
150
- if (!uv_is_vec2) vertexShader = vertexShader.replace(/\bvec3 (\w+) = uv;/g, 'vec3 $1 = vec3(uv, 0.0);');
151
- if (!uv1_is_vec2) vertexShader = vertexShader.replace(/\bvec3 (\w+) = uv1;/g, 'vec3 $1 = vec3(uv1, 0.0);');
152
- if (!uv2_is_vec2) vertexShader = vertexShader.replace(/\bvec3 (\w+) = uv2;/g, 'vec3 $1 = vec3(uv2, 0.0);');
153
- if (!uv3_is_vec2) vertexShader = vertexShader.replace(/\bvec3 (\w+) = uv3;/g, 'vec3 $1 = vec3(uv3, 0.0);');
154
- // Also handle non-declaration assignments (e.g. `texcoord_0 = uv;`)
155
- if (!uv_is_vec2) vertexShader = vertexShader.replace(/(\w+) = uv;/g, (match, name) => {
156
- // Only replace if not already a vec3() call
157
- return match.includes('vec3') ? match : `${name} = vec3(uv, 0.0);`;
158
- });
158
+ // Patch uv vec2→vec3. Three.js always provides uv/uv1/uv2/uv3 as vec2
159
+ // attributes. When MaterialX originally declared them as vec3, any
160
+ // assignment from these attributes to a vec3 variable needs wrapping.
161
+ // When the UV was originally vec2, all assignments are already compatible.
162
+ // Three.js always provides uv/uv1/uv2/uv3 as vec2 attributes.
163
+ // When the generated shader assigns them to vec3 variables, we need to wrap.
164
+ // This applies regardless of the original declaration type, because Three.js
165
+ // always delivers vec2.
166
+ /** @param {string} shader @param {string} uvName */
167
+ function patchUvAssignments(shader, uvName) {
168
+ // 1. Declaration assignments: `vec3 x = <uv>;` → `vec3 x = vec3(<uv>, 0.0);`
169
+ shader = shader.replace(new RegExp(`\\bvec3 (\\w+) = ${uvName};`, 'g'), `vec3 $1 = vec3(${uvName}, 0.0);`);
170
+ // 2. Non-declaration assignments: `x = <uv>;` → wrap only when target is vec3
171
+ shader = shader.replace(new RegExp(`(\\w+) = ${uvName};`, 'g'), (match, name) => {
172
+ if (match.includes('vec3')) return match; // already handled
173
+ const isVec3 = new RegExp(`\\bvec3\\s+${name}\\b`).test(shader);
174
+ return isVec3 ? `${name} = vec3(${uvName}, 0.0);` : match;
175
+ });
176
+ return shader;
177
+ }
178
+ vertexShader = patchUvAssignments(vertexShader, 'uv');
179
+ vertexShader = patchUvAssignments(vertexShader, 'uv1');
180
+ vertexShader = patchUvAssignments(vertexShader, 'uv2');
181
+ vertexShader = patchUvAssignments(vertexShader, 'uv3');
159
182
 
160
183
  // Patch units – seems MaterialX uses different units and we end up with wrong light values?
161
184
  // result.direction = light.position - position;
@@ -182,7 +205,12 @@ export class MaterialXMaterial extends ShaderMaterial {
182
205
  if (hasTangent) defines['USE_TANGENT'] = '';
183
206
  if (hasColor) defines['USE_COLOR'] = '';
184
207
 
185
- // Add Three.js shadow support
208
+ // Detect whether the vertex shader declares the inverse-transpose matrix uniform.
209
+ // Unlit shaders omit this uniform, so shadow code that references it would fail.
210
+ const hasShadowUniforms = vertexShader.includes('u_worldInverseTransposeMatrix');
211
+
212
+ // Add Three.js shadow support (only when the vertex shader has the required uniforms)
213
+ if (hasShadowUniforms) {
186
214
  // Insert shadow pars before main() in vertex shader
187
215
  vertexShader = vertexShader.replace(
188
216
  /void\s+main\s*\(\s*\)\s*\{/,
@@ -199,7 +227,7 @@ void main() {`
199
227
  /(\n\s*)\}(\s*)$/,
200
228
  `$1 // Three.js shadow support
201
229
  $1 vec4 worldPosition = u_worldMatrix * vec4(position, 1.0);
202
- $1 vec3 transformedNormal = mat3(viewMatrix) * normalWorld;
230
+ $1 vec3 transformedNormal = normalize(mat3(viewMatrix) * mat3(u_worldInverseTransposeMatrix) * normal);
203
231
  $1 #include <shadowmap_vertex>
204
232
  $1}$2`
205
233
  );
@@ -320,6 +348,7 @@ void sampleLightSource(LightData light, vec3 position, out lightshader result)`
320
348
  }
321
349
  $2`
322
350
  );
351
+ } // end hasShadowUniforms
323
352
 
324
353
  const isTransparent = init.parameters?.transparent ?? false;
325
354
  materialParameters = {