@needle-tools/materialx 1.2.0 → 1.2.1-next.2cdc46a
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 +3 -0
- package/package.json +2 -2
- package/src/index.d.ts +2 -1
- package/src/index.js +9 -4
- package/src/loader/loader.needle.js +2 -0
- package/src/materialx.d.ts +2 -0
- package/src/materialx.js +36 -4
- package/src/materialx.material.js +10 -2
- package/src/utils.d.ts +3 -0
- package/src/utils.js +13 -0
- package/src/utils.texture.d.ts +2 -0
- package/src/utils.texture.js +7 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,9 @@ 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.2.1] - 2025-07-23
|
|
8
|
+
- Fix: error caused by scene.environment being null
|
|
9
|
+
|
|
7
10
|
## [1.2.0] - 2025-07-23
|
|
8
11
|
- Add: Support to load raw MaterialX materials (from mtlx as XML)
|
|
9
12
|
- Fix: Warn if tangents are missing
|
package/package.json
CHANGED
package/src/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { ready, type MaterialXContext } from "./materialx.js";
|
|
1
|
+
export { ready, type MaterialXContext, preloadWasm } from "./materialx.js";
|
|
2
2
|
export { MaterialXMaterial } from "./materialx.material.js";
|
|
3
3
|
export { MaterialXLoader } from "./loader/loader.three.js";
|
|
4
4
|
|
|
@@ -9,3 +9,4 @@ declare const Experimental_API: {
|
|
|
9
9
|
};
|
|
10
10
|
|
|
11
11
|
export { Experimental_API };
|
|
12
|
+
|
package/src/index.js
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
|
-
|
|
1
|
+
import { createMaterialXMaterial } from "./loader/loader.three.js";
|
|
2
|
+
|
|
3
|
+
export { ready, preloadWasm } from "./materialx.js";
|
|
2
4
|
export { MaterialXMaterial } from "./materialx.material.js";
|
|
3
5
|
export { MaterialXLoader } from "./loader/loader.three.js";
|
|
4
6
|
|
|
5
|
-
import { createMaterialXMaterial } from "./loader/loader.three.js";
|
|
6
7
|
|
|
8
|
+
/**
|
|
9
|
+
* Experimental API for creating MaterialX materials.
|
|
10
|
+
*/
|
|
7
11
|
const Experimental_API = {
|
|
8
12
|
createMaterialXMaterial
|
|
9
|
-
}
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export { Experimental_API };
|
|
10
16
|
|
|
11
|
-
export { Experimental_API }
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { addCustomExtensionPlugin, Context } from "@needle-tools/engine";
|
|
2
2
|
import { useNeedleMaterialX as _useNeedleMaterialX } from "./loader.three.js";
|
|
3
3
|
import { debug } from "../utils.js";
|
|
4
|
+
import { preloadWasm } from "../materialx.js";
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* @typedef {import("@needle-tools/engine").INeedleGLTFExtensionPlugin} INeedleGLTFExtensionPlugin
|
|
@@ -58,5 +59,6 @@ export class MaterialXLoaderPlugin {
|
|
|
58
59
|
* @returns {Promise<void>}
|
|
59
60
|
*/
|
|
60
61
|
export async function useNeedleMaterialX() {
|
|
62
|
+
preloadWasm("network_idle");
|
|
61
63
|
addCustomExtensionPlugin(new MaterialXLoaderPlugin());
|
|
62
64
|
}
|
package/src/materialx.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { Light, Scene, Texture, WebGLRenderer } from "three";
|
|
2
2
|
import type { MaterialX as MX } from "./materialx.types.js";
|
|
3
3
|
|
|
4
|
+
export function preloadWasm(trigger: "immediately" | "network_idle"): Promise<void>;
|
|
5
|
+
|
|
4
6
|
export type MaterialXContext = {
|
|
5
7
|
getTime?(): number;
|
|
6
8
|
getFrame?(): number;
|
package/src/materialx.js
CHANGED
|
@@ -1,8 +1,26 @@
|
|
|
1
1
|
import MaterialX from "../bin/JsMaterialXGenShader.js";
|
|
2
|
-
import { debug } from "./utils.js";
|
|
2
|
+
import { debug, waitForNetworkIdle } from "./utils.js";
|
|
3
3
|
import { renderPMREMToEquirect } from "./utils.texture.js";
|
|
4
4
|
import { Light, Mesh, MeshBasicMaterial, Object3D, PlaneGeometry, PMREMGenerator, Scene, Texture, WebGLRenderer } from "three";
|
|
5
5
|
import { registerLights, getLightData } from "./materialx.helper.js";
|
|
6
|
+
import { whiteTexture } from "./utils.texture.js";
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Preloads the MaterialX WebAssembly module.
|
|
11
|
+
* @type {import("./materialx.js").preloadWasm}
|
|
12
|
+
*/
|
|
13
|
+
export async function preloadWasm(trigger) {
|
|
14
|
+
if (trigger === "immediately") {
|
|
15
|
+
// Load the WASM module immediately
|
|
16
|
+
return ready();
|
|
17
|
+
} else if (trigger === "network_idle") {
|
|
18
|
+
// Wait for network to be idle before loading
|
|
19
|
+
return waitForNetworkIdle().then(ready);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
|
|
6
24
|
|
|
7
25
|
export const state = new class {
|
|
8
26
|
/** @type {import("./materialx.types.js").MaterialX.MODULE | null} */
|
|
@@ -113,8 +131,9 @@ export async function ready() {
|
|
|
113
131
|
*/
|
|
114
132
|
|
|
115
133
|
/**
|
|
116
|
-
* MaterialXEnvironment manages the environment settings for MaterialX materials.
|
|
134
|
+
* MaterialXEnvironment manages the environment settings for MaterialX materials.
|
|
117
135
|
*/
|
|
136
|
+
// @dont-generate-component
|
|
118
137
|
export class MaterialXEnvironment {
|
|
119
138
|
|
|
120
139
|
/**
|
|
@@ -256,7 +275,14 @@ export class MaterialXEnvironment {
|
|
|
256
275
|
// If the material has its own envMap, we don't use the irradiance texture
|
|
257
276
|
return this._getTextures(material.envMap);
|
|
258
277
|
}
|
|
259
|
-
|
|
278
|
+
|
|
279
|
+
// Use the scene background for lighting if no environment is available
|
|
280
|
+
// If we don't do this we don't see the correct lighting for scenes exported with 'Environment Lighting: Color' and 'Environment Reflections: Skybox'
|
|
281
|
+
const skybox = this._scene.environment || this._scene.background;
|
|
282
|
+
if (skybox instanceof Texture) {
|
|
283
|
+
return this._getTextures(skybox);
|
|
284
|
+
}
|
|
285
|
+
return this._getTextures(null);
|
|
260
286
|
}
|
|
261
287
|
|
|
262
288
|
/** @type {PMREMGenerator | null} */
|
|
@@ -284,6 +310,12 @@ export class MaterialXEnvironment {
|
|
|
284
310
|
* @returns {{radianceTexture: Texture | null, irradianceTexture: Texture | null}}
|
|
285
311
|
*/
|
|
286
312
|
_getTextures(texture) {
|
|
313
|
+
|
|
314
|
+
// Fallback to white texture if no texture is provided
|
|
315
|
+
if (!texture) {
|
|
316
|
+
texture = whiteTexture;
|
|
317
|
+
}
|
|
318
|
+
|
|
287
319
|
/** @type {EnvironmentTextureSet | undefined} */
|
|
288
320
|
let res = this._texturesCache.get(texture || null);
|
|
289
321
|
if (res) {
|
|
@@ -322,7 +354,7 @@ export class MaterialXEnvironment {
|
|
|
322
354
|
const lights = new Array();
|
|
323
355
|
this._scene.traverse((/** @type {Object3D} */ object) => {
|
|
324
356
|
if ((/** @type {Light} */ (object)).isLight && object.visible)
|
|
325
|
-
lights.push(/** @type {Light} */
|
|
357
|
+
lights.push(/** @type {Light} */(object));
|
|
326
358
|
});
|
|
327
359
|
this._lights = lights;
|
|
328
360
|
}
|
|
@@ -23,6 +23,7 @@ const worldViewPos = new Vector3();
|
|
|
23
23
|
* @typedef {"highp" | "mediump" | "lowp"} Precision
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
|
+
// @dont-generate-component
|
|
26
27
|
export class MaterialXMaterial extends ShaderMaterial {
|
|
27
28
|
|
|
28
29
|
/** The original name of the shader
|
|
@@ -90,6 +91,7 @@ export class MaterialXMaterial extends ShaderMaterial {
|
|
|
90
91
|
// Patch fragmentShader
|
|
91
92
|
const precision = init.parameters?.precision || "highp";
|
|
92
93
|
vertexShader = vertexShader.replace(/precision mediump float;/g, `precision ${precision} float;`);
|
|
94
|
+
vertexShader = vertexShader.replace(/#define M_FLOAT_EPS 1e-8/g, precision === "highp" ? `#define M_FLOAT_EPS 1e-8` : `#define M_FLOAT_EPS 1e-3`);
|
|
93
95
|
fragmentShader = fragmentShader.replace(/precision mediump float;/g, `precision ${precision} float;`);
|
|
94
96
|
fragmentShader = fragmentShader.replace(/#define M_FLOAT_EPS 1e-8/g, precision === "highp" ? `#define M_FLOAT_EPS 1e-8` : `#define M_FLOAT_EPS 1e-3`);
|
|
95
97
|
|
|
@@ -258,21 +260,25 @@ export class MaterialXMaterial extends ShaderMaterial {
|
|
|
258
260
|
// Update standard transformation matrices
|
|
259
261
|
if (uniforms.u_worldMatrix) {
|
|
260
262
|
uniforms.u_worldMatrix.value = object.matrixWorld;
|
|
263
|
+
// @ts-ignore
|
|
261
264
|
uniforms.u_worldMatrix.needsUpdate = true;
|
|
262
265
|
}
|
|
263
266
|
|
|
264
267
|
if (uniforms.u_viewProjectionMatrix) {
|
|
265
268
|
uniforms.u_viewProjectionMatrix.value.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);
|
|
269
|
+
// @ts-ignore
|
|
266
270
|
uniforms.u_viewProjectionMatrix.needsUpdate = true;
|
|
267
271
|
}
|
|
268
272
|
|
|
269
273
|
if (uniforms.u_worldInverseTransposeMatrix) {
|
|
270
274
|
uniforms.u_worldInverseTransposeMatrix.value.setFromMatrix3(normalMat.getNormalMatrix(object.matrixWorld));
|
|
275
|
+
// @ts-ignore
|
|
271
276
|
uniforms.u_worldInverseTransposeMatrix.needsUpdate = true;
|
|
272
277
|
}
|
|
273
278
|
|
|
274
279
|
if (uniforms.u_viewPosition) {
|
|
275
280
|
uniforms.u_viewPosition.value.copy(camera.getWorldPosition(worldViewPos));
|
|
281
|
+
// @ts-ignore
|
|
276
282
|
uniforms.u_viewPosition.needsUpdate = true;
|
|
277
283
|
}
|
|
278
284
|
|
|
@@ -294,10 +300,10 @@ export class MaterialXMaterial extends ShaderMaterial {
|
|
|
294
300
|
uniforms.u_frame.value = frame;
|
|
295
301
|
}
|
|
296
302
|
|
|
303
|
+
this.uniformsNeedUpdate = true;
|
|
304
|
+
|
|
297
305
|
// Update light uniforms
|
|
298
306
|
this.updateEnvironmentUniforms(environment);
|
|
299
|
-
|
|
300
|
-
this.uniformsNeedUpdate = true;
|
|
301
307
|
}
|
|
302
308
|
|
|
303
309
|
/**
|
|
@@ -331,6 +337,7 @@ export class MaterialXMaterial extends ShaderMaterial {
|
|
|
331
337
|
if (uniforms.u_envRadiance) {
|
|
332
338
|
const prev = uniforms.u_envRadiance.value;
|
|
333
339
|
uniforms.u_envRadiance.value = textures.radianceTexture;
|
|
340
|
+
// @ts-ignore
|
|
334
341
|
if (prev != textures.radianceTexture) uniforms.u_envRadiance.needsUpdate = true;
|
|
335
342
|
}
|
|
336
343
|
if (uniforms.u_envRadianceMips) {
|
|
@@ -339,6 +346,7 @@ export class MaterialXMaterial extends ShaderMaterial {
|
|
|
339
346
|
if (uniforms.u_envIrradiance) {
|
|
340
347
|
const prev = uniforms.u_envIrradiance.value;
|
|
341
348
|
uniforms.u_envIrradiance.value = textures.irradianceTexture;
|
|
349
|
+
// @ts-ignore
|
|
342
350
|
if (prev != textures.irradianceTexture) uniforms.u_envIrradiance.needsUpdate = true;
|
|
343
351
|
}
|
|
344
352
|
|
package/src/utils.d.ts
CHANGED
package/src/utils.js
CHANGED
|
@@ -44,3 +44,16 @@ function updateTime() {
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
window.requestAnimationFrame(updateTime);
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
export async function waitForNetworkIdle() {
|
|
52
|
+
if (typeof requestIdleCallback !== "undefined") {
|
|
53
|
+
return new Promise(res => requestIdleCallback(res));
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
console.debug("[MaterialX] Can not wait for network idle, using fallback");
|
|
57
|
+
return new Promise(res => setTimeout(res, 100)); // Fallback to a short delay
|
|
58
|
+
}
|
|
59
|
+
}
|
package/src/utils.texture.d.ts
CHANGED
package/src/utils.texture.js
CHANGED
|
@@ -3,6 +3,13 @@ import { getParam } from './utils.js';
|
|
|
3
3
|
|
|
4
4
|
const debug = getParam("debugmaterialx");
|
|
5
5
|
|
|
6
|
+
export const whiteTexture = new Texture();
|
|
7
|
+
whiteTexture.needsUpdate = true;
|
|
8
|
+
whiteTexture.image = new Image();
|
|
9
|
+
whiteTexture.image.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAAAXNSR0IB2cksfwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAANQTFRFr6+vGqg52AAAAAxJREFUeJxjZGBEgQAAWAAJLpjsTQAAAABJRU5ErkJggg=="
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
6
13
|
/**
|
|
7
14
|
* Renders a PMREM environment map to an equirectangular texture with specified roughness
|
|
8
15
|
* @param {WebGLRenderer} renderer - Three.js WebGL renderer
|