@needle-tools/materialx 1.0.1-next.31390e3 → 1.0.1-next.64f3b67

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.
@@ -1,84 +1,70 @@
1
1
  import { Context } from "@needle-tools/engine";
2
- import { ShaderMaterial, Material, MeshStandardMaterial, LoadingManager, TextureLoader, Texture, NearestFilter, Matrix4, GLSL3, AddEquation, OneMinusSrcAlphaFactor, SrcAlphaFactor, DoubleSide, Matrix3, Vector3, Object3D, Camera } from "three";
3
- import { GLTFLoaderPlugin, GLTFParser } from "three/examples/jsm/loaders/GLTFLoader.js";
4
- import { getUniformValues } from "../helper.js";
2
+ import { Material, MeshStandardMaterial, Texture, NearestFilter, CompressedTexture } from "three";
3
+ import { GLTF, GLTFLoaderPlugin, GLTFParser } from "three/examples/jsm/loaders/GLTFLoader.js";
5
4
  import { ready, MaterialXEnvironment, state } from "../materialx.js";
6
5
  import { debug } from "../utils.js";
6
+ import { MaterialXMaterial } from "../materialx.material.js";
7
7
 
8
8
  // TypeScript interfaces matching the C# data structures
9
- interface MaterialXData {
10
- version: string; // e.g. "1.39"
11
- name: string; // e.g. "Material"
12
- mtlx: string; // MaterialX XML content
9
+ export type MaterialX_root_extension = {
10
+ /** e.g. 1.39 */
11
+ version: string;
12
+ /** e.g. "Material" */
13
+ name: string;
14
+ /** MaterialX xml content */
15
+ mtlx: string;
13
16
  }
14
17
 
15
- interface MaterialXDataIndex {
16
- name: string; // Material name reference
18
+ export type MaterialX_material_extension = {
19
+ name: string; // Material name reference
17
20
  }
18
21
 
19
22
  // MaterialX loader extension for js GLTFLoader
20
23
  export class MaterialXLoader implements GLTFLoaderPlugin {
21
- name = "NEEDLE_materials_mtlx";
24
+ readonly name = "NEEDLE_materials_mtlx";
22
25
 
23
- private rootMaterialXData: MaterialXData | null = null;
24
- private parsedDocument: any = null;
25
- private documentParsePromise: Promise<any> | null = null;
26
- private rootDataInitialized = false;
27
- private environmentInitialized = false;
28
- private generatedMaterials: ShaderMaterial[] = [];
26
+ private readonly _generatedMaterials: MaterialXMaterial[] = [];
29
27
 
30
- constructor(private parser: GLTFParser, private context: Context) {
31
- if (debug) console.log("MaterialXLoader created for parser");
32
- // Initialize MaterialX environment after MaterialX is ready
33
- this.initializeEnvironment();
34
- }
35
-
36
- // Initialize MaterialX environment - called once after MaterialX is ready
37
- private async initializeEnvironment(): Promise<void> {
38
- if (this.environmentInitialized) return;
28
+ private _documentReadyPromise: Promise<any> | null = null;
39
29
 
40
- if (debug) console.log("[MaterialX] MaterialXLoader: Initializing MaterialX environment...");
41
-
42
- // Ensure MaterialX is initialized first
43
- await ready();
30
+ get materialX_root_data() {
31
+ return this.parser.json.extensions?.[this.name] as MaterialX_root_extension | null;
32
+ }
44
33
 
45
- // Set up environment with context
46
- const environment = state.materialXEnvironment;
47
- environment.context = this.context;
34
+ /** Generated materialX materials */
35
+ get materials() {
36
+ return this._generatedMaterials;
37
+ }
48
38
 
49
- // Initialize the environment from context (properly awaited)
50
- try {
51
- await environment.initializeFromContext();
52
- this.environmentInitialized = true;
53
- if (debug) console.log("[MaterialX] MaterialXLoader: Environment initialized successfully");
54
- } catch (error) {
55
- console.warn("[MaterialX] MaterialXLoader: Failed to initialize MaterialX environment:", error);
39
+ constructor(private parser: GLTFParser, private context: Context) {
40
+ if (debug) console.log("MaterialXLoader created for parser");
41
+ // Start loading of MaterialX environment if the root extension exists
42
+ if (this.materialX_root_data) {
43
+ ready();
56
44
  }
57
45
  }
58
46
 
59
- // Initialize root data from parser.json.extensions once
60
- private initializeRootData(): void {
61
- if (this.rootDataInitialized) return;
62
-
63
- const gltfExtensions = this.parser.json.extensions;
64
- if (gltfExtensions?.[this.name]) {
65
- if (debug) console.log("[MaterialX] extension found in root:", gltfExtensions[this.name]);
66
-
67
- const materialXExtension = gltfExtensions[this.name];
68
- this.rootMaterialXData = materialXExtension as MaterialXData;
47
+ loadMaterial(materialIndex: number): Promise<Material> | null {
48
+ const materialDef = this.parser.json.materials?.[materialIndex];
49
+ if (!materialDef?.extensions?.[this.name]) {
50
+ return null;
69
51
  }
70
- this.rootDataInitialized = true;
52
+ // Wrap the async implementation
53
+ return this._loadMaterialAsync(materialIndex);
54
+ }
55
+
56
+ afterRoot = async (_gltf: GLTF) => {
57
+ // Initialize MaterialX lighting system with scene data
58
+ const environment = state.materialXEnvironment;
59
+ environment.initialize(this.context);
71
60
  }
72
61
 
73
62
  // Parse the MaterialX document once and cache it
74
- private async parseRootDocument(): Promise<any> {
75
- if (this.documentParsePromise) {
76
- return this.documentParsePromise;
63
+ private async _materialXDocumentReady(): Promise<any> {
64
+ if (this._documentReadyPromise) {
65
+ return this._documentReadyPromise;
77
66
  }
78
-
79
- this.documentParsePromise = (async () => {
80
- if (this.parsedDocument) return this.parsedDocument;
81
-
67
+ return this._documentReadyPromise = (async () => {
82
68
  if (debug) console.log("[MaterialX] Parsing MaterialX root document...");
83
69
 
84
70
  // Ensure MaterialX is initialized
@@ -93,43 +79,28 @@ export class MaterialXLoader implements GLTFLoaderPlugin {
93
79
  doc.setDataLibrary(state.materialXStdLib);
94
80
 
95
81
  // Parse all MaterialX XML strings from the root data
96
- if (this.rootMaterialXData) {
97
- if (debug) console.log(`[MaterialX] Parsing XML for: ${this.rootMaterialXData.name}`);
98
- await state.materialXModule.readFromXmlString(doc, this.rootMaterialXData.mtlx, "");
82
+ const root = this.materialX_root_data
83
+ if (root) {
84
+ if (debug) console.log(`[MaterialX] Parsing XML for: ${root.name}`);
85
+ await state.materialXModule.readFromXmlString(doc, root.mtlx, "");
99
86
  }
100
87
 
101
88
  if (debug) console.log("[MaterialX] root document parsed successfully");
102
-
103
- this.parsedDocument = doc;
104
89
  return doc;
105
90
  })();
106
-
107
- return this.documentParsePromise;
108
- }
109
-
110
- loadMaterial(materialIndex: number): Promise<Material> | null {
111
- const materialDef = this.parser.json.materials?.[materialIndex];
112
- if (!materialDef?.extensions?.[this.name]) {
113
- return null;
114
- }
115
-
116
- // Wrap the async implementation
117
- return this._loadMaterialAsync(materialIndex);
118
91
  }
119
92
 
120
93
  private async _loadMaterialAsync(materialIndex: number): Promise<Material> {
121
- // Initialize root data first
122
- this.initializeRootData();
123
94
 
124
95
  const materialDef = this.parser.json.materials?.[materialIndex];
125
96
  if (debug) console.log("[MaterialX] extension found in material:", materialDef.extensions[this.name]);
126
97
 
127
98
  // Handle different types of MaterialX data
128
- const dataIndex = materialDef.extensions[this.name] as MaterialXDataIndex;
99
+ const dataIndex = materialDef.extensions[this.name] as MaterialX_material_extension;
129
100
 
130
101
  if (dataIndex) {
131
102
  // Create a new material and process MaterialX - AWAIT THIS!
132
- return await this.createMaterialXMaterial(dataIndex);
103
+ return await this._createMaterialXMaterial(dataIndex);
133
104
  }
134
105
 
135
106
  // Return fallback material instead of null
@@ -138,29 +109,22 @@ export class MaterialXLoader implements GLTFLoaderPlugin {
138
109
  return fallbackMaterial;
139
110
  }
140
111
 
141
- private rootDocument: Promise<any> | null = null;
142
- private async createMaterialXMaterial(materialXData: MaterialXDataIndex): Promise<Material> {
112
+ private async _createMaterialXMaterial(material_extension: MaterialX_material_extension): Promise<Material> {
143
113
  try {
144
- if (debug) console.log(`Creating MaterialX material: ${materialXData.name}`);
114
+ if (debug) console.log(`Creating MaterialX material: ${material_extension.name}`);
145
115
 
146
- // Ensure MaterialX is initialized and document is parsed
147
- await ready();
148
-
149
- if (!this.rootDocument) {
150
- this.rootDocument = this.parseRootDocument();
151
- }
152
- const doc = await this.rootDocument;
116
+ const doc = await this._materialXDocumentReady();
153
117
 
154
118
  if (!state.materialXModule || !state.materialXGenerator || !state.materialXGenContext) {
155
119
  console.warn("[MaterialX] WASM module not ready, returning fallback material");
156
120
  const fallbackMaterial = new MeshStandardMaterial();
157
- fallbackMaterial.userData.materialX = materialXData;
158
- fallbackMaterial.name = `MaterialX_Fallback_${materialXData.name}`;
121
+ fallbackMaterial.userData.materialX = material_extension;
122
+ fallbackMaterial.name = `MaterialX_Fallback_${material_extension.name}`;
159
123
  return fallbackMaterial;
160
124
  }
161
125
 
162
126
  // Find the renderable element following MaterialX example pattern exactly
163
- let renderableElement = null;
127
+ let renderableElement: any = null;
164
128
  let foundRenderable = false;
165
129
 
166
130
  if (debug) console.log("[MaterialX] document", doc);
@@ -178,7 +142,7 @@ export class MaterialXLoader implements GLTFLoaderPlugin {
178
142
  if (debug) console.log('[MaterialX] Scan material: ', i, materialName);
179
143
 
180
144
  // Find the matching material
181
- if (materialName == materialXData.name) {
145
+ if (materialName == material_extension.name) {
182
146
  renderableElement = materialNode;
183
147
  foundRenderable = true;
184
148
  if (debug) console.log('[MaterialX] -- add material: ', materialName);
@@ -237,262 +201,88 @@ export class MaterialXLoader implements GLTFLoaderPlugin {
237
201
  if (!renderableElement) {
238
202
  console.warn("[MaterialX] No renderable element found in MaterialX document");
239
203
  const fallbackMaterial = new MeshStandardMaterial();
240
- fallbackMaterial.userData.materialX = materialXData;
241
- fallbackMaterial.name = `MaterialX_NoRenderable_${materialXData.name}`;
204
+ fallbackMaterial.userData.materialX = material_extension;
205
+ fallbackMaterial.name = `MaterialX_NoRenderable_${material_extension.name}`;
242
206
  return fallbackMaterial;
243
207
  }
244
208
 
245
209
  if (debug) console.log("[MaterialX] Using renderable element for shader generation");
246
210
 
247
211
  // Check transparency and set context options like the reference
248
- let isTransparent = state.materialXModule.isTransparentSurface(renderableElement, state.materialXGenerator.getTarget());
212
+ const isTransparent = state.materialXModule.isTransparentSurface(renderableElement, state.materialXGenerator.getTarget());
249
213
  state.materialXGenContext.getOptions().hwTransparency = isTransparent;
250
-
214
+
251
215
  // Generate shaders using the element's name path
252
216
  if (debug) console.log("[MaterialX] Generating MaterialX shaders...");
253
217
  const elementName = (renderableElement as any).getNamePath ? (renderableElement as any).getNamePath() : (renderableElement as any).getName();
254
218
 
255
219
  const shader = state.materialXGenerator.generate(elementName, renderableElement, state.materialXGenContext);
256
-
257
- // Get vertex and fragment shader source, and remove #version directive for newer js.
258
- // It's added by three.js glslVersion.
259
- let vertexShader = shader.getSourceCode("vertex").replace(/^#version.*$/gm, '').trim();
260
- let fragmentShader = shader.getSourceCode("pixel").replace(/^#version.*$/gm, '').trim();
261
-
262
- // MaterialX uses different attribute names than js defaults,
263
- // so we patch the MaterialX shaders to match the js standard names.
264
- // Otherwise, we'd have to modify the mesh attributes (see original MaterialX for reference).
265
-
266
- // Patch vertexShader
267
- vertexShader = vertexShader.replace(/\bi_position\b/g, 'position');
268
- vertexShader = vertexShader.replace(/\bi_normal\b/g, 'normal');
269
- vertexShader = vertexShader.replace(/\bi_texcoord_0\b/g, 'uv');
270
- vertexShader = vertexShader.replace(/\bi_texcoord_1\b/g, 'uv1');
271
- vertexShader = vertexShader.replace(/\bi_tangent\b/g, 'tangent');
272
- vertexShader = vertexShader.replace(/\bi_color_0\b/g, 'color');
273
-
274
- // Patch fragmentShader
275
- fragmentShader = fragmentShader.replace(/\bi_position\b/g, 'position');
276
- fragmentShader = fragmentShader.replace(/\bi_normal\b/g, 'normal');
277
- fragmentShader = fragmentShader.replace(/\bi_texcoord_0\b/g, 'uv');
278
- fragmentShader = fragmentShader.replace(/\bi_texcoord_1\b/g, 'uv1');
279
- fragmentShader = fragmentShader.replace(/\bi_tangent\b/g, 'tangent');
280
- fragmentShader = fragmentShader.replace(/\bi_color_0\b/g, 'color');
281
-
282
- // Remove `in vec3 position;` and so on since they're already declared by ShaderMaterial
283
- vertexShader = vertexShader.replace(/in\s+vec3\s+position;/g, '');
284
- vertexShader = vertexShader.replace(/in\s+vec3\s+normal;/g, '');
285
- vertexShader = vertexShader.replace(/in\s+vec3\s+uv;/g, '');
286
- vertexShader = vertexShader.replace(/in\s+vec3\s+uv1;/g, '');
287
- vertexShader = vertexShader.replace(/in\s+vec4\s+tangent;/g, '');
288
- vertexShader = vertexShader.replace(/in\s+vec4\s+color;/g, '');
289
-
290
- // Patch uv 2-component to 3-component (`texcoord_0 = uv;` needs to be replaced with `texcoord_0 = vec3(uv, 0.0);`)
291
- // TODO what if we actually have a 3-component UV? Not sure what three.js does then
292
- vertexShader = vertexShader.replace(/texcoord_0 = uv;/g, 'texcoord_0 = vec3(uv, 0.0);');
293
-
294
- // Add tonemapping and colorspace handling
295
- // Replace `out vec4 out1;` with `out vec4 gl_FragColor;`
296
- fragmentShader = fragmentShader.replace(/out\s+vec4\s+out1;/, 'layout(location = 0) out vec4 pc_fragColor;\n#define gl_FragColor pc_fragColor');
297
- // Replace `out1 = vec4(<CAPTURE>)` with `gl_FragColor = vec4(<CAPTURE>)` and tonemapping/colorspace handling
298
- fragmentShader = fragmentShader.replace(/^\s*out1\s*=\s*vec4\((.*)\);/gm,
299
- `
300
- gl_FragColor = vec4($1);
301
- #include <tonemapping_fragment>
302
- #include <colorspace_fragment>`);
303
-
304
- if (debug) {
305
- console.group("[MaterialX]: ", materialXData.name);
306
- console.log("Vertex shader length:", vertexShader.length, vertexShader);
307
- console.log("Fragment shader length:", fragmentShader.length, fragmentShader);
308
- console.groupEnd();
309
- }
310
-
311
- // Extract uniforms from both vertex and pixel stages
312
- const loadingManager = new LoadingManager();
313
- loadingManager.setURLModifier((url: string) => {
314
- if (debug) console.log("Loading URL:", url);
315
- // Find the texture in the textures in the parser and load it from there
316
- return url;
317
- });
318
- let textureLoader = new TextureLoader(loadingManager);
319
-
320
- // Override the textureLoader.load method to use the parser's loadTexture directly,
321
- // since we want to load the textures from the glTF document and not from disk.
322
- // That loader does its own caching too.
323
- textureLoader.load = (url: string, onLoad?: (texture: Texture) => void, onProgress?: (event: ProgressEvent) => void, onError?: (err: Error) => void): Texture => {
324
-
325
- // Return a checkerboard texture for now
326
- const checkerboardTexture = new Texture();
327
- checkerboardTexture.image = new Image();
328
- checkerboardTexture.image.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91JpzAAAAG0lEQVR4nGJqWH9q9e8XjA/VrL8UfQIEAAD//zn2CCX5UcsdAAAAAElFTkSuQmCC";
329
- checkerboardTexture.needsUpdate = true;
330
- // Pixelated filtering
331
- checkerboardTexture.magFilter = NearestFilter;
332
- checkerboardTexture.minFilter = NearestFilter;
333
-
334
- new Promise(() => {
335
- // Find the index of the texture in the parser
336
- const filenameWithoutExt = url.split('/').pop()?.split('.').shift() || '';
337
-
338
- // Find in the root extension; there's the textures dictionary:
339
- // a mapping from the texture name used in MaterialX to the glTF texture index
340
- const ext = this.parser.json.extensions?.[this.name];
341
- const textures = ext?.textures || [];
342
-
343
- const index = textures.findIndex(tex => {
344
- if (debug) console.log("[MaterialX] Checking texture:", tex.name, "against URL:", filenameWithoutExt);
345
- return tex.name === filenameWithoutExt;
346
- });
347
-
348
- if (index < 0) {
349
- console.warn("[MaterialX] Texture not found in parser:", filenameWithoutExt, this.parser.json);
350
- onError?.(new Error(`Texture not found: ${filenameWithoutExt}`));
351
- return;
352
- }
353
- this.parser.getDependency("texture", index).then(tex => {
354
- if (debug) console.log("[MaterialX] Texture loaded:", tex);
355
- // update the checkerboard texture with the loaded texture
356
- checkerboardTexture.image = tex.image;
357
- checkerboardTexture.needsUpdate = true;
358
- onLoad?.(tex);
359
- }).catch(err => {
360
- onError?.(err);
361
- });
362
- });
363
-
364
- return checkerboardTexture;
365
- };
366
-
367
- const searchPath = ""; // Could be derived from the asset path if needed
368
- const flipV = false; // Set based on your geometry requirements
369
-
370
- let uniforms = {
371
- ...getUniformValues(shader.getStage('vertex'), textureLoader, searchPath, flipV),
372
- ...getUniformValues(shader.getStage('pixel'), textureLoader, searchPath, flipV),
373
- };
374
-
375
- // Get lighting and environment data from MaterialX environment
376
- const environment = state.materialXEnvironment;
377
- // const lights = environment.getLights() || [];
378
- const lightData = environment.getLightData() || null;
379
- const radianceTexture = environment.getRadianceTexture() || null;
380
- const irradianceTexture = environment.getIrradianceTexture() || null;
381
- const getLightRotation = () => {
382
- // Placeholder for light rotation logic, if needed
383
- return new Matrix4();
384
- };
385
-
386
- if (debug) console.log({ lightData, radianceTexture, irradianceTexture });
387
-
388
- Object.assign(uniforms, {
389
- u_numActiveLightSources: { value: lightData?.length || 0, type: 'i' },
390
- });
391
-
392
- if (lightData?.length) {
393
- Object.assign(uniforms, {
394
- u_lightData: { value: lightData, type: 'v4v' },
395
- });
396
- }
397
-
398
- const mips = Math.trunc(Math.log2(Math.max(radianceTexture?.width ?? 0, radianceTexture?.height ?? 0))) + 1;
399
- if (debug) console.log("[MaterialX] Radiance texture mips:", mips, "for texture size:", radianceTexture?.width, "x", radianceTexture?.height);
400
- Object.assign(uniforms, {
401
- u_envMatrix: { value: getLightRotation() },
402
- u_envRadiance: { value: radianceTexture, type: 't' },
403
- u_envRadianceMips: { value: 8, type: 'i' },
404
- // TODO we need to figure out how we can set a PMREM here... doing many texture samples is prohibitively expensive
405
- u_envRadianceSamples: { value: 8, type: 'i' },
406
- u_envIrradiance: { value: irradianceTexture, type: 't' },
407
- u_refractionEnv: { value: true },
408
- });
409
-
410
- // console.log("NEW MATERIAL UNIFORMS", uniforms);
411
-
412
- // Debug: Log the actual shaders to see what we're working with
413
- // console.log("Generated vertex shader:", vertexShader.substring(0, 500) + "...");
414
- // console.log("Generated fragment shader:", fragmentShader.substring(0, 500) + "...");
415
-
416
- // Create js RawShaderMaterial (with GLSL3 for MaterialX compatibility)
417
- const shaderMaterial = new ShaderMaterial({
418
- uniforms: uniforms,
419
- vertexShader: vertexShader,
420
- fragmentShader: fragmentShader,
421
- glslVersion: GLSL3,
220
+ const shaderMaterial = new MaterialXMaterial({
221
+ name: material_extension.name,
222
+ shader,
422
223
  transparent: isTransparent,
423
- // Only set blend settings if actually transparent
424
- ...(isTransparent && {
425
- blendEquation: AddEquation,
426
- blendSrc: OneMinusSrcAlphaFactor,
427
- blendDst: SrcAlphaFactor,
428
- }),
429
- side: DoubleSide,
430
- // Add some debug settings
431
- depthTest: true,
432
- depthWrite: !isTransparent
433
- });
434
-
435
- // Add helper matrices for uniform updates (similar to MaterialX example)
436
- const normalMat = new Matrix3();
437
- const viewProjMat = new Matrix4();
438
- const worldViewPos = new Vector3();
439
-
440
- // Store helper objects on the material for uniform updates
441
- shaderMaterial.userData.materialX = materialXData;
442
- shaderMaterial.userData.updateUniforms = (object: Object3D, camera: Camera) => {
443
- const uniforms = shaderMaterial.uniforms;
444
-
445
- if (!uniforms) return;
446
-
447
- // TODO remove. Not sure why this is needed, but without it
448
- // we currently get some "swimming" where matrices are not up to date.
449
- camera.updateMatrixWorld(true);
450
-
451
- // Update standard transformation matrices
452
- if (uniforms.u_worldMatrix) {
453
- if (!uniforms.u_worldMatrix.value?.isMatrix4) uniforms.u_worldMatrix.value = new Matrix4();
454
- uniforms.u_worldMatrix.value = object.matrixWorld;
455
- }
456
-
457
- if (uniforms.u_viewProjectionMatrix) {
458
- if (!uniforms.u_viewProjectionMatrix.value?.isMatrix4) uniforms.u_viewProjectionMatrix.value = new Matrix4();
459
- uniforms.u_viewProjectionMatrix.value.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);
460
- }
461
-
462
- if (uniforms.u_viewPosition) {
463
- if (!uniforms.u_viewPosition.value?.isVector3) uniforms.u_viewPosition.value = new Vector3();
464
- uniforms.u_viewPosition.value.copy(camera.getWorldPosition(worldViewPos));
465
- }
466
-
467
- if (uniforms.u_worldInverseTransposeMatrix) {
468
- if (!uniforms.u_worldInverseTransposeMatrix.value?.isMatrix4) uniforms.u_worldInverseTransposeMatrix.value = new Matrix4();
469
- uniforms.u_worldInverseTransposeMatrix.value.setFromMatrix3(normalMat.getNormalMatrix(object.matrixWorld));
470
- }
471
-
472
- // Update time uniforms
473
- const time = performance.now() / 1000.0;
474
- if (uniforms.u_time) {
475
- uniforms.u_time.value = time;
476
- }
477
- if (uniforms.u_frame) {
478
- uniforms.u_frame.value = time;
224
+ loaders: {
225
+ getTexture: url => {
226
+ // Return a checkerboard texture for now
227
+ const defaultTexture = new Texture();
228
+ defaultTexture.image = new Image();
229
+ defaultTexture.image.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91JpzAAAAG0lEQVR4nGJqWH9q9e8XjA/VrL8UfQIEAAD//zn2CCX5UcsdAAAAAElFTkSuQmCC";
230
+ defaultTexture.needsUpdate = true;
231
+ // Pixelated filtering
232
+ defaultTexture.magFilter = NearestFilter;
233
+ defaultTexture.minFilter = NearestFilter;
234
+ new Promise(() => {
235
+ // Find the index of the texture in the parser
236
+ const filenameWithoutExt = url.split('/').pop()?.split('.').shift() || '';
237
+
238
+ // Find in the root extension; there's the textures dictionary:
239
+ // a mapping from the texture name used in MaterialX to the glTF texture index
240
+ const ext = this.parser.json.extensions?.["NEEDLE_materials_mtlx"];
241
+ const textures = ext?.textures || [];
242
+
243
+ const index = textures.findIndex(tex => {
244
+ if (debug) console.log("[MaterialX] Checking texture:", tex.name, "against URL:", filenameWithoutExt);
245
+ return tex.name === filenameWithoutExt;
246
+ });
247
+
248
+ if (index < 0) {
249
+ console.error("[MaterialX] Texture not found in parser:", filenameWithoutExt, this.parser.json);
250
+ return;
251
+ }
252
+ this.parser.getDependency("texture", index).then(tex => {
253
+ if (debug) console.log("[MaterialX] Texture loaded:", tex);
254
+ console.log(tex, " -> ", defaultTexture)
255
+
256
+ // update the checkerboard texture with the loaded texture
257
+ defaultTexture.image = tex.image;
258
+ defaultTexture.needsUpdate = true;
259
+ defaultTexture.userData = tex.userData || {}; // needed for LODs
260
+ defaultTexture.format = tex.format;
261
+ defaultTexture.source = tex.source || defaultTexture.source;
262
+ defaultTexture.type = tex.type;
263
+ defaultTexture.format = tex.format;
264
+ defaultTexture.magFilter = tex.magFilter;
265
+ defaultTexture.minFilter = tex.minFilter;
266
+ defaultTexture.wrapS = tex.wrapS;
267
+ defaultTexture.wrapT = tex.wrapT;
268
+ defaultTexture.generateMipmaps = tex.generateMipmaps;
269
+ defaultTexture.mipmaps = tex.mipmaps || defaultTexture.mipmaps;
270
+ defaultTexture.unpackAlignment = tex.unpackAlignment;
271
+
272
+ if (tex instanceof CompressedTexture) {
273
+ defaultTexture.isCompressedTexture = true;
274
+ }
275
+ });
276
+ });
277
+ return defaultTexture;
278
+ }
479
279
  }
480
-
481
- // Mark uniforms as needing update
482
- shaderMaterial.uniformsNeedUpdate = true;
483
- };
484
-
485
- shaderMaterial.name = `MaterialX_Generated_${materialXData.name}`;
486
-
487
- // Add debugging to see if the material compiles correctly
488
- if (debug) console.log("[MaterialX] material created successfully:", shaderMaterial.name);
489
- // if (debug) console.log("Material uniforms keys:", Object.keys(shaderMaterial.uniforms || {}));
490
- // if (debug) console.log("Material transparent:", shaderMaterial.transparent);
491
- // if (debug) console.log("Material side:", shaderMaterial.side);
492
-
280
+ });
493
281
  // Track this material for later lighting updates
494
- this.generatedMaterials.push(shaderMaterial);
282
+ this._generatedMaterials.push(shaderMaterial);
495
283
 
284
+ // Add debugging to see if the material compiles correctly
285
+ if (debug) console.log("[MaterialX] material created:", shaderMaterial.name);
496
286
  return shaderMaterial;
497
287
 
498
288
  } catch (error) {
@@ -500,69 +290,9 @@ export class MaterialXLoader implements GLTFLoaderPlugin {
500
290
  console.error("[MaterialX] Error creating MaterialX material:", error);
501
291
  // Return a fallback material with stored MaterialX data
502
292
  const fallbackMaterial = new MeshStandardMaterial();
503
- fallbackMaterial.userData.materialX = materialXData;
504
- fallbackMaterial.name = `MaterialX_Error_${materialXData.name}`;
293
+ fallbackMaterial.userData.materialX = material_extension;
294
+ fallbackMaterial.name = `MaterialX_Error_${material_extension.name}`;
505
295
  return fallbackMaterial;
506
296
  }
507
297
  }
508
-
509
- private identityMatrix: Matrix4 = new Matrix4().identity();
510
- // Update lighting uniforms for all generated MaterialX materials
511
- updateLightingFromEnvironment(environment: MaterialXEnvironment): void {
512
-
513
- // Get lighting data from environment
514
- const lightData = environment.getLightData() || null;
515
- const lightCount = environment.getLightCount() || 0;
516
- const radianceTexture = environment.getRadianceTexture() || null;
517
- const irradianceTexture = environment.getIrradianceTexture() || null;
518
-
519
- if (debug) console.log(`[MaterialX] Updating lighting for ${this.generatedMaterials.length} MaterialX materials`, {
520
- lightData, radianceTexture, irradianceTexture,
521
- });
522
-
523
- // Update each generated material's lighting uniforms
524
- this.generatedMaterials.forEach((material, _index) => {
525
- if (!material.uniforms) return;
526
-
527
- // Update light count
528
- if (material.uniforms.u_numActiveLightSources && lightCount >= 0) {
529
- material.uniforms.u_numActiveLightSources.value = lightCount;
530
- }
531
-
532
- // Update light data if we have lights
533
- if (lightData) {
534
- if (!material.uniforms.u_lightData) {
535
- material.uniforms.u_lightData = { value: null };
536
- }
537
- material.uniforms.u_lightData.value = lightData;
538
- if (debug) console.log("[MaterialX] Updated light data for material", material.name, lightData, material.uniforms, );
539
- }
540
-
541
- // Update environment uniforms
542
- if (material.uniforms.u_envMatrix) {
543
- material.uniforms.u_envMatrix.value = this.identityMatrix;
544
- }
545
- if (material.uniforms.u_envRadiance) {
546
- material.uniforms.u_envRadiance.value = radianceTexture;
547
- }
548
- if (material.uniforms.u_envRadianceMips) {
549
- material.uniforms.u_envRadianceMips.value = Math.trunc(Math.log2(Math.max(radianceTexture?.source.data.width ?? 0, radianceTexture?.source.data.height ?? 0))) + 1;
550
- }
551
- if (material.uniforms.u_envIrradiance) {
552
- material.uniforms.u_envIrradiance.value = irradianceTexture;
553
- }
554
-
555
- // Mark uniforms as needing update
556
- // console.log("Light data in uniforms", material.uniforms, material.fragmentShader);
557
- material.uniformsNeedUpdate = true;
558
- });
559
-
560
- /* TODO not working yet
561
- this.generatedMaterials.forEach((material, index) => {
562
- console.log("Regenerating shaders for MaterialX material:", index, material.name);
563
- material.userData.regenerateShaders();
564
- resetShaders(this.context);
565
- });
566
- */
567
- }
568
298
  }