@needle-tools/materialx 1.0.1-next.c1bbe8d → 1.0.1-next.c315a2f

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