@needle-tools/materialx 1.1.1-next.623fc20 → 1.1.1-next.8e8afe1

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.
@@ -4,7 +4,6 @@
4
4
  //
5
5
  import * as THREE from 'three';
6
6
  import { debug, debugUpdate } from './utils.js';
7
- import { MaterialX } from './materialx.types.js';
8
7
 
9
8
  const IMAGE_PROPERTY_SEPARATOR = "_";
10
9
  const UADDRESS_MODE_SUFFIX = IMAGE_PROPERTY_SEPARATOR + "uaddressmode";
@@ -13,13 +12,13 @@ const FILTER_TYPE_SUFFIX = IMAGE_PROPERTY_SEPARATOR + "filtertype";
13
12
  const IMAGE_PATH_SEPARATOR = "/";
14
13
 
15
14
  /**
16
- * Initialized the environment texture as MaterialX expects it
15
+ * Initializes the environment texture as MaterialX expects it
17
16
  * @param {THREE.Texture} texture
18
17
  * @param {Object} capabilities
19
18
  * @returns {THREE.Texture}
20
19
  */
21
20
  export function prepareEnvTexture(texture, capabilities) {
22
- let newTexture = new THREE.DataTexture(texture.image.data, texture.image.width, texture.image.height, texture.format, texture.type);
21
+ let newTexture = new THREE.DataTexture(texture.image.data, texture.image.width, texture.image.height, /** @type {any} */ (texture.format), texture.type);
23
22
  newTexture.wrapS = THREE.RepeatWrapping;
24
23
  newTexture.anisotropy = capabilities.getMaxAnisotropy();
25
24
  newTexture.minFilter = THREE.LinearMipmapLinearFilter;
@@ -34,7 +33,7 @@ export function prepareEnvTexture(texture, capabilities) {
34
33
  * Get Three uniform from MaterialX vector
35
34
  * @param {any} value
36
35
  * @param {any} dimension
37
- * @returns {THREE.Uniform}
36
+ * @returns {Array<number>}
38
37
  */
39
38
  function fromVector(value, dimension) {
40
39
  let outValue;
@@ -52,8 +51,11 @@ function fromVector(value, dimension) {
52
51
 
53
52
  /**
54
53
  * Get Three uniform from MaterialX matrix
54
+ * @param {any} matrix
55
+ * @param {number} dimension
56
+ * @returns {Array<number>}
55
57
  */
56
- function fromMatrix(matrix: MaterialX.Matrix, dimension: MaterialX.Matrix["size"]) {
58
+ function fromMatrix(matrix, dimension) {
57
59
  const vec = new Array(dimension);
58
60
  if (matrix) {
59
61
  for (let i = 0; i < matrix.numRows(); ++i) {
@@ -69,19 +71,11 @@ function fromMatrix(matrix: MaterialX.Matrix, dimension: MaterialX.Matrix["size"
69
71
  return vec;
70
72
  }
71
73
 
72
-
73
- export type Loaders = {
74
- /**
75
- * Cache key for the loaders, used to identify and reuse textures
76
- */
77
- readonly cacheKey: string;
78
- /**
79
- * Get a texture by path
80
- * @param {string} path - The path to the texture
81
- * @return {Promise<THREE.Texture>} - A promise that resolves to the texture
82
- */
83
- readonly getTexture: (path: string) => Promise<THREE.Texture>;
84
- }
74
+ /**
75
+ * @typedef {Object} Callbacks
76
+ * @property {string} [cacheKey] - Cache key for the loaders, used to identify and reuse textures
77
+ * @property {(path: string) => Promise<THREE.Texture | null | void>} getTexture - Get a texture by path
78
+ */
85
79
 
86
80
  const defaultTexture = new THREE.Texture();
87
81
  defaultTexture.needsUpdate = true;
@@ -94,21 +88,28 @@ defaultTexture.image.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAA
94
88
  // defaultTexture.magFilter = THREE.NearestFilter;
95
89
  // defaultTexture.repeat = new THREE.Vector2(100, 100);
96
90
 
97
-
98
91
  const defaultNormalTexture = new THREE.Texture();
99
92
  defaultNormalTexture.needsUpdate = true;
100
93
  defaultNormalTexture.image = new Image();
101
94
  defaultNormalTexture.image.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIBAMAAAA2IaO4AAAAAXNSR0IB2cksfwAAAAlwSFlzAAALEwAACxMBAJqcGAAAABJQTFRFgYH4gIH4gYH3gIH3gIH5gID4m94ORAAAADFJREFUeJxjZBBkfMdo9P/BB0aBj/8FGB0ufghgFGT4r8wo+P8rD2Pgo3sMjIz8jAwAMLoN0ZjS5hgAAAAASUVORK5CYII=";
102
95
 
103
-
104
- function tryGetFromCache(key: string): any {
96
+ /**
97
+ * @param {string} key
98
+ * @returns {any}
99
+ */
100
+ function tryGetFromCache(key) {
105
101
  const wasEnabled = THREE.Cache.enabled;
106
102
  THREE.Cache.enabled = true;
107
103
  const value = THREE.Cache.get(key);
108
104
  THREE.Cache.enabled = wasEnabled;
109
105
  return value;
110
106
  }
111
- function addToCache(key: string, value: any): void {
107
+
108
+ /**
109
+ * @param {string} key
110
+ * @param {any} value
111
+ */
112
+ function addToCache(key, value) {
112
113
  const wasEnabled = THREE.Cache.enabled;
113
114
  THREE.Cache.enabled = true;
114
115
  THREE.Cache.add(key, value);
@@ -118,10 +119,17 @@ function addToCache(key: string, value: any): void {
118
119
 
119
120
  /**
120
121
  * Get Three uniform from MaterialX value
122
+ * @param {any} uniforms
123
+ * @param {string} type
124
+ * @param {any} value
125
+ * @param {string} name
126
+ * @param {Callbacks} loaders
127
+ * @param {string} searchPath
128
+ * @returns {THREE.Uniform}
121
129
  */
122
- function toThreeUniform(uniforms: any, type: string, value: any, name: string, loaders: Loaders, searchPath: string): THREE.Uniform {
130
+ function toThreeUniform(uniforms, type, value, name, loaders, searchPath) {
123
131
 
124
- const uniform = new THREE.Uniform<any>(null);
132
+ const uniform = new THREE.Uniform(/** @type {any} */ (null));
125
133
 
126
134
  switch (type) {
127
135
  case 'float':
@@ -130,21 +138,21 @@ function toThreeUniform(uniforms: any, type: string, value: any, name: string, l
130
138
  uniform.value = value;
131
139
  break;
132
140
  case 'vector2':
133
- uniform.value = fromVector(value, 2);
141
+ uniform.value = /** @type {any} */ (fromVector(value, 2));
134
142
  break;
135
143
  case 'vector3':
136
144
  case 'color3':
137
- uniform.value = fromVector(value, 3);
145
+ uniform.value = /** @type {any} */ (fromVector(value, 3));
138
146
  break;
139
147
  case 'vector4':
140
148
  case 'color4':
141
- uniform.value = fromVector(value, 4);
149
+ uniform.value = /** @type {any} */ (fromVector(value, 4));
142
150
  break;
143
151
  case 'matrix33':
144
- uniform.value = fromMatrix(value, 9);
152
+ uniform.value = /** @type {any} */ (fromMatrix(value, 9));
145
153
  break;
146
154
  case 'matrix44':
147
- uniform.value = fromMatrix(value, 16);
155
+ uniform.value = /** @type {any} */ (fromMatrix(value, 16));
148
156
  break;
149
157
  case 'filename':
150
158
  if (value) {
@@ -165,7 +173,7 @@ function toThreeUniform(uniforms: any, type: string, value: any, name: string, l
165
173
  checkCache = true;
166
174
  }
167
175
 
168
- const cacheKey = `${loaders.cacheKey}-${texturePath}`;
176
+ const cacheKey = loaders.cacheKey?.length ? `${loaders.cacheKey}-${texturePath}` : texturePath;
169
177
  const cacheValue = checkCache ? tryGetFromCache(cacheKey) : null;
170
178
  if (cacheValue) {
171
179
  if (debug) console.log('[MaterialX] Use cached texture: ', cacheKey, cacheValue);
@@ -180,28 +188,30 @@ function toThreeUniform(uniforms: any, type: string, value: any, name: string, l
180
188
  }
181
189
  }
182
190
  else {
183
- if (name.toLowerCase().includes("normal")) {
184
- uniform.value = defaultNormalTexture;
185
- }
186
- else {
187
- uniform.value = defaultTexture;
188
- }
189
-
190
191
  if (debug) console.log('[MaterialX] Load texture:', texturePath);
192
+
193
+ if (name.toLowerCase().includes("normal")) uniform.value = /** @type {any} */ (defaultNormalTexture);
194
+ else uniform.value = /** @type {any} */ (defaultTexture);
195
+ const defaultValue = uniform.value;
191
196
  // Save the loading promise in the cache
192
- const promise = loaders.getTexture(texturePath).then(res => {
193
- if (res) {
194
- res = res.clone(); // we need to clone the texture once to avoid colorSpace issues with other materials
195
- res.colorSpace = THREE.LinearSRGBColorSpace;
196
- setTextureParameters(res, name, uniforms);
197
- }
198
- return res;
199
- });
200
- if (checkCache) {
201
- addToCache(cacheKey, promise);
202
- }
203
- promise.then(res => {
204
- if (res) uniform.value = res;
197
+ const promise = loaders.getTexture(texturePath)
198
+ ?.then(res => {
199
+ if (res) {
200
+ res = res.clone(); // we need to clone the texture once to avoid colorSpace issues with other materials
201
+ res.colorSpace = THREE.LinearSRGBColorSpace;
202
+ setTextureParameters(res, name, uniforms);
203
+ }
204
+ return res;
205
+ })
206
+ .catch(err => {
207
+ console.error(`[MaterialX] Failed to load texture ${name} '${texturePath}'`, err);
208
+ return defaultValue;
209
+ });
210
+
211
+ if (checkCache) addToCache(cacheKey, promise);
212
+
213
+ promise?.then(res => {
214
+ if (res) uniform.value = /** @type {any} */ (res);
205
215
  else console.warn(`[MaterialX] Failed to load texture ${name} '${texturePath}'`);
206
216
  });
207
217
  }
@@ -222,12 +232,15 @@ function toThreeUniform(uniforms: any, type: string, value: any, name: string, l
222
232
  return uniform;
223
233
  }
224
234
 
225
- const valueTypeWarningMap = new Map<string, boolean>();
235
+ /** @type {Map<string, boolean>} */
236
+ const valueTypeWarningMap = new Map();
226
237
 
227
238
  /**
228
239
  * Get Three wrapping mode
240
+ * @param {number} mode
241
+ * @returns {THREE.Wrapping}
229
242
  */
230
- function getWrapping(mode: number): THREE.Wrapping {
243
+ function getWrapping(mode) {
231
244
  let wrap;
232
245
  switch (mode) {
233
246
  case 1:
@@ -246,11 +259,14 @@ function getWrapping(mode: number): THREE.Wrapping {
246
259
  return wrap;
247
260
  }
248
261
 
249
-
250
262
  /**
251
263
  * Set Three texture parameters
264
+ * @param {THREE.Texture} texture
265
+ * @param {string} name
266
+ * @param {any} uniforms
267
+ * @param {boolean} [generateMipmaps=true]
252
268
  */
253
- function setTextureParameters(texture: THREE.Texture, name: string, uniforms: any, generateMipmaps = true) {
269
+ function setTextureParameters(texture, name, uniforms, generateMipmaps = true) {
254
270
  const idx = name.lastIndexOf(IMAGE_PROPERTY_SEPARATOR);
255
271
  const base = name.substring(0, idx) || name;
256
272
 
@@ -265,15 +281,16 @@ function setTextureParameters(texture: THREE.Texture, name: string, uniforms: an
265
281
  }
266
282
 
267
283
  const mxFilterType = uniforms.find(base + FILTER_TYPE_SUFFIX) ? uniforms.get(base + FILTER_TYPE_SUFFIX).value : -1;
268
- let minFilter: THREE.TextureFilter = generateMipmaps ? THREE.LinearMipMapLinearFilter : THREE.LinearFilter;
284
+ let minFilter = generateMipmaps ? THREE.LinearMipMapLinearFilter : THREE.LinearFilter;
269
285
  if (mxFilterType === 0) {
270
- minFilter = generateMipmaps ? THREE.NearestMipMapNearestFilter : THREE.NearestFilter;
286
+ minFilter = /** @type {any} */ (generateMipmaps ? THREE.NearestMipMapNearestFilter : THREE.NearestFilter);
271
287
  }
272
288
  texture.minFilter = minFilter;
273
289
  }
274
290
 
275
291
  /**
276
292
  * Return the global light rotation matrix
293
+ * @returns {THREE.Matrix4}
277
294
  */
278
295
  export function getLightRotation() {
279
296
  return new THREE.Matrix4().makeRotationY(Math.PI / 2);
@@ -281,11 +298,11 @@ export function getLightRotation() {
281
298
 
282
299
  /**
283
300
  * Returns all lights nodes in a MaterialX document
284
- * @param {mx.Document} doc
285
- * @returns {Array.<mx.Node>}
301
+ * @param {any} doc
302
+ * @returns {Array<any>}
286
303
  */
287
- export function findLights(doc: MaterialX.Document) {
288
- let lights = new Array<any>;
304
+ export function findLights(doc) {
305
+ let lights = new Array();
289
306
  for (let node of doc.getNodes()) {
290
307
  if (node.getType() === "lightshader")
291
308
  lights.push(node);
@@ -293,14 +310,16 @@ export function findLights(doc: MaterialX.Document) {
293
310
  return lights;
294
311
  }
295
312
 
313
+ /** @type {Object<string, number>} */
296
314
  let lightTypesBound = {};
297
315
 
298
316
  /**
299
317
  * Register lights in shader generation context
300
- * @param {MaterialX.MODULE} mx MaterialX Module
301
- * @param {mx.GenContext} genContext Shader generation context
318
+ * @param {any} mx - MaterialX Module
319
+ * @param {any} genContext - Shader generation context
320
+ * @returns {Promise<void>}
302
321
  */
303
- export async function registerLights(mx: MaterialX.MODULE, genContext: any): Promise<void> {
322
+ export async function registerLights(mx, genContext) {
304
323
  lightTypesBound = {};
305
324
  const maxLightCount = genContext.getOptions().hwMaxActiveLightSources;
306
325
  mx.HwShaderGenerator.unbindLightShaders(genContext);
@@ -353,7 +372,11 @@ export async function registerLights(mx: MaterialX.MODULE, genContext: any): Pro
353
372
  if (debug) console.log("Light types bound in MaterialX context", lightTypesBound);
354
373
  }
355
374
 
356
- // Converts Three.js light type to MaterialX node name
375
+ /**
376
+ * Converts Three.js light type to MaterialX node name
377
+ * @param {string} threeLightType
378
+ * @returns {string}
379
+ */
357
380
  function threeLightTypeToMaterialXNodeName(threeLightType) {
358
381
  switch (threeLightType) {
359
382
  case 'PointLight':
@@ -366,23 +389,27 @@ function threeLightTypeToMaterialXNodeName(threeLightType) {
366
389
  console.warn('MaterialX: Unsupported light type: ' + threeLightType);
367
390
  return 'ND_point_light'; // Default to point light
368
391
  }
369
- };
370
-
371
- export type LightData = {
372
- type: number, // Light type ID
373
- position: THREE.Vector3, // Position in world space
374
- direction: THREE.Vector3, // Direction in world space
375
- color: THREE.Color, // Color of the light
376
- intensity: number, // Intensity of the light
377
- decay_rate: number, // Decay rate for point and spot lights
378
- inner_angle: number, // Inner angle for spot lights
379
- outer_angle: number, // Outer angle for spot lights
380
392
  }
381
393
 
394
+ /**
395
+ * @typedef {Object} LightData
396
+ * @property {number} type - Light type ID
397
+ * @property {THREE.Vector3} position - Position in world space
398
+ * @property {THREE.Vector3} direction - Direction in world space
399
+ * @property {THREE.Color} color - Color of the light
400
+ * @property {number} intensity - Intensity of the light
401
+ * @property {number} decay_rate - Decay rate for point and spot lights
402
+ * @property {number} inner_angle - Inner angle for spot lights
403
+ * @property {number} outer_angle - Outer angle for spot lights
404
+ */
405
+
382
406
  /**
383
407
  * Update light data for shader uniforms
408
+ * @param {Array<THREE.Light>} lights
409
+ * @param {any} genContext
410
+ * @returns {{ lightData: LightData[], lightCount: number }}
384
411
  */
385
- export function getLightData(lights: Array<THREE.Light>, genContext: any): { lightData: LightData[], lightCount: number } {
412
+ export function getLightData(lights, genContext) {
386
413
  const lightData = new Array();
387
414
  const maxLightCount = genContext.getOptions().hwMaxActiveLightSources;
388
415
 
@@ -408,8 +435,8 @@ export function getLightData(lights: Array<THREE.Light>, genContext: any): { lig
408
435
  // float cosDir = dot(result.direction, -light.direction);
409
436
  // float spotAttenuation = smoothstep(low, high, cosDir);
410
437
 
411
- const outerAngleRad = (light as THREE.SpotLight).angle;
412
- const innerAngleRad = outerAngleRad * (1 - (light as THREE.SpotLight).penumbra);
438
+ const outerAngleRad = /** @type {THREE.SpotLight} */ (light).angle;
439
+ const innerAngleRad = outerAngleRad * (1 - /** @type {THREE.SpotLight} */ (light).penumbra);
413
440
  const inner_angle = Math.cos(innerAngleRad);
414
441
  const outer_angle = Math.cos(outerAngleRad);
415
442
 
@@ -420,7 +447,7 @@ export function getLightData(lights: Array<THREE.Light>, genContext: any): { lig
420
447
  color: new THREE.Color().fromArray(light.color.toArray()),
421
448
  // Luminous efficacy for converting radiant power in watts (W) to luminous flux in lumens (lm) at a wavelength of 555 nm.
422
449
  // 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.
423
- intensity: light.intensity * ((light as THREE.PointLight).isPointLight ? 683.0 / 3.1415 : (light as THREE.SpotLight).isSpotLight ? 683.0 / 3.1415 : 1.0),
450
+ intensity: light.intensity * (/** @type {THREE.PointLight} */ (light).isPointLight ? 683.0 / 3.1415 : /** @type {THREE.SpotLight} */ (light).isSpotLight ? 683.0 / 3.1415 : 1.0),
424
451
  decay_rate: 2.0,
425
452
  // Approximations for testing – the relevant light has 61.57986...129.4445 as inner/outer spot angle
426
453
  inner_angle: inner_angle,
@@ -453,8 +480,13 @@ export function getLightData(lights: Array<THREE.Light>, genContext: any): { lig
453
480
 
454
481
  /**
455
482
  * Get uniform values for a shader
483
+ * @param {any} shaderStage
484
+ * @param {Callbacks} loaders
485
+ * @param {string} searchPath
486
+ * @returns {Object<string, THREE.Uniform>}
456
487
  */
457
- export function getUniformValues(shaderStage: MaterialX.ShaderStage, loaders: Loaders, searchPath: string) {
488
+ export function getUniformValues(shaderStage, loaders, searchPath) {
489
+ /** @type {Object<string, THREE.Uniform>} */
458
490
  const threeUniforms = {};
459
491
 
460
492
  const uniformBlocks = shaderStage.getUniformBlocks()
@@ -468,8 +500,8 @@ export function getUniformValues(shaderStage: MaterialX.ShaderStage, loaders: Lo
468
500
  const value = variable.getValue()?.getData();
469
501
  const uniformName = variable.getVariable();
470
502
  const type = variable.getType().getName();
471
- if (debug) console.log("Adding uniform", { path: variable.getPath(), name: uniformName, value: value, type: type });
472
503
  threeUniforms[uniformName] = toThreeUniform(uniforms, type, value, uniformName, loaders, searchPath);
504
+ if (debug) console.log("Adding uniform", { path: variable.getPath(), type: type, name: uniformName, value: threeUniforms[uniformName], },);
473
505
  }
474
506
  }
475
507
  }
@@ -477,7 +509,11 @@ export function getUniformValues(shaderStage: MaterialX.ShaderStage, loaders: Lo
477
509
  return threeUniforms;
478
510
  }
479
511
 
480
- export function generateMaterialPropertiesForUniforms(material: THREE.ShaderMaterial, shaderStage: MaterialX.ShaderStage) {
512
+ /**
513
+ * @param {THREE.ShaderMaterial} material
514
+ * @param {any} shaderStage
515
+ */
516
+ export function generateMaterialPropertiesForUniforms(material, shaderStage) {
481
517
 
482
518
  const uniformBlocks = shaderStage.getUniformBlocks()
483
519
  for (const [blockName, uniforms] of Object.entries(uniformBlocks)) {
@@ -501,22 +537,27 @@ export function generateMaterialPropertiesForUniforms(material: THREE.ShaderMate
501
537
  break;
502
538
  }
503
539
  if (key) {
504
- Object.defineProperty(material, key, {
505
- get: function () {
506
- return this.uniforms?.[uniformName].value
507
- },
508
- set: function (v) {
509
- const uniforms = this.uniforms;
510
- if (!uniforms || !uniforms[uniformName]) {
511
- console.warn(`[MaterialX] Uniform ${uniformName} not found in ${this.name} uniforms`);
512
- return;
540
+ if (material.hasOwnProperty(key)) {
541
+ if (debug) console.warn(`[MaterialX] Uniform ${uniformName} already exists in material as property ${key}, skipping.`);
542
+ }
543
+ else {
544
+ Object.defineProperty(material, key, {
545
+ get: function () {
546
+ return this.uniforms?.[uniformName].value
547
+ },
548
+ set: function (v) {
549
+ const uniforms = this.uniforms;
550
+ if (!uniforms || !uniforms[uniformName]) {
551
+ console.warn(`[MaterialX] Uniform ${uniformName} not found in ${this.name} uniforms`);
552
+ return;
553
+ }
554
+ this.uniforms[uniformName].value = v;
555
+ this.uniformsNeedUpdate = true;
513
556
  }
514
- this.uniforms[uniformName].value = v;
515
- this.uniformsNeedUpdate = true;
516
- }
517
- });
557
+ });
558
+ }
518
559
  }
519
560
  }
520
561
  }
521
562
  }
522
- }
563
+ }