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

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,9 +2,7 @@
2
2
  import { TypeStore } from "@needle-tools/engine"
3
3
 
4
4
  // Import types
5
- import { MaterialXMaterial } from "../src/materialx.material.js";
6
5
  import { MaterialXUniformUpdate } from "../src/loader/loader.needle.js";
7
6
 
8
7
  // Register types
9
- TypeStore.add("MaterialXMaterial", MaterialXMaterial);
10
8
  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.b9467c8",
3
+ "version": "1.0.1-next.c1bbe8d",
4
4
  "type": "module",
5
5
  "main": "index.ts",
6
6
  "exports": {
@@ -5,8 +5,8 @@
5
5
 
6
6
  import { getParam, getWorldDirection } from '@needle-tools/engine';
7
7
  import * as THREE from 'three';
8
- import { debug, debugUpdate } from './utils';
9
- import { MaterialX } from './materialx.types';
8
+
9
+ const debug = getParam("debugmaterialx");
10
10
 
11
11
  const IMAGE_PROPERTY_SEPARATOR = "_";
12
12
  const UADDRESS_MODE_SUFFIX = IMAGE_PROPERTY_SEPARATOR + "uaddressmode";
@@ -20,7 +20,8 @@ const IMAGE_PATH_SEPARATOR = "/";
20
20
  * @param {Object} capabilities
21
21
  * @returns {THREE.Texture}
22
22
  */
23
- export function prepareEnvTexture(texture, capabilities) {
23
+ export function prepareEnvTexture(texture, capabilities)
24
+ {
24
25
  let newTexture = new THREE.DataTexture(texture.image.data, texture.image.width, texture.image.height, texture.format, texture.type);
25
26
  newTexture.wrapS = THREE.RepeatWrapping;
26
27
  newTexture.anisotropy = capabilities.getMaxAnisotropy();
@@ -38,12 +39,15 @@ export function prepareEnvTexture(texture, capabilities) {
38
39
  * @param {any} dimension
39
40
  * @returns {THREE.Uniform}
40
41
  */
41
- function fromVector(value, dimension) {
42
+ function fromVector(value, dimension)
43
+ {
42
44
  let outValue;
43
- if (value) {
45
+ if (value)
46
+ {
44
47
  outValue = [...value.data()];
45
48
  }
46
- else {
49
+ else
50
+ {
47
51
  outValue = [];
48
52
  for (let i = 0; i < dimension; ++i)
49
53
  outValue.push(0.0);
@@ -54,16 +58,23 @@ function fromVector(value, dimension) {
54
58
 
55
59
  /**
56
60
  * Get Three uniform from MaterialX matrix
61
+ * @param {mx.matrix} matrix
62
+ * @param {mx.matrix.size} dimension
57
63
  */
58
- function fromMatrix(matrix: MaterialX.Matrix, dimension: MaterialX.Matrix["size"]) {
59
- const vec = new Array(dimension);
60
- if (matrix) {
61
- for (let i = 0; i < matrix.numRows(); ++i) {
62
- for (let k = 0; k < matrix.numColumns(); ++k) {
64
+ function fromMatrix(matrix, dimension)
65
+ {
66
+ let vec = [];
67
+ if (matrix)
68
+ {
69
+ for (let i = 0; i < matrix.numRows(); ++i)
70
+ {
71
+ for (let k = 0; k < matrix.numColumns(); ++k)
72
+ {
63
73
  vec.push(matrix.getItem(i, k));
64
74
  }
65
75
  }
66
- } else {
76
+ } else
77
+ {
67
78
  for (let i = 0; i < dimension; ++i)
68
79
  vec.push(0.0);
69
80
  }
@@ -71,99 +82,125 @@ function fromMatrix(matrix: MaterialX.Matrix, dimension: MaterialX.Matrix["size"
71
82
  return vec;
72
83
  }
73
84
 
74
-
75
- export type Loaders = {
76
- getTexture: (path: string) => THREE.Texture;
77
- }
78
-
79
85
  /**
80
86
  * Get Three uniform from MaterialX value
81
87
  * @param {mx.Uniform.type} type
82
88
  * @param {mx.Uniform.value} value
83
89
  * @param {mx.Uniform.name} name
84
90
  * @param {mx.Uniforms} uniforms
85
- * @param {Loaders} loaders
91
+ * @param {THREE.TextureLoader} textureLoader
86
92
  * @param {string} searchPath
87
93
  * @param {boolean} flipY
88
94
  */
89
- function toThreeUniform(type: string, value: any, name: string, uniforms: any, loaders: Loaders, searchPath, flipY: boolean) {
90
-
91
- switch (type) {
95
+ function toThreeUniform(type, value, name, uniforms, textureLoader, searchPath, flipY)
96
+ {
97
+ let outValue = null;
98
+ switch (type)
99
+ {
92
100
  case 'float':
93
101
  case 'integer':
94
102
  case 'boolean':
95
- return value;
103
+ outValue = value;
96
104
  break;
97
105
  case 'vector2':
98
- return fromVector(value, 2);
106
+ outValue = fromVector(value, 2);
99
107
  break;
100
108
  case 'vector3':
101
109
  case 'color3':
102
- return fromVector(value, 3);
110
+ outValue = fromVector(value, 3);
111
+ break;
103
112
  case 'vector4':
104
113
  case 'color4':
105
- return fromVector(value, 4);
114
+ outValue = fromVector(value, 4);
115
+ break;
106
116
  case 'matrix33':
107
- return fromMatrix(value, 9);
117
+ outValue = fromMatrix(value, 9);
118
+ break;
108
119
  case 'matrix44':
109
- return fromMatrix(value, 16);
120
+ outValue = fromMatrix(value, 16);
121
+ break;
110
122
  case 'filename':
111
- if (value) {
123
+ if (value)
124
+ {
112
125
  // Cache / reuse texture to avoid reload overhead.
113
126
  // Note: that data blobs and embedded data textures are not cached as they are transient data.
114
127
  let checkCache = false;
115
128
  let texturePath = searchPath + IMAGE_PATH_SEPARATOR + value;
116
- if (value.startsWith('blob:')) {
129
+ if (value.startsWith('blob:'))
130
+ {
117
131
  texturePath = value;
118
132
  if (debug) console.log('Load blob URL:', texturePath);
119
133
  checkCache = false;
120
134
  }
121
- else if (value.startsWith('http')) {
135
+ else if (value.startsWith('http'))
136
+ {
122
137
  texturePath = value;
123
138
  if (debug) console.log('Load HTTP URL:', texturePath);
124
139
  }
125
- else if (value.startsWith('data:')) {
140
+ else if (value.startsWith('data:'))
141
+ {
126
142
  texturePath = value;
127
143
  checkCache = false;
128
144
  if (debug) console.log('Load data URL:', texturePath);
129
145
  }
130
146
  const cachedTexture = checkCache && THREE.Cache.get(texturePath);
131
- if (cachedTexture) {
132
- if (debug) console.log('Use cached texture: ', texturePath, cachedTexture);
133
- return cachedTexture;
147
+ if (cachedTexture)
148
+ {
149
+ // Get texture from cache
150
+ outValue = cachedTexture;
151
+ if (debug) console.log('Use cached texture: ', texturePath, outValue);
134
152
  }
135
- else {
136
- const texture = loaders.getTexture(texturePath);
137
- if (checkCache) THREE.Cache.add(texturePath, texture);
153
+ else
154
+ {
155
+ outValue = textureLoader.load(
156
+ texturePath,
157
+ function (texture) {
158
+ if (debug) console.log('Load new texture: ' + texturePath, texture);
159
+ outValue = texture;
160
+
161
+ // Add texture to ThreeJS cache
162
+ if (checkCache)
163
+ THREE.Cache.add(texturePath, texture);
164
+ },
165
+ undefined,
166
+ function (error) {
167
+ console.error('Error loading texture: ', error);
168
+ });
169
+
138
170
  // Set address & filtering mode
139
- if (texture) setTextureParameters(texture, name, uniforms, flipY);
140
- return texture;
171
+ if (outValue)
172
+ setTextureParameters(outValue, name, uniforms, flipY);
141
173
  }
142
174
  }
143
175
  break;
144
176
  case 'samplerCube':
145
177
  case 'string':
146
- return null;
178
+ break;
147
179
  default:
148
180
  const key = type + ':' + name;
149
- if (!valueTypeWarningMap.has(key)) {
181
+ if (!valueTypeWarningMap.has(key))
182
+ {
150
183
  valueTypeWarningMap.set(key, true);
151
184
  console.warn('MaterialX: Unsupported uniform type: ' + type + ' for uniform: ' + name, value);
152
185
  }
153
- return null;
186
+ outValue = null;
154
187
  }
188
+
189
+ return outValue;
155
190
  }
156
191
 
157
- const valueTypeWarningMap = new Map<string, boolean>();
192
+ const valueTypeWarningMap = new Map();
158
193
 
159
194
  /**
160
195
  * Get Three wrapping mode
161
196
  * @param {mx.TextureFilter.wrap} mode
162
197
  * @returns {THREE.Wrapping}
163
198
  */
164
- function getWrapping(mode) {
199
+ function getWrapping(mode)
200
+ {
165
201
  let wrap;
166
- switch (mode) {
202
+ switch (mode)
203
+ {
167
204
  case 1:
168
205
  wrap = THREE.ClampToEdgeWrapping;
169
206
  break;
@@ -185,9 +222,12 @@ function getWrapping(mode) {
185
222
  * @param {mx.TextureFilter.minFilter} type
186
223
  * @param {mx.TextureFilter.generateMipmaps} generateMipmaps
187
224
  */
188
- function getMinFilter(type, generateMipmaps) {
189
- let filterType: THREE.TextureFilter = generateMipmaps ? THREE.LinearMipMapLinearFilter : THREE.LinearFilter;
190
- if (type === 0) {
225
+ function getMinFilter(type, generateMipmaps)
226
+ {
227
+ /** @type {THREE.TextureFilter} */
228
+ let filterType = generateMipmaps ? THREE.LinearMipMapLinearFilter : THREE.LinearFilter;
229
+ if (type === 0)
230
+ {
191
231
  filterType = generateMipmaps ? THREE.NearestMipMapNearestFilter : THREE.NearestFilter;
192
232
  }
193
233
  return filterType;
@@ -200,22 +240,25 @@ function getMinFilter(type, generateMipmaps) {
200
240
  * @param {mx.Uniforms} uniforms
201
241
  * @param {mx.TextureFilter.generateMipmaps} generateMipmaps
202
242
  */
203
- function setTextureParameters(texture, name, uniforms, flipY = true, generateMipmaps = true) {
243
+ function setTextureParameters(texture, name, uniforms, flipY = true, generateMipmaps = true)
244
+ {
204
245
  const idx = name.lastIndexOf(IMAGE_PROPERTY_SEPARATOR);
205
246
  const base = name.substring(0, idx) || name;
206
247
 
207
- // texture.generateMipmaps = generateMipmaps;
208
- // texture.wrapS = THREE.RepeatWrapping;
209
- // texture.wrapT = THREE.RepeatWrapping;
210
- // texture.magFilter = THREE.LinearFilter;
211
- // texture.flipY = flipY;
248
+ texture.generateMipmaps = generateMipmaps;
249
+ texture.wrapS = THREE.RepeatWrapping;
250
+ texture.wrapT = THREE.RepeatWrapping;
251
+ texture.magFilter = THREE.LinearFilter;
252
+ texture.flipY = flipY;
212
253
 
213
- if (uniforms.find(base + UADDRESS_MODE_SUFFIX)) {
254
+ if (uniforms.find(base + UADDRESS_MODE_SUFFIX))
255
+ {
214
256
  const uaddressmode = uniforms.find(base + UADDRESS_MODE_SUFFIX).getValue().getData();
215
257
  texture.wrapS = getWrapping(uaddressmode);
216
258
  }
217
259
 
218
- if (uniforms.find(base + VADDRESS_MODE_SUFFIX)) {
260
+ if (uniforms.find(base + VADDRESS_MODE_SUFFIX))
261
+ {
219
262
  const vaddressmode = uniforms.find(base + VADDRESS_MODE_SUFFIX).getValue().getData();
220
263
  texture.wrapT = getWrapping(vaddressmode);
221
264
  }
@@ -227,7 +270,8 @@ function setTextureParameters(texture, name, uniforms, flipY = true, generateMip
227
270
  /**
228
271
  * Return the global light rotation matrix
229
272
  */
230
- export function getLightRotation() {
273
+ export function getLightRotation()
274
+ {
231
275
  return new THREE.Matrix4().makeRotationY(Math.PI / 2);
232
276
  }
233
277
 
@@ -236,9 +280,11 @@ export function getLightRotation() {
236
280
  * @param {mx.Document} doc
237
281
  * @returns {Array.<mx.Node>}
238
282
  */
239
- export function findLights(doc: MaterialX.Document) {
240
- let lights = new Array<any>;
241
- for (let node of doc.getNodes()) {
283
+ export function findLights(doc)
284
+ {
285
+ let lights = [];
286
+ for (let node of doc.getNodes())
287
+ {
242
288
  if (node.getType() === "lightshader")
243
289
  lights.push(node);
244
290
  }
@@ -249,14 +295,19 @@ let lightTypesBound = {};
249
295
 
250
296
  /**
251
297
  * Register lights in shader generation context
252
- * @param {MaterialX.MODULE} mx MaterialX Module
298
+ * @param {Object} mx MaterialX Module
253
299
  * @param {mx.GenContext} genContext Shader generation context
300
+ * @returns {Array.<mx.Node>}
254
301
  */
255
- export async function registerLights(mx: MaterialX.MODULE, genContext: any): Promise<void> {
302
+ export async function registerLights(mx, genContext)
303
+ {
256
304
  lightTypesBound = {};
257
305
  const maxLightCount = genContext.getOptions().hwMaxActiveLightSources;
306
+
258
307
  mx.HwShaderGenerator.unbindLightShaders(genContext);
308
+
259
309
  let lightId = 1;
310
+
260
311
  // All light types so that we have NodeDefs for them
261
312
  const defaultLightRigXml = `<?xml version="1.0"?>
262
313
  <materialx version="1.39">
@@ -274,27 +325,30 @@ export async function registerLights(mx: MaterialX.MODULE, genContext: any): Pro
274
325
 
275
326
  // Load default light rig XML to ensure we have all light types available
276
327
  const lightRigDoc = mx.createDocument();
277
- await mx.readFromXmlString(lightRigDoc, defaultLightRigXml, "");
328
+ await mx.readFromXmlString(lightRigDoc, defaultLightRigXml);
278
329
  const document = mx.createDocument();
279
330
  const stdlib = mx.loadStandardLibraries(genContext);
280
331
  document.setDataLibrary(stdlib);
281
332
  document.importLibrary(lightRigDoc);
282
333
  const defaultLights = findLights(document);
283
- if (debug) console.log("Default lights in MaterialX document", defaultLights);
334
+ // if (debug)
335
+ console.log("Default lights in MaterialX document", defaultLights);
284
336
 
285
337
  // Loading a document seems to reset this option for some reason, so we set it again
286
338
  genContext.getOptions().hwMaxActiveLightSources = maxLightCount;
287
339
 
288
340
  // Register types only – we get these from the default light rig XML above
289
341
  // This is needed to ensure that the light shaders are bound for each light type
290
- for (let light of defaultLights) {
342
+ for (let light of defaultLights)
343
+ {
291
344
  const lightDef = light.getNodeDef();
292
345
  if (debug) console.log("Default light node definition", lightDef);
293
346
  if (!lightDef) continue;
294
347
 
295
348
  const lightName = lightDef.getName();
296
349
  if (debug) console.log("Registering default light", { lightName, lightDef });
297
- if (!lightTypesBound[lightName]) {
350
+ if (!lightTypesBound[lightName])
351
+ {
298
352
  // TODO check if we need to bind light shader for each three.js light instead of once per type
299
353
  if (debug) console.log("Bind light shader for node", { lightName, lightId, lightDef });
300
354
  lightTypesBound[lightName] = lightId;
@@ -320,22 +374,16 @@ function threeLightTypeToMaterialXNodeName(threeLightType) {
320
374
  }
321
375
  };
322
376
 
323
- type LightData = {
324
- type: number, // Light type ID
325
- position: THREE.Vector3, // Position in world space
326
- direction: THREE.Vector3, // Direction in world space
327
- color: THREE.Color, // Color of the light
328
- intensity: number, // Intensity of the light
329
- decay_rate: number, // Decay rate for point and spot lights
330
- inner_angle: number, // Inner angle for spot lights
331
- outer_angle: number, // Outer angle for spot lights
332
- }
333
-
334
377
  /**
335
378
  * Update light data for shader uniforms
379
+ * @param {Object} mx MaterialX Module
380
+ * @param {Array.<mx.Node>} lights Light nodes
381
+ * @param {mx.GenContext} genContext Shader generation context
382
+ * @returns {{ lightData: Array<any>, lightCount: number }}
336
383
  */
337
- export function getLightData(lights: any, genContext: any): { lightData: LightData[], lightCount: number } {
338
- const lightData = new Array();
384
+ export function getLightData(mx, lights, genContext)
385
+ {
386
+ const lightData = [];
339
387
  const maxLightCount = genContext.getOptions().hwMaxActiveLightSources;
340
388
 
341
389
  // Three.js lights
@@ -351,18 +399,9 @@ export function getLightData(lights: any, genContext: any): { lightData: LightDa
351
399
  console.error("MaterialX: Light type not registered in context. Make sure to register light types before using them.", lightDefinitionName);
352
400
 
353
401
  const wp = light.getWorldPosition(new THREE.Vector3());
354
- const wd = getWorldDirection(light, new THREE.Vector3(0, 0, -1));
355
-
356
- // Shader math from the generated MaterialX shader:
357
- // float low = min(light.inner_angle, light.outer_angle);
358
- // float high = light.inner_angle;
359
- // float cosDir = dot(result.direction, -light.direction);
360
- // float spotAttenuation = smoothstep(low, high, cosDir);
402
+ const wd = getWorldDirection(light, new THREE.Vector3(0,0,-1));
361
403
 
362
- const outerAngleRad = light.angle;
363
- const innerAngleRad = outerAngleRad * (1 - light.penumbra);
364
- const inner_angle = Math.cos(innerAngleRad);
365
- const outer_angle = Math.cos(outerAngleRad);
404
+ // console.log("Registering light", light.penumbra);
366
405
 
367
406
  lightData.push({
368
407
  type: lightTypesBound[lightDefinitionName],
@@ -371,11 +410,11 @@ export function getLightData(lights: any, genContext: any): { lightData: LightDa
371
410
  color: new THREE.Color().fromArray(light.color.toArray()),
372
411
  // Luminous efficacy for converting radiant power in watts (W) to luminous flux in lumens (lm) at a wavelength of 555 nm.
373
412
  // Also, three.js lights don't have PI scale baked in, but MaterialX does, so we need to divide by PI for point and spot lights.
374
- intensity: light.intensity * (light.isPointLight ? 683.0 / 3.1415 : light.isSpotLight ? 683.0 / 3.1415 : 1.0),
413
+ intensity: light.intensity * (light.isPointLight ? 683.0 / 3.1415 : light.isSpotLight ? 683.0 / 3.1415: 1.0),
375
414
  decay_rate: 2.0,
376
415
  // Approximations for testing – the relevant light has 61.57986...129.4445 as inner/outer spot angle
377
- inner_angle: inner_angle,
378
- outer_angle: outer_angle,
416
+ inner_angle: 0.9,
417
+ outer_angle: 0.4,
379
418
  });
380
419
  }
381
420
 
@@ -383,7 +422,8 @@ export function getLightData(lights: any, genContext: any): { lightData: LightDa
383
422
  const lightCount = lightData.length;
384
423
 
385
424
  // If we don't have enough entries in lightData, fill with empty lights
386
- while (lightData.length < maxLightCount) {
425
+ while (lightData.length < maxLightCount)
426
+ {
387
427
  const emptyLight = {
388
428
  type: 0, // Default light type
389
429
  position: new THREE.Vector3(0, 0, 0),
@@ -397,33 +437,38 @@ export function getLightData(lights: any, genContext: any): { lightData: LightDa
397
437
  lightData.push(emptyLight);
398
438
  }
399
439
 
400
- if (debugUpdate) console.log("Registered lights in MaterialX context", lightTypesBound, lightData);
440
+ if (debug)
441
+ console.log("Registered lights in MaterialX context", lightTypesBound, lightData);
401
442
 
402
443
  return { lightData, lightCount };
403
444
  }
404
445
 
405
446
  /**
406
447
  * Get uniform values for a shader
448
+ * @param {mx.shaderStage} shaderStage
449
+ * @param {THREE.TextureLoader} textureLoader
407
450
  */
408
- export function getUniformValues(shaderStage: MaterialX.ShaderStage, loaders: Loaders, searchPath: string, flipY: boolean) {
451
+ export function getUniformValues(shaderStage, textureLoader, searchPath, flipY)
452
+ {
409
453
  const threeUniforms = {};
410
454
 
411
- const uniformBlocks = shaderStage.getUniformBlocks()
412
- for (const [blockName, uniforms] of Object.entries(uniformBlocks)) {
413
- // Seems struct uniforms (like in LightData) end up here as well, we should filter those out.
414
- if (blockName === "LightData") continue;
415
-
416
- if (!uniforms.empty()) {
417
- for (let i = 0; i < uniforms.size(); ++i) {
455
+ const uniformBlocks = Object.values(shaderStage.getUniformBlocks());
456
+ uniformBlocks.forEach(uniforms =>
457
+ {
458
+ // TODO Seems struct uniforms (like in LightData) end up here as well, we should filter those out.
459
+ if (!uniforms.empty())
460
+ {
461
+ for (let i = 0; i < uniforms.size(); ++i)
462
+ {
418
463
  const variable = uniforms.get(i);
419
464
  const value = variable.getValue()?.getData();
420
465
  const name = variable.getVariable();
421
- if (debug) console.log("Adding uniform", { path: variable.getPath(), name, value, type: variable.getType().getName() });
466
+ if (debug) console.log("Adding uniform", { name, value, type: variable.getType().getName() });
422
467
  threeUniforms[name] = new THREE.Uniform(toThreeUniform(variable.getType().getName(), value, name, uniforms,
423
- loaders, searchPath, flipY));
468
+ textureLoader, searchPath, flipY));
424
469
  }
425
470
  }
426
- }
471
+ });
427
472
 
428
473
  return threeUniforms;
429
474
  }
@@ -6,39 +6,40 @@ 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 { MaterialXEnvironment, state } from "../materialx.js";
10
- import { MaterialXMaterial } from "../materialx.material.js";
9
+ import { state } from "../materialx.js";
11
10
 
12
11
  //@dont-generate-component
13
12
  export class MaterialXUniformUpdate extends Component {
14
13
 
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
+
15
26
  onEnable(): void {
16
- this.context.addBeforeRenderListener(this.gameObject, this.onBeforeRenderThree);
27
+ this.context.addBeforeRenderListener(this.gameObject, this._onBeforeRender);
17
28
  }
18
29
 
19
30
  onDisable(): void {
20
- this.context.removeBeforeRenderListener(this.gameObject, this.onBeforeRenderThree);
31
+ this.context.removeBeforeRenderListener(this.gameObject, this._onBeforeRender);
21
32
  }
22
33
 
23
- onBeforeRenderThree = () => {
34
+ _onBeforeRender = () => {
24
35
  // Update uniforms or perform any pre-render logic here
25
36
  const gameObject = this.gameObject as any as Mesh;
26
37
  const material = gameObject?.material;
27
38
 
28
39
  const camera = this.context.mainCamera;
29
40
  if (!camera) return;
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
- }
41
+
42
+ MaterialXUniformUpdate.updateMaterial(material, gameObject, camera);
42
43
  }
43
44
  }
44
45
 
@@ -58,13 +59,9 @@ export class MaterialXLoaderPlugin implements INeedleGLTFExtensionPlugin {
58
59
  });
59
60
  };
60
61
 
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 });
62
+ onLoaded = (url: string, gltf: GLTF, context: Context) => {
63
+ if (debug) console.log("[MaterialX] MaterialXLoaderPlugin: glTF loaded", url, gltf.scene);
63
64
 
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
- }
68
65
  // Set up onBeforeRender callbacks for objects with MaterialX materials
69
66
  // This ensures uniforms are updated properly during rendering
70
67
  gltf.scene.traverse((child) => {
@@ -72,7 +69,7 @@ export class MaterialXLoaderPlugin implements INeedleGLTFExtensionPlugin {
72
69
  const mesh = child as Mesh;
73
70
  const material = mesh.material as Material;
74
71
 
75
- if (material instanceof MaterialXMaterial) {
72
+ if (material?.userData?.updateUniforms) {
76
73
  if (debug) console.log("[MaterialX] Adding MaterialX uniform update component to:", child.name);
77
74
  child.addComponent(MaterialXUniformUpdate);
78
75
  }
@@ -80,6 +77,13 @@ export class MaterialXLoaderPlugin implements INeedleGLTFExtensionPlugin {
80
77
  });
81
78
 
82
79
  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
+ });
83
87
  };
84
88
 
85
89
  onExport = (_exporter: GLTFExporter, _context: Context) => {