@needle-tools/materialx 1.6.0-next.2af5fc1 → 1.7.0-next.0d06218

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/materialx.js CHANGED
@@ -1,11 +1,22 @@
1
1
  import MaterialX from "../bin/JsMaterialXGenShader.js";
2
2
  import { debug, waitForNetworkIdle } from "./utils.js";
3
- import { renderPMREMToEquirect } from "./utils.texture.js";
4
- import { Light, Mesh, MeshBasicMaterial, Object3D, PlaneGeometry, PMREMGenerator, Scene, Texture, WebGLRenderer } from "three";
3
+ import { renderPMREMToEquirect, renderPMREMToPrefilteredEquirect } from "./utils.texture.js";
4
+ import { CubeUVReflectionMapping, Light, Mesh, MeshBasicMaterial, Object3D, PlaneGeometry, PMREMGenerator, Scene, Texture, WebGLRenderer } from "three";
5
5
  import { registerLights, getLightData } from "./materialx.helper.js";
6
6
  import { whiteTexture } from "./utils.texture.js";
7
7
  import { VERSION } from "./constants.js";
8
8
 
9
+ /**
10
+ * Accept Texture instances from any Three.js copy. Tooling like the fidelity
11
+ * renderer can host scenes with a different Three.js module instance than this
12
+ * package, so `instanceof Texture` is too strict here.
13
+ * @param {unknown} value
14
+ * @returns {value is Texture}
15
+ */
16
+ function isTextureLike(value) {
17
+ return !!value && typeof value === "object" && /** @type {{ isTexture?: unknown }} */(value).isTexture === true;
18
+ }
19
+
9
20
 
10
21
  /**
11
22
  * Preloads the MaterialX WebAssembly module.
@@ -51,7 +62,7 @@ export async function ready() {
51
62
 
52
63
  // NOTE: This must be a plain string literal (not a template) so that the
53
64
  // makeFilesLocal Vite plugin can statically detect and localize this URL.
54
- const defaultBaseUrl = "https://cdn.needle.tools/static/materialx/1.6.0/";
65
+ const defaultBaseUrl = "https://cdn.needle.tools/static/materialx/1.7.0/";
55
66
 
56
67
  /** @type {Array<string>} */
57
68
  let urls;
@@ -121,7 +132,7 @@ export async function ready() {
121
132
  // SPECULAR_ENVIRONMENT_NONE: Do not use specular environment maps.
122
133
  // SPECULAR_ENVIRONMENT_FIS: Use Filtered Importance Sampling for specular environment/indirect lighting.
123
134
  // SPECULAR_ENVIRONMENT_PREFILTER: Use pre-filtered environment maps for specular environment/indirect lighting.
124
- state.materialXGenContext.getOptions().hwSpecularEnvironmentMethod = state.materialXModule.HwSpecularEnvironmentMethod.SPECULAR_ENVIRONMENT_FIS;
135
+ state.materialXGenContext.getOptions().hwSpecularEnvironmentMethod = state.materialXModule.HwSpecularEnvironmentMethod.SPECULAR_ENVIRONMENT_PREFILTER;
125
136
 
126
137
  // TRANSMISSION_REFRACTION: Use a refraction approximation for transmission rendering.
127
138
  // TRANSMISSION_OPACITY: Use opacity for transmission rendering.
@@ -161,6 +172,7 @@ export async function ready() {
161
172
  * @typedef {Object} EnvironmentTextureSet
162
173
  * @property {Texture | null} radianceTexture
163
174
  * @property {Texture | null} irradianceTexture
175
+ * @property {() => void} [dispose]
164
176
  */
165
177
 
166
178
  /**
@@ -285,9 +297,10 @@ export class MaterialXEnvironment {
285
297
  this._pmremGenerator?.dispose();
286
298
  this._pmremGenerator = null;
287
299
  this._renderer = null;
288
- for (const textureSet of this._texturesCache.values()) {
289
- textureSet.radianceTexture?.dispose();
290
- textureSet.irradianceTexture?.dispose();
300
+ for (const textureModeMap of this._texturesCache.values()) {
301
+ for (const textureSet of textureModeMap.values()) {
302
+ textureSet.dispose?.();
303
+ }
291
304
  }
292
305
  this._texturesCache.clear();
293
306
  }
@@ -304,25 +317,26 @@ export class MaterialXEnvironment {
304
317
  * @param {import("./materialx.material.js").MaterialXMaterial} material
305
318
  */
306
319
  getTextures(material) {
320
+ const radianceMode = material.environmentRadianceMode ?? "three-pmrem";
307
321
  if (material.envMap) {
308
322
  // If the material has its own envMap, we don't use the irradiance texture
309
- return this._getTextures(material.envMap);
323
+ return this._getTextures(material.envMap, radianceMode);
310
324
  }
311
325
 
312
326
  // Use the scene background for lighting if no environment is available
313
327
  // If we don't do this we don't see the correct lighting for scenes exported with 'Environment Lighting: Color' and 'Environment Reflections: Skybox'
314
328
  const skybox = this._scene.environment || this._scene.background;
315
- if (skybox instanceof Texture) {
316
- return this._getTextures(skybox);
329
+ if (isTextureLike(skybox)) {
330
+ return this._getTextures(skybox, radianceMode);
317
331
  }
318
- return this._getTextures(null);
332
+ return this._getTextures(null, radianceMode);
319
333
  }
320
334
 
321
335
  /** @type {PMREMGenerator | null} */
322
336
  _pmremGenerator = null;
323
337
  /** @type {WebGLRenderer | null} */
324
338
  _renderer = null;
325
- /** @type {Map<Texture | null, EnvironmentTextureSet>} */
339
+ /** @type {Map<Texture | null, Map<string, EnvironmentTextureSet>>} */
326
340
  _texturesCache = new Map();
327
341
 
328
342
  /**
@@ -331,7 +345,6 @@ export class MaterialXEnvironment {
331
345
  */
332
346
  async _initialize(renderer) {
333
347
  this._isInitialized = false;
334
- this._pmremGenerator = new PMREMGenerator(renderer);
335
348
  this._renderer = renderer;
336
349
  this.updateLighting(true);
337
350
  this._isInitialized = true;
@@ -340,30 +353,65 @@ export class MaterialXEnvironment {
340
353
 
341
354
  /**
342
355
  * @param {Texture | null | undefined} texture
343
- * @returns {{radianceTexture: Texture | null, irradianceTexture: Texture | null}}
356
+ * @param {"three-pmrem" | "materialx-prefiltered" | "materialx-fis"} [radianceMode]
357
+ * @returns {EnvironmentTextureSet}
344
358
  */
345
- _getTextures(texture) {
359
+ _getTextures(texture, radianceMode = "three-pmrem") {
346
360
 
347
361
  // Fallback to white texture if no texture is provided
348
362
  if (!texture) {
349
363
  texture = whiteTexture;
350
364
  }
351
365
 
366
+ const cacheKey = texture || null;
367
+ let textureModeMap = this._texturesCache.get(cacheKey);
368
+ if (!textureModeMap) {
369
+ textureModeMap = new Map();
370
+ this._texturesCache.set(cacheKey, textureModeMap);
371
+ }
372
+
352
373
  /** @type {EnvironmentTextureSet | undefined} */
353
- let res = this._texturesCache.get(texture || null);
374
+ let res = textureModeMap.get(radianceMode);
354
375
  if (res) {
355
376
  return res;
356
377
  }
357
378
 
358
- if (this._scene && this._pmremGenerator && this._renderer && texture) {
379
+ const isPmremTexture = texture.mapping === CubeUVReflectionMapping || texture.isRenderTargetTexture === true;
380
+
381
+ if (this._scene && this._renderer && texture) {
359
382
  if (debug) console.log("[MaterialX] Generating environment textures", texture.name);
360
- const target = this._pmremGenerator.fromEquirectangular(texture);
361
- const radianceRenderTarget = renderPMREMToEquirect(this._renderer, target.texture, 0.0, 1024, 512, target.height);
362
- const irradianceRenderTarget = renderPMREMToEquirect(this._renderer, target.texture, 1.0, 32, 16, target.height);
363
- target.dispose();
383
+ let radianceRenderTarget;
384
+ let irradianceRenderTarget;
385
+
386
+ if (isPmremTexture) {
387
+ // Scene.environment is often already PMREM-processed (CubeUV layout).
388
+ // Running PMREMGenerator on it again corrupts the sampling layout.
389
+ radianceRenderTarget = radianceMode === "materialx-prefiltered"
390
+ ? renderPMREMToPrefilteredEquirect(this._renderer, texture)
391
+ : radianceMode === "materialx-fis"
392
+ ? renderPMREMToEquirect(this._renderer, texture, 0.0, 1024, 512)
393
+ : null;
394
+ irradianceRenderTarget = renderPMREMToEquirect(this._renderer, texture, 1.0, 32, 16);
395
+ } else {
396
+ const target = this._getPMREMGenerator().fromEquirectangular(texture);
397
+ radianceRenderTarget = radianceMode === "materialx-prefiltered"
398
+ ? renderPMREMToPrefilteredEquirect(this._renderer, target.texture, undefined, undefined, target.height)
399
+ : radianceMode === "three-pmrem"
400
+ ? target
401
+ : null;
402
+ irradianceRenderTarget = renderPMREMToEquirect(this._renderer, target.texture, 1.0, 32, 16, target.height);
403
+ if (radianceMode !== "three-pmrem") {
404
+ target.dispose();
405
+ }
406
+ }
407
+
364
408
  res = {
365
- radianceTexture: radianceRenderTarget.texture,
366
- irradianceTexture: irradianceRenderTarget.texture
409
+ radianceTexture: radianceRenderTarget?.texture ?? texture,
410
+ irradianceTexture: irradianceRenderTarget.texture,
411
+ dispose: () => {
412
+ radianceRenderTarget?.dispose();
413
+ irradianceRenderTarget?.dispose();
414
+ },
367
415
  }
368
416
  }
369
417
  else {
@@ -372,10 +420,21 @@ export class MaterialXEnvironment {
372
420
  irradianceTexture: null
373
421
  }
374
422
  }
375
- this._texturesCache.set(texture || null, res);
423
+ textureModeMap.set(radianceMode, res);
376
424
  return res;
377
425
  }
378
426
 
427
+ /**
428
+ * @returns {PMREMGenerator}
429
+ */
430
+ _getPMREMGenerator() {
431
+ if (!this._renderer) {
432
+ throw new Error("[MaterialX] Cannot create PMREMGenerator before renderer initialization.");
433
+ }
434
+ this._pmremGenerator ??= new PMREMGenerator(this._renderer);
435
+ return this._pmremGenerator;
436
+ }
437
+
379
438
  /**
380
439
  * @param {boolean} collectLights
381
440
  */
@@ -1,5 +1,6 @@
1
- import { BufferGeometry, Camera, Group, IUniform, MaterialParameters, Object3D, Scene, ShaderMaterial, Texture, WebGLRenderer } from "three";
1
+ import { BufferGeometry, Camera, Euler, Group, IUniform, MaterialParameters, Object3D, Scene, ShaderMaterial, Texture, WebGLRenderer } from "three";
2
2
  import { MaterialXContext, MaterialXEnvironment } from "./materialx.js";
3
+ import type { MaterialXEnvironmentRadianceMode } from "./materialx.js";
3
4
  import { Callbacks } from "./materialx.helper.js";
4
5
 
5
6
  declare type MaterialXMaterialInitParameters = {
@@ -9,6 +10,8 @@ declare type MaterialXMaterialInitParameters = {
9
10
  loaders: Callbacks;
10
11
  context: MaterialXContext;
11
12
  parameters?: MaterialParameters;
13
+ environmentRadianceMode?: MaterialXEnvironmentRadianceMode;
14
+ specularAntialiasing?: boolean;
12
15
  debug?: boolean;
13
16
  }
14
17
 
@@ -17,6 +20,7 @@ type Precision = "highp" | "mediump" | "lowp";
17
20
 
18
21
  export declare class MaterialXMaterial extends ShaderMaterial {
19
22
  readonly shaderName: string | null;
23
+ readonly ready: Promise<void>;
20
24
 
21
25
  copy(source: MaterialXMaterial): this;
22
26
 
@@ -31,7 +35,12 @@ export declare class MaterialXMaterial extends ShaderMaterial {
31
35
 
32
36
  envMapIntensity: number;
33
37
  envMap: Texture | null;
38
+ envMapRotation: Euler;
39
+ environmentRadianceMode: MaterialXEnvironmentRadianceMode;
40
+ specularAntialiasing: boolean;
34
41
  updateUniforms(environment: MaterialXEnvironment, _renderer: WebGLRenderer, object: Object3D, camera: Camera, time?: number, frame?: number): void;
35
42
 
36
- private updateEnvironmentUniforms(environment: MaterialXEnvironment): void;
43
+ private updateEnvironmentUniforms(environment: MaterialXEnvironment, scene: Scene): void;
37
44
  }
45
+
46
+ export declare const DEFAULT_ENVIRONMENT_RADIANCE_MODE: MaterialXEnvironmentRadianceMode;