@needle-tools/materialx 1.1.1 → 1.2.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 CHANGED
@@ -4,6 +4,12 @@ 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.0] - 2025-07-23
8
+ - Add: Support to load raw MaterialX materials (from mtlx as XML)
9
+ - Fix: Warn if tangents are missing
10
+ - Fix: Improve unsupported light handling
11
+ - Change: Refactor library to js + jsdoc
12
+
7
13
  ## [1.1.0] - 2025-07-15
8
14
  - Add: `useNeedleMaterialX` hooks for vanilla three.js and Needle Engine
9
15
 
@@ -0,0 +1,4 @@
1
+ # ensure LF line endings
2
+ *.data binary
3
+ *.data.txt binary
4
+ *.wasm binary
package/bin/README.md ADDED
@@ -0,0 +1,6 @@
1
+ Source: https://github.com/AcademySoftwareFoundation/MaterialX/tree/gh-pages
2
+
3
+ Edits:
4
+
5
+ - In `JsMaterialXGenShader.js` added `export default MaterialX;` at bottom
6
+ - Renamed `JsMaterialXGenShader.data` to `JsMaterialXGenShader.data.txt` so it can be loaded by vite etc
@@ -1,2 +1,2 @@
1
1
  export * from "./src/index.js";
2
- export { useNeedleMaterialX } from "./src/loader/loader.three.js";
2
+ export { useNeedleMaterialX } from "./src/loader/loader.three.js";
package/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export * from "./src/index.js";
2
+ export { useNeedleMaterialX } from "./src/loader/loader.three.js";
@@ -1,2 +1,2 @@
1
1
  export * from "./src/index.js";
2
- export { useNeedleMaterialX } from "./src/loader/loader.needle.js";
2
+ export { useNeedleMaterialX } from "./src/loader/loader.needle.js";
package/needle.js ADDED
@@ -0,0 +1,2 @@
1
+ export * from "./src/index.js";
2
+ export { useNeedleMaterialX } from "./src/loader/loader.needle.js";
package/package.json CHANGED
@@ -1,32 +1,46 @@
1
1
  {
2
2
  "name": "@needle-tools/materialx",
3
- "version": "1.1.1",
3
+ "version": "1.2.0",
4
4
  "type": "module",
5
- "main": "index.ts",
5
+ "main": "index.js",
6
+ "types": "index.d.ts",
6
7
  "exports": {
7
8
  ".": {
8
- "import": "./index.ts",
9
- "require": "./index.js"
9
+ "import": "./index.js",
10
+ "require": "./index.js",
11
+ "types": "./index.d.ts"
12
+ },
13
+ "./needle": {
14
+ "import": "./needle.js",
15
+ "require": "./needle.js",
16
+ "types": "./needle.d.ts"
10
17
  },
11
18
  "./package.json": "./package.json",
12
19
  "./codegen/register_types.ts": {
13
20
  "import": "./codegen/register_types.ts",
14
21
  "require": "./codegen/register_types.js"
15
- },
16
- "./needle": {
17
- "import": "./needle.ts",
18
- "require": "./needle.js"
19
22
  }
20
23
  },
21
24
  "peerDependencies": {
22
- "@needle-tools/engine": "4.x || ^4.6.0-0",
23
- "three": ">=0.169.0"
25
+ "three": ">=0.160.0"
24
26
  },
25
27
  "devDependencies": {
26
28
  "@needle-tools/engine": "4.x",
27
29
  "@types/three": "0.169.0",
28
30
  "three": "npm:@needle-tools/three@^0.169.5"
29
31
  },
32
+ "files": [
33
+ "index.js",
34
+ "index.d.ts",
35
+ "needle.js",
36
+ "needle.d.ts",
37
+ "src/",
38
+ "bin/",
39
+ "codegen/",
40
+ "README.md",
41
+ "CHANGELOG.md",
42
+ "package.needle.json"
43
+ ],
30
44
  "publishConfig": {
31
45
  "access": "public",
32
46
  "registry": "https://registry.npmjs.org/"
package/src/index.d.ts ADDED
@@ -0,0 +1,11 @@
1
+ export { ready, type MaterialXContext } from "./materialx.js";
2
+ export { MaterialXMaterial } from "./materialx.material.js";
3
+ export { MaterialXLoader } from "./loader/loader.three.js";
4
+
5
+ import { createMaterialXMaterial } from "./loader/loader.three.js";
6
+
7
+ declare const Experimental_API: {
8
+ createMaterialXMaterial: typeof createMaterialXMaterial;
9
+ };
10
+
11
+ export { Experimental_API };
package/src/index.js ADDED
@@ -0,0 +1,11 @@
1
+ export { ready } from "./materialx.js";
2
+ export { MaterialXMaterial } from "./materialx.material.js";
3
+ export { MaterialXLoader } from "./loader/loader.three.js";
4
+
5
+ import { createMaterialXMaterial } from "./loader/loader.three.js";
6
+
7
+ const Experimental_API = {
8
+ createMaterialXMaterial
9
+ }
10
+
11
+ export { Experimental_API }
@@ -0,0 +1,15 @@
1
+ import { Context, GLTF, INeedleGLTFExtensionPlugin } from "@needle-tools/engine";
2
+ import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
3
+ import { GLTFExporter } from "three/examples/jsm/exporters/GLTFExporter.js";
4
+ import { MaterialXLoader } from "./loader.three.js";
5
+
6
+ export declare class MaterialXLoaderPlugin implements INeedleGLTFExtensionPlugin {
7
+ readonly name: "MaterialXLoaderPlugin";
8
+ private loader: MaterialXLoader | null;
9
+
10
+ onImport(loader: GLTFLoader, url: string, context: Context): void;
11
+ onLoaded(url: string, gltf: GLTF, _context: Context): void;
12
+ onExport(_exporter: GLTFExporter, _context: Context): void;
13
+ }
14
+
15
+ export declare function useNeedleMaterialX(): Promise<void>;
@@ -0,0 +1,62 @@
1
+ import { addCustomExtensionPlugin, Context } from "@needle-tools/engine";
2
+ import { useNeedleMaterialX as _useNeedleMaterialX } from "./loader.three.js";
3
+ import { debug } from "../utils.js";
4
+
5
+ /**
6
+ * @typedef {import("@needle-tools/engine").INeedleGLTFExtensionPlugin} INeedleGLTFExtensionPlugin
7
+ */
8
+
9
+ /**
10
+ * MaterialX Loader Plugin for Needle Engine
11
+ * @implements {INeedleGLTFExtensionPlugin}
12
+ */
13
+ export class MaterialXLoaderPlugin {
14
+ /** @readonly */
15
+ name = "MaterialXLoaderPlugin";
16
+
17
+ /** @type {import("./loader.three.d.ts").MaterialXLoader | null} */
18
+ loader = null;
19
+
20
+ /**
21
+ * @param {import('three/examples/jsm/loaders/GLTFLoader.js').GLTFLoader} loader
22
+ * @param {string} url
23
+ * @param {Context} context
24
+ */
25
+ onImport = (loader, url, context) => {
26
+ if (debug) console.log("MaterialXLoaderPlugin: Registering MaterialX extension for", url);
27
+ _useNeedleMaterialX(loader, {
28
+ cacheKey: url,
29
+ parameters: {
30
+ precision: /** @type {import('three').MaterialParameters["precision"]} */ (context.renderer.capabilities.getMaxPrecision("highp")),
31
+ }
32
+ }, {
33
+ getTime: () => context.time.time,
34
+ getFrame: () => context.time.frame,
35
+ });
36
+ };
37
+
38
+ /**
39
+ * @param {string} url
40
+ * @param {import("@needle-tools/engine").GLTF} gltf
41
+ * @param {Context} _context
42
+ */
43
+ onLoaded = (url, gltf, _context) => {
44
+ if (debug) console.log("[MaterialX] MaterialXLoaderPlugin: glTF loaded", { url, scene: gltf.scene, materialX_root_data: this.loader?.materialX_root_data });
45
+ };
46
+
47
+ /**
48
+ * @param {import('three/examples/jsm/exporters/GLTFExporter.js').GLTFExporter} _exporter
49
+ * @param {Context} _context
50
+ */
51
+ onExport = (_exporter, _context) => {
52
+ console.warn("[MaterialX] Export is not supported");
53
+ };
54
+ }
55
+
56
+ /**
57
+ * Add the MaterialXLoaderPlugin to the Needle Engine.
58
+ * @returns {Promise<void>}
59
+ */
60
+ export async function useNeedleMaterialX() {
61
+ addCustomExtensionPlugin(new MaterialXLoaderPlugin());
62
+ }
@@ -0,0 +1,71 @@
1
+ import { Material, MaterialParameters } from "three";
2
+ import { GLTFLoader, GLTFLoaderPlugin, GLTFParser } from "three/examples/jsm/loaders/GLTFLoader.js";
3
+ import { MaterialXContext } from "../materialx.js";
4
+ import { MaterialXMaterial } from "../materialx.material.js";
5
+ import { Callbacks } from "../materialx.helper.js";
6
+
7
+ export interface MaterialX_root_extension {
8
+ /** e.g. 1.39 */
9
+ version: string;
10
+ /** e.g. "Material" */
11
+ name: string;
12
+ /** MaterialX xml content */
13
+ mtlx: string;
14
+ /** MaterialX texture pointers */
15
+ textures: Array<{ name: string, pointer: string }>;
16
+ shaders?: Array<{
17
+ /** The materialx node name */
18
+ name: string;
19
+ /** The original name of the shader */
20
+ originalName: string;
21
+ }>;
22
+ }
23
+
24
+ export interface MaterialX_material_extension {
25
+ /** The MaterialX material name */
26
+ name: string;
27
+ /** The index of the shader in the shaders array of the root extension. */
28
+ shader?: number;
29
+ }
30
+
31
+ export interface MaterialXLoaderOptions {
32
+ /** The URL of the GLTF file being loaded */
33
+ cacheKey?: string;
34
+ /** Parameters for the MaterialX loader */
35
+ parameters?: Pick<MaterialParameters, "precision">;
36
+ }
37
+
38
+ export declare class MaterialXLoader implements GLTFLoaderPlugin {
39
+ readonly name: "NEEDLE_materials_mtlx";
40
+ private readonly _generatedMaterials: MaterialXMaterial[];
41
+ private _documentReadyPromise: Promise<any> | null;
42
+ private parser: GLTFParser;
43
+ private options: MaterialXLoaderOptions;
44
+ private context: MaterialXContext;
45
+
46
+ get materialX_root_data(): MaterialX_root_extension | null;
47
+ get materials(): MaterialXMaterial[];
48
+
49
+ constructor(
50
+ parser: GLTFParser,
51
+ options: MaterialXLoaderOptions,
52
+ context: MaterialXContext
53
+ );
54
+
55
+ loadMaterial(materialIndex: number): Promise<Material> | null;
56
+ private _loadMaterialAsync(materialIndex: number): Promise<Material>;
57
+ }
58
+
59
+ export declare function useNeedleMaterialX(
60
+ loader: GLTFLoader,
61
+ options?: MaterialXLoaderOptions,
62
+ context?: MaterialXContext
63
+ ): void;
64
+
65
+ export declare function createMaterialXMaterial(
66
+ mtlx: string,
67
+ materialNodeName: string,
68
+ loaders: Callbacks,
69
+ options?: MaterialXLoaderOptions,
70
+ context?: MaterialXContext
71
+ ): Promise<Material>;
@@ -0,0 +1,334 @@
1
+ import { Material, MeshStandardMaterial, DoubleSide, FrontSide } from "three";
2
+ import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
3
+ import { ready, state } from "../materialx.js";
4
+ import { debug } from "../utils.js";
5
+ import { MaterialXMaterial } from "../materialx.material.js";
6
+
7
+ /**
8
+ * @import { MaterialX_root_extension, MaterialX_material_extension, MaterialXLoaderOptions } from "./loader.three.d.ts"
9
+ */
10
+
11
+ /**
12
+ * @typedef {Object} MaterialDefinition
13
+ * @property {string} [name] - Optional name for the material
14
+ * @property {boolean} [doubleSided] - Whether the material is double-sided
15
+ * @property {Object<string, any>} [extensions] - Extensions for the material, including MaterialX
16
+ */
17
+
18
+ /**
19
+ * @typedef {Object} MaterialXMaterialOptions
20
+ * @property {import('three').MaterialParameters} [parameters]
21
+ */
22
+
23
+ // MaterialX loader extension for js GLTFLoader
24
+ export class MaterialXLoader {
25
+ /** @readonly */
26
+ name = "NEEDLE_materials_mtlx";
27
+
28
+ /** @type {MaterialXMaterial[]} */
29
+ _generatedMaterials = [];
30
+
31
+ /** @type {Promise<any> | null} */
32
+ _documentReadyPromise = null;
33
+
34
+ get materialX_root_data() {
35
+ return /** @type {MaterialX_root_extension | null} */ (this.parser.json.extensions?.[this.name]) || null;
36
+ }
37
+
38
+ /** Generated materialX materials */
39
+ get materials() {
40
+ return this._generatedMaterials;
41
+ }
42
+
43
+ /**
44
+ * MaterialXLoader constructor
45
+ * @param {import('three/examples/jsm/loaders/GLTFLoader.js').GLTFParser} parser - The GLTFParser instance
46
+ * @param {MaterialXLoaderOptions} options - The loader options
47
+ * @param {import('../materialx.js').MaterialXContext} context - The context for the GLTF loading process
48
+ */
49
+ constructor(parser, options, context) {
50
+ this.parser = parser;
51
+ this.options = options;
52
+ this.context = context;
53
+
54
+ if (debug) console.log("MaterialXLoader created for parser");
55
+ // Start loading of MaterialX environment if the root extension exists
56
+ if (this.materialX_root_data) {
57
+ ready();
58
+ }
59
+ }
60
+
61
+ /**
62
+ * @param {number} materialIndex
63
+ * @returns {Promise<Material> | null}
64
+ */
65
+ loadMaterial(materialIndex) {
66
+ const materialDef = this.parser.json.materials?.[materialIndex];
67
+ if (!materialDef?.extensions?.[this.name]) {
68
+ return null;
69
+ }
70
+ // Wrap the async implementation
71
+ return this._loadMaterialAsync(materialIndex);
72
+ }
73
+
74
+ /**
75
+ * @private
76
+ * @param {number} materialIndex
77
+ * @returns {Promise<Material>}
78
+ */
79
+ async _loadMaterialAsync(materialIndex) {
80
+
81
+ /** @type {MaterialDefinition} */
82
+ const materialDef = this.parser.json.materials?.[materialIndex];
83
+ if (debug) console.log("[MaterialX] extension found in material:", materialDef.extensions?.[this.name]);
84
+
85
+ // Handle different types of MaterialX data
86
+ /** @type {MaterialX_material_extension} */
87
+ const ext = materialDef.extensions?.[this.name];
88
+
89
+ const mtlx = this.materialX_root_data?.mtlx;
90
+
91
+ if (ext && mtlx) {
92
+
93
+ /** @type {MaterialXMaterialOptions} */
94
+ const materialOptions = {
95
+ ...this.options,
96
+ }
97
+
98
+ if (!materialOptions.parameters) materialOptions.parameters = {};
99
+
100
+ if (materialOptions.parameters?.side === undefined && materialDef.doubleSided !== undefined) {
101
+ materialOptions.parameters.side = materialDef.doubleSided ? DoubleSide : FrontSide;
102
+ }
103
+
104
+ return createMaterialXMaterial(mtlx, ext.name, {
105
+ cacheKey: this.options.cacheKey || "",
106
+ getTexture: async url => {
107
+ // Find the index of the texture in the parser
108
+ const filenameWithoutExt = url.split('/').pop()?.split('.').shift() || '';
109
+
110
+ // Resolve the texture from the MaterialX root extension
111
+ if (this.materialX_root_data) {
112
+ const textures = this.materialX_root_data.textures || [];
113
+ let index = -1;
114
+ for (const texture of textures) {
115
+ // Find the texture by name and use the pointer string to get the index
116
+ if (texture.name === filenameWithoutExt) {
117
+ const ptr = texture.pointer;
118
+ const indexStr = ptr.substring("/textures/".length);
119
+ index = parseInt(indexStr);
120
+
121
+ if (isNaN(index) || index < 0) {
122
+ console.error("[MaterialX] Invalid texture index in pointer:", ptr);
123
+ return;
124
+ }
125
+ else {
126
+ if (debug) console.log("[MaterialX] Texture index found:", index, "for", filenameWithoutExt);
127
+ }
128
+ }
129
+ }
130
+
131
+ if (index < 0) {
132
+ console.error("[MaterialX] Texture not found in parser:", filenameWithoutExt, this.parser.json);
133
+ return;
134
+ }
135
+ return this.parser.getDependency("texture", index);
136
+ }
137
+ return null;
138
+ }
139
+ }, materialOptions, this.context)
140
+ // Cache and return the generated material
141
+ .then(mat => {
142
+ if (mat instanceof MaterialXMaterial) this._generatedMaterials.push(mat);
143
+ return mat;
144
+ })
145
+ }
146
+
147
+ // Return fallback material instead of null
148
+ const fallbackMaterial = new MeshStandardMaterial();
149
+ fallbackMaterial.name = "MaterialX_Fallback";
150
+ return fallbackMaterial;
151
+ }
152
+ }
153
+
154
+ /**
155
+ * Add the MaterialXLoader to the GLTFLoader instance.
156
+ * @param {GLTFLoader} loader
157
+ * @param {MaterialXLoaderOptions} [options]
158
+ * @param {import('../materialx.js').MaterialXContext} [context]
159
+ */
160
+ export function useNeedleMaterialX(loader, options, context) {
161
+ loader.register(p => {
162
+ const loader = new MaterialXLoader(p, options || {}, context || {});
163
+ return loader;
164
+ });
165
+ }
166
+
167
+ /**
168
+ * Parse the MaterialX document once and cache it
169
+ * @param {string} mtlx
170
+ * @returns {Promise<any>}
171
+ */
172
+ async function load(mtlx) {
173
+ // Ensure MaterialX is initialized
174
+ await ready();
175
+ if (!state.materialXModule) {
176
+ throw new Error("[MaterialX] module failed to initialize");
177
+ }
178
+ // Create MaterialX document and parse ALL the XML data from root
179
+ const doc = state.materialXModule.createDocument();
180
+ doc.setDataLibrary(state.materialXStdLib);
181
+ // Parse all MaterialX XML strings from the root data
182
+ await state.materialXModule.readFromXmlString(doc, mtlx, "");
183
+ if (debug) console.log("[MaterialX] root document parsed successfully");
184
+ return doc;
185
+ }
186
+
187
+ /**
188
+ * @param {string} mtlx
189
+ * @param {string} materialNodeName
190
+ * @param {import('../materialx.helper.js').Callbacks} loaders
191
+ * @param {MaterialXLoaderOptions} [options]
192
+ * @param {import('../materialx.js').MaterialXContext} [context]
193
+ * @returns {Promise<Material>}
194
+ */
195
+ export async function createMaterialXMaterial(mtlx, materialNodeName, loaders, options, context) {
196
+ try {
197
+ if (debug) console.log(`Creating MaterialX material: ${materialNodeName}`);
198
+
199
+ const doc = await load(mtlx);
200
+
201
+ if (!state.materialXModule || !state.materialXGenerator || !state.materialXGenContext) {
202
+ console.warn("[MaterialX] WASM module not ready, returning fallback material");
203
+ const fallbackMaterial = new MeshStandardMaterial();
204
+ fallbackMaterial.name = `MaterialX_Fallback_${materialNodeName}`;
205
+ return fallbackMaterial;
206
+ }
207
+
208
+ // Find the renderable element following MaterialX example pattern exactly
209
+ let renderableElement = null;
210
+ let foundRenderable = false;
211
+
212
+ if (debug) console.log("[MaterialX] document", doc);
213
+
214
+ // Search for material nodes first (following the reference pattern)
215
+ const materialNodes = doc.getMaterialNodes();
216
+ if (debug) console.log(`[MaterialX] Found ${materialNodes.length} material nodes in document`, materialNodes);
217
+
218
+ // Handle both array and vector-like APIs
219
+ for (let i = 0; i < materialNodes.length; ++i) {
220
+ const materialNode = materialNodes[i];
221
+ if (materialNode) {
222
+ const name = materialNode.getNamePath();
223
+ if (debug) console.log(`[MaterialX] Scan material[${i}]: ${name}`);
224
+
225
+ // Find the matching material
226
+ if (materialNodes.length === 1 || name == materialNodeName) {
227
+ materialNodeName = name;
228
+ renderableElement = materialNode;
229
+ foundRenderable = true;
230
+ if (debug) console.log(`[MaterialX] Use material node: '${name}'`);
231
+ break;
232
+ }
233
+ }
234
+ }
235
+
236
+ /*
237
+ // If no material nodes found, search nodeGraphs
238
+ if (!foundRenderable) {
239
+ const nodeGraphs = doc.getNodeGraphs();
240
+ console.log(`Found ${nodeGraphs.length} node graphs in document`);
241
+ const nodeGraphsLength = nodeGraphs.length;
242
+ for (let i = 0; i < nodeGraphsLength; ++i) {
243
+ const nodeGraph = nodeGraphs[i];
244
+ if (nodeGraph) {
245
+ // Skip any nodegraph that has nodedef or sourceUri
246
+ if ((nodeGraph as any).hasAttribute('nodedef') || (nodeGraph as any).hasSourceUri()) {
247
+ continue;
248
+ }
249
+ // Skip any nodegraph that is connected to something downstream
250
+ if ((nodeGraph as any).getDownstreamPorts().length > 0) {
251
+ continue;
252
+ }
253
+ const outputs = (nodeGraph as any).getOutputs();
254
+ for (let j = 0; j < outputs.length; ++j) {
255
+ const output = outputs[j];
256
+ if (output && !foundRenderable) {
257
+ renderableElement = output;
258
+ foundRenderable = true;
259
+ break;
260
+ }
261
+ }
262
+ if (foundRenderable) break;
263
+ }
264
+ }
265
+ }
266
+
267
+ // If still no element found, search document outputs
268
+ if (!foundRenderable) {
269
+ const outputs = doc.getOutputs();
270
+ console.log(`Found ${outputs.length} output nodes in document`);
271
+ const outputsLength = outputs.length;
272
+ for (let i = 0; i < outputsLength; ++i) {
273
+ const output = outputs[i];
274
+ if (output && !foundRenderable) {
275
+ renderableElement = output;
276
+ foundRenderable = true;
277
+ break;
278
+ }
279
+ }
280
+ }
281
+ */
282
+
283
+ if (!renderableElement) {
284
+ console.warn(`[MaterialX] No renderable element found in MaterialX document (${materialNodeName})`);
285
+ const fallbackMaterial = new MeshStandardMaterial();
286
+ fallbackMaterial.color.set(0xff00ff);
287
+ fallbackMaterial.name = `MaterialX_NoRenderable_${materialNodeName}`;
288
+ return fallbackMaterial;
289
+ }
290
+
291
+ if (debug) console.log("[MaterialX] Using renderable element for shader generation");
292
+
293
+ // Check transparency and set context options like the reference
294
+ const isTransparent = state.materialXModule.isTransparentSurface(renderableElement, state.materialXGenerator.getTarget());
295
+ state.materialXGenContext.getOptions().hwTransparency = isTransparent;
296
+
297
+ // Generate shaders using the element's name path
298
+ if (debug) console.log("[MaterialX] Generating MaterialX shaders...");
299
+ const elementName = renderableElement.getNamePath ? renderableElement.getNamePath() : renderableElement.getName();
300
+
301
+ const shader = state.materialXGenerator.generate(elementName, renderableElement, state.materialXGenContext);
302
+
303
+ // const rootExtension = this.materialX_root_data;
304
+
305
+ // const shaderInfo = rootExtension && material_extension.shader !== undefined && material_extension.shader >= 0
306
+ // ? rootExtension.shaders?.[material_extension.shader]
307
+ // : null;
308
+
309
+ const shaderMaterial = new MaterialXMaterial({
310
+ name: materialNodeName,
311
+ shaderName: null, //shaderInfo?.originalName || shaderInfo?.name || null,
312
+ shader,
313
+ context: context || {},
314
+ parameters: {
315
+ transparent: isTransparent,
316
+ ...options?.parameters,
317
+ },
318
+ loaders: loaders,
319
+ });
320
+
321
+ // Add debugging to see if the material compiles correctly
322
+ if (debug) console.log("[MaterialX] material created:", shaderMaterial.name);
323
+ return shaderMaterial;
324
+
325
+ } catch (error) {
326
+ // This is a wasm error (an int) that we need to resolve
327
+ console.error(`[MaterialX] Error creating MaterialX material (${materialNodeName}):`, error);
328
+ // Return a fallback material with stored MaterialX data
329
+ const fallbackMaterial = new MeshStandardMaterial();
330
+ fallbackMaterial.color.set(0xff00ff);
331
+ fallbackMaterial.name = `MaterialX_Error_${materialNodeName}`;
332
+ return fallbackMaterial;
333
+ }
334
+ }
@@ -0,0 +1,60 @@
1
+ import { Light, Scene, Texture, WebGLRenderer } from "three";
2
+ import type { MaterialX as MX } from "./materialx.types.js";
3
+
4
+ export type MaterialXContext = {
5
+ getTime?(): number;
6
+ getFrame?(): number;
7
+ }
8
+
9
+ type EnvironmentTextureSet = {
10
+ radianceTexture: Texture | null;
11
+ irradianceTexture: Texture | null;
12
+ }
13
+
14
+ export declare const state: {
15
+ materialXModule: MX.MODULE | null;
16
+ materialXGenerator: any | null;
17
+ materialXGenContext: any | null;
18
+ materialXStdLib: any | null;
19
+ materialXInitPromise: Promise<void> | null;
20
+ };
21
+
22
+ /**
23
+ * Wait for the MaterialX WASM module to be ready.
24
+ */
25
+ export declare function ready(): Promise<void>;
26
+
27
+ /**
28
+ * MaterialXEnvironment manages the environment settings for MaterialX materials.
29
+ */
30
+ export declare class MaterialXEnvironment {
31
+ static get(scene: Scene): MaterialXEnvironment | null;
32
+ private static _environments: WeakMap<Scene, MaterialXEnvironment>;
33
+ private static getEnvironment(scene: Scene): MaterialXEnvironment;
34
+
35
+ private _lights: Array<Light>;
36
+ private _lightData: any[] | null;
37
+ private _lightCount: number;
38
+ private _initializePromise: Promise<boolean> | null;
39
+ private _isInitialized: boolean;
40
+ private _lastUpdateFrame: number;
41
+ private _scene: Scene;
42
+
43
+ constructor(_scene: Scene);
44
+
45
+ initialize(renderer: WebGLRenderer): Promise<boolean>;
46
+ update(frame: number, scene: Scene, renderer: WebGLRenderer): void;
47
+ reset(): void;
48
+
49
+ get lights(): Array<Light>;
50
+ get lightData(): any[] | null;
51
+ get lightCount(): number;
52
+ getTextures(material: any): EnvironmentTextureSet;
53
+
54
+ private _pmremGenerator: any | null;
55
+ private _renderer: WebGLRenderer | null;
56
+ private _texturesCache: Map<Texture | null, EnvironmentTextureSet>;
57
+ private _initialize(renderer: WebGLRenderer): Promise<boolean>;
58
+ private _getTextures(texture: Texture | null | undefined): EnvironmentTextureSet;
59
+ private updateLighting(collectLights?: boolean): void;
60
+ }