rayzee 9.4.2 → 9.5.0

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.
Files changed (144) hide show
  1. package/README.md +223 -21
  2. package/dist/ActiveColor-YvuQq44O.js +13 -0
  3. package/dist/ArchiveImporter-xuUJiklP.js +4301 -0
  4. package/dist/AssetConfig-DSEntE65.js +30 -0
  5. package/dist/BidirectionalIntegrator-C5PKjJoT.js +934 -0
  6. package/dist/ColorManagement-CheBCPaW.js +1488 -0
  7. package/dist/EngineDefaults-DH3bI5t-.js +844 -0
  8. package/dist/EngineEvents-Ci8dVWCP.js +73 -0
  9. package/dist/EngineIssues-CIZnXHsF.js +72 -0
  10. package/dist/FileFormats-sPGJeCRc.js +102 -0
  11. package/dist/Logger-BnHghprG.js +97 -0
  12. package/dist/{NeuralRetouch-vkdXXyK2.js → NeuralRetouch-CW11yLZ_.js} +8 -6
  13. package/dist/{NeuralSuperRes-FVqRJKQV.js → NeuralSuperRes-BekaecYg.js} +55 -120
  14. package/dist/PathTracer-Ccl9KOuF.js +29729 -0
  15. package/dist/PhysicalSky-Dv2PMFGW.js +1219 -0
  16. package/dist/RayzeeRenderer-BQxnzCTD.js +3542 -0
  17. package/dist/SolarLimb-ken-JbDK.js +721 -0
  18. package/dist/ToneMapGPU-BHi5JzIq.js +177 -0
  19. package/dist/ViewTransforms-D0rTLBsX.js +255 -0
  20. package/dist/WorkingMatrix-C6y233hT.js +27 -0
  21. package/dist/ZipReader-gDSWr0I5.js +98 -0
  22. package/dist/addons/archives.es.js +3 -0
  23. package/dist/addons/bidirectional.es.js +2 -0
  24. package/dist/addons/color.es.js +3 -0
  25. package/dist/addons/formats.es.js +2 -0
  26. package/dist/addons/physical-sky.es.js +2 -0
  27. package/dist/addons/storage.es.js +3 -0
  28. package/dist/archiveFormats-BZw7TiDp.js +75 -0
  29. package/dist/identity-DrQsGT2o.js +39 -0
  30. package/dist/openStorage-DwcBRUj7.js +820 -0
  31. package/dist/rayzee-core.es.js +12 -0
  32. package/dist/rayzee.es.js +2266 -44079
  33. package/dist/rayzee.umd.js +485 -473
  34. package/dist/sceneCachePolicy-BmwYOYDu.js +183 -0
  35. package/dist/shared-BGBcSxBQ.js +17 -0
  36. package/package.json +23 -2
  37. package/src/Color/ActiveColor.js +34 -0
  38. package/src/Color/BasicColor.js +147 -0
  39. package/src/Color/ColorManagement.js +7 -31
  40. package/src/EngineDefaults.js +22 -8
  41. package/src/EngineEvents.js +2 -0
  42. package/src/EngineIssues.js +1 -0
  43. package/src/PathTracerApp.js +617 -4016
  44. package/src/Pipeline/PipelineContext.js +0 -4
  45. package/src/Platform.js +36 -0
  46. package/src/Processor/ArchiveCache.js +1 -1
  47. package/src/Processor/ArchiveImporter.js +1259 -0
  48. package/src/Processor/AssetLoader.js +322 -1746
  49. package/src/Processor/AtmosphereModel.js +1 -2
  50. package/src/Processor/BVHBuilder.js +4 -95
  51. package/src/Processor/EnvironmentExactTable.js +187 -0
  52. package/src/Processor/EquirectHDRInfo.js +30 -223
  53. package/src/Processor/FileFormats.js +116 -0
  54. package/src/Processor/GLTFDecoders.js +85 -0
  55. package/src/Processor/GLTFDiffuseTransmission.js +63 -0
  56. package/src/Processor/GeometryExtractor.js +12 -1
  57. package/src/Processor/IESParser.js +47 -35
  58. package/src/Processor/KernelManager.js +35 -2
  59. package/src/Processor/MaterialPacking.js +8 -7
  60. package/src/Processor/PBRT/PBRTMaterials.js +129 -41
  61. package/src/Processor/PBRT/PBRTParser.js +30 -17
  62. package/src/Processor/PBRT/PBRTSceneBuilder.js +548 -37
  63. package/src/Processor/PBRT/PBRTTextureBake.js +154 -0
  64. package/src/Processor/PBRT/PFM.js +59 -0
  65. package/src/Processor/PackedRayBuffer.js +116 -9
  66. package/src/Processor/ParallelBVHBuilder.js +3 -6
  67. package/src/Processor/PhysicalSky.js +5 -4
  68. package/src/Processor/PlatformImageLoader.js +50 -6
  69. package/src/Processor/QueueManager.js +12 -3
  70. package/src/Processor/ReinsertionOptimizer.js +2 -2
  71. package/src/Processor/SceneProcessor.js +15 -55
  72. package/src/Processor/ShaderBuilder.js +6 -16
  73. package/src/Processor/SolarLimb.js +3 -0
  74. package/src/Processor/TextureCreator.js +1 -1
  75. package/src/Processor/Workers/BVHSubtreeWorker.js +1 -34
  76. package/src/Processor/Workers/BVHWorker.js +5 -18
  77. package/src/Processor/Workers/CDFWorker.js +6 -183
  78. package/src/Processor/archiveFormats.js +7 -0
  79. package/src/Processor/utils.js +2 -1
  80. package/src/RayzeeRenderer.js +3902 -0
  81. package/src/RenderSettings.js +49 -41
  82. package/src/Stages/ASVGF.js +5 -7
  83. package/src/Stages/AutoExposure.js +3 -12
  84. package/src/Stages/Compositor.js +8 -23
  85. package/src/Stages/NRD.js +9 -2
  86. package/src/Stages/NormalDepth.js +9 -6
  87. package/src/Stages/PathTracer.js +254 -38
  88. package/src/Stages/PathTracerStage.js +11 -16
  89. package/src/Storage/BLASCache.js +1 -1
  90. package/src/Storage/CDFCache.js +18 -17
  91. package/src/Storage/DownloadCache.js +1 -1
  92. package/src/Storage/SceneGraphCodec.js +46 -3
  93. package/src/Storage/SpillStore.js +1 -1
  94. package/src/Storage/StorageManager.js +2 -4
  95. package/src/Storage/areas.js +4 -0
  96. package/src/TSL/BVHTraversal.js +62 -24
  97. package/src/TSL/Bidirectional.js +204 -0
  98. package/src/TSL/BidirectionalLamps.js +132 -0
  99. package/src/TSL/Clearcoat.js +15 -6
  100. package/src/TSL/Common.js +19 -14
  101. package/src/TSL/ConnectKernel.js +147 -0
  102. package/src/TSL/Debugger.js +1 -1
  103. package/src/TSL/Displacement.js +4 -3
  104. package/src/TSL/EmissiveSampling.js +64 -17
  105. package/src/TSL/Environment.js +77 -106
  106. package/src/TSL/EnvironmentCDF.js +85 -39
  107. package/src/TSL/ExtendKernel.js +8 -3
  108. package/src/TSL/FinalWriteKernel.js +4 -4
  109. package/src/TSL/GenerateKernel.js +18 -4
  110. package/src/TSL/HitFacet.js +35 -5
  111. package/src/TSL/LightBVHSampling.js +40 -9
  112. package/src/TSL/LightGenerateKernel.js +293 -0
  113. package/src/TSL/LightGuide.js +198 -0
  114. package/src/TSL/LightSplatKernel.js +165 -0
  115. package/src/TSL/LightsCore.js +13 -38
  116. package/src/TSL/LightsDirect.js +28 -29
  117. package/src/TSL/LightsIndirect.js +3 -1
  118. package/src/TSL/LightsSampling.js +90 -38
  119. package/src/TSL/MaterialEvaluation.js +113 -31
  120. package/src/TSL/MaterialProperties.js +44 -17
  121. package/src/TSL/MaterialTransmission.js +52 -47
  122. package/src/TSL/MergeKernel.js +218 -0
  123. package/src/TSL/PathTracerCore.js +65 -29
  124. package/src/TSL/Random.js +4 -0
  125. package/src/TSL/SceneResources.js +51 -0
  126. package/src/TSL/ShadeKernel.js +881 -103
  127. package/src/TSL/Struct.js +10 -0
  128. package/src/TSL/Sun.js +1 -1
  129. package/src/TSL/TextureSampling.js +65 -60
  130. package/src/addons/archives.js +14 -0
  131. package/src/addons/bidirectional.js +13 -0
  132. package/src/addons/color.js +32 -0
  133. package/src/addons/formats.js +16 -0
  134. package/src/addons/physicalSky.js +14 -0
  135. package/src/addons/storage.js +19 -0
  136. package/src/core.js +35 -0
  137. package/src/index.js +3 -1
  138. package/src/integrators/BidirectionalIntegrator.js +832 -0
  139. package/src/managers/EnvironmentManager.js +85 -33
  140. package/src/managers/MaterialDataManager.js +42 -0
  141. package/src/managers/UniformManager.js +0 -1
  142. package/dist/ToneMapGPU-DKQM1pMd.js +0 -552
  143. package/dist/rolldown-runtime-CAFD8bLK.js +0 -11
  144. package/src/Processor/TreeletOptimizer.js +0 -574
@@ -8,18 +8,19 @@
8
8
  */
9
9
 
10
10
  import {
11
- RGBAFormat, RedFormat, FloatType, LinearFilter, Vector2, Vector3, Color, Matrix4, DataTexture,
11
+ RGBAFormat, FloatType, LinearFilter, Vector2, Vector3, Color, Matrix4, DataTexture,
12
12
  } from 'three';
13
13
  import { EquirectHDRInfo } from '../Processor/EquirectHDRInfo.js';
14
- import { PhysicalSky } from '../Processor/PhysicalSky.js';
14
+ import { packExactTable } from '../Processor/EnvironmentExactTable.js';
15
15
  import { SimpleSky } from '../Processor/SimpleSky.js';
16
16
  import { convertLinearTriple } from '../Color/WorkingMatrix.js';
17
17
  import { createLogger, fmt } from '../utils/Logger.js';
18
18
 
19
19
  const log = createLogger( 'env' );
20
20
  import { ENGINE_DEFAULTS as DEFAULT_STATE } from '../EngineDefaults.js';
21
- import { getActiveColorManagement } from '../Color/ColorManagement.js';
21
+ import { getActiveColorManagement } from '../Color/ActiveColor.js';
22
22
  import { loadCDF, saveCDF } from '../Storage/CDFCache.js';
23
+ import { ISSUE_CODES } from '../EngineIssues.js';
23
24
 
24
25
  const SKY_WIDTH = 1024;
25
26
  const SKY_HEIGHT = 512;
@@ -45,7 +46,12 @@ export class EnvironmentManager {
45
46
  // CDF computation engine
46
47
  this.equirectHdrInfo = new EquirectHDRInfo();
47
48
 
48
- // Sky renderers (lazy init)
49
+ // Sky renderers (lazy init). The physical sky is a capability: setProceduralSky() installs its class.
50
+ this.ProceduralSky = null;
51
+ this._loadProceduralSky = null;
52
+ this._proceduralSkyLoading = null;
53
+ /** @type {?import('../EngineIssues.js').IssueLog} */
54
+ this.issues = null;
49
55
  this.physicalSky = null;
50
56
  this.simpleSkyRenderer = null;
51
57
  this._sun = null;
@@ -99,7 +105,7 @@ export class EnvironmentManager {
99
105
 
100
106
  /**
101
107
  * Optional callbacks set by the owning stage.
102
- * @type {{ onReset?: Function, onAutoExposureReset?: Function, getSceneTextureNodes?: Function }}
108
+ * @type {{ onReset?: Function, onLightingChanged?: Function, getSceneTextureNodes?: Function }}
103
109
  */
104
110
  this.callbacks = {};
105
111
 
@@ -145,7 +151,7 @@ export class EnvironmentManager {
145
151
  if ( mode !== 'procedural' && prev === 'procedural' ) this._releaseSky();
146
152
 
147
153
  this.markDirty();
148
- this.callbacks.onAutoExposureReset?.();
154
+ this.callbacks.onLightingChanged?.();
149
155
  this._notifyReset();
150
156
 
151
157
  }
@@ -259,42 +265,43 @@ export class EnvironmentManager {
259
265
  * Initialize the packed CDF storage buffer with placeholder data.
260
266
  * Must be called before shader compilation so the node exists in the graph.
261
267
  *
262
- * 1×1 R32F placeholder until a real env CDF is built (env IS is off meanwhile).
268
+ * 1×1 RGBA32F placeholder until a real env table is built (env IS is off meanwhile).
263
269
  * @private
264
270
  */
265
271
  _initCDFTexture() {
266
272
 
267
- this.envCDFTexture = new DataTexture( new Float32Array( [ 0 ] ), 1, 1, RedFormat, FloatType );
273
+ this.envCDFTexture = new DataTexture( new Float32Array( 4 ), 1, 1, RGBAFormat, FloatType );
268
274
  this.envCDFTexture.needsUpdate = true;
275
+ this._exactTable = null;
269
276
 
270
277
  }
271
278
 
272
279
  /**
273
- * Rebuild the CDF texture from equirectHdrInfo. Packs the 2D conditional + 1D marginal into one
274
- * R32F texture of (W+1)×H: conditional[cy*W+cx] at texel (cx, cy); marginal[cy] at texel (W, cy).
280
+ * Rebuild the CDF texture from equirectHdrInfo's table, laid out as packExactTable (EnvironmentExactTable.js)
281
+ * has it.
275
282
  * @private
276
283
  */
277
284
  _updateCDFTexture() {
278
285
 
279
- const marginal = this.equirectHdrInfo.marginalData;
280
- const conditional = this.equirectHdrInfo.conditionalData;
281
- if ( ! marginal || ! conditional ) return;
286
+ const info = this.equirectHdrInfo;
287
+ if ( ! info.exactConditional || ! ( info.exactWidth > 0 ) ) return;
282
288
 
283
- const W = this.equirectHdrInfo.width;
284
- const H = this.equirectHdrInfo.height;
285
- const texW = W + 1;
286
- const data = new Float32Array( texW * H );
287
- for ( let cy = 0; cy < H; cy ++ ) {
289
+ const { data, width, height } = packExactTable( info );
290
+ if ( ! this.envCDFTexture?._isPhysicalSky ) this.envCDFTexture?.dispose?.();
291
+ this.envCDFTexture = new DataTexture( data, width, height, RGBAFormat, FloatType );
292
+ this.envCDFTexture.needsUpdate = true;
293
+ this._exactTable = { radianceIntegral: info.radianceIntegral };
288
294
 
289
- const dstRow = cy * texW;
290
- data.set( conditional.subarray( cy * W, cy * W + W ), dstRow ); // conditional → columns [0,W)
291
- data[ dstRow + W ] = marginal[ cy ]; // marginal → column W
295
+ }
292
296
 
293
- }
297
+ /**
298
+ * The environment as a bidirectional light source: ∫ luminance dω of the map, before intensity, while the
299
+ * CDF texture carries its table; null otherwise.
300
+ * @returns {?{ radianceIntegral: number }}
301
+ */
302
+ get exactTable() {
294
303
 
295
- if ( ! this.envCDFTexture?._isPhysicalSky ) this.envCDFTexture?.dispose?.();
296
- this.envCDFTexture = new DataTexture( data, texW, H, RedFormat, FloatType );
297
- this.envCDFTexture.needsUpdate = true;
304
+ return this.scene.environment ? this._exactTable ?? null : null;
298
305
 
299
306
  }
300
307
 
@@ -371,7 +378,6 @@ export class EnvironmentManager {
371
378
  this._cdfSignature = null;
372
379
  this._updateCDFTexture();
373
380
  this.uniforms.set( 'envTotalSum', 0.0 );
374
- this.uniforms.set( 'envCompensationDelta', 0.0 );
375
381
  return;
376
382
 
377
383
  }
@@ -389,7 +395,6 @@ export class EnvironmentManager {
389
395
  this._cdfSignature = null;
390
396
  this._updateCDFTexture();
391
397
  this.uniforms.set( 'envTotalSum', 0.0 );
392
- this.uniforms.set( 'envCompensationDelta', 0.0 );
393
398
  return;
394
399
 
395
400
  }
@@ -419,7 +424,6 @@ export class EnvironmentManager {
419
424
 
420
425
  this._updateCDFTexture();
421
426
  this.uniforms.set( 'envTotalSum', this.equirectHdrInfo.totalSum );
422
- this.uniforms.set( 'envCompensationDelta', this.equirectHdrInfo.compensationDelta );
423
427
 
424
428
  const { width, height } = this.equirectHdrInfo;
425
429
  if ( width && height ) {
@@ -438,7 +442,6 @@ export class EnvironmentManager {
438
442
  this._cdfSignature = null;
439
443
  log.error( 'CDF build failed:', error );
440
444
  this.uniforms.set( 'envTotalSum', 0.0 );
441
- this.uniforms.set( 'envCompensationDelta', 0.0 );
442
445
 
443
446
  }
444
447
 
@@ -500,7 +503,6 @@ export class EnvironmentManager {
500
503
  this._cdfSignature = null;
501
504
  this._updateCDFTexture();
502
505
  this.uniforms.set( 'envTotalSum', 0.0 );
503
- this.uniforms.set( 'envCompensationDelta', 0.0 );
504
506
 
505
507
  }
506
508
 
@@ -596,6 +598,29 @@ export class EnvironmentManager {
596
598
  */
597
599
  generateProceduralSkyTexture() {
598
600
 
601
+ if ( ! this.ProceduralSky && this._loadProceduralSky ) {
602
+
603
+ this._proceduralSkyLoading ??= this._loadProceduralSky().then( ( Sky ) => {
604
+
605
+ this._proceduralSkyLoading = null;
606
+ if ( ! this.ProceduralSky ) this.ProceduralSky = Sky;
607
+
608
+ } );
609
+ return this._proceduralSkyLoading.then( () => this.generateProceduralSkyTexture() );
610
+
611
+ }
612
+
613
+ if ( ! this.ProceduralSky ) {
614
+
615
+ this.issues?.record(
616
+ ISSUE_CODES.CAPABILITY_MISSING,
617
+ 'procedural mode needs the physical sky: environmentManager.setProceduralSky( PhysicalSky ), from rayzee/addons/physical-sky',
618
+ { capability: 'physical-sky' }
619
+ );
620
+ return Promise.resolve();
621
+
622
+ }
623
+
599
624
  this._skyRequested = true;
600
625
  this._skyCaughtUp ??= new Promise( resolve => void ( this._resolveSky = resolve ) );
601
626
  this._pumpSky();
@@ -639,7 +664,7 @@ export class EnvironmentManager {
639
664
 
640
665
  }
641
666
 
642
- const sky = this.physicalSky ??= new PhysicalSky( SKY_WIDTH, SKY_HEIGHT );
667
+ const sky = this.physicalSky ??= new this.ProceduralSky( SKY_WIDTH, SKY_HEIGHT );
643
668
  const albedo = p.skyGroundAlbedo;
644
669
  const bake = ++ this._skyBakes;
645
670
  const latest = () => bake === this._skyBakes && ! this._skyRequested;
@@ -661,12 +686,12 @@ export class EnvironmentManager {
661
686
  this._installSky( texture, cdfTexture );
662
687
 
663
688
  // Frames until they land normalise by an older bake's.
664
- const { totalSum, compensationDelta } = await stats;
689
+ const { totalSum, radianceIntegral } = await stats;
665
690
  if ( p.mode === 'procedural' && sky === this.physicalSky && bake > this._skyStatsBake ) {
666
691
 
667
692
  this._skyStatsBake = bake;
668
693
  this.uniforms.set( 'envTotalSum', totalSum );
669
- this.uniforms.set( 'envCompensationDelta', compensationDelta );
694
+ if ( this._exactTable?.sky ) this._exactTable.radianceIntegral = radianceIntegral;
670
695
  if ( latest() ) this._notifyReset();
671
696
 
672
697
  }
@@ -687,6 +712,32 @@ export class EnvironmentManager {
687
712
 
688
713
  }
689
714
 
715
+ /**
716
+ * Installs the class that bakes 'procedural' mode — `PhysicalSky` from rayzee/addons/physical-sky: constructed with
717
+ * (width, height), `bake( renderer, params )` returns `{ texture, cdfTexture, sun, stats }`, and `dispose( renderer )`.
718
+ * @param {?Function} Sky
719
+ */
720
+ setProceduralSky( Sky ) {
721
+
722
+ this._loadProceduralSky = null;
723
+ if ( Sky === this.ProceduralSky ) return;
724
+ this._releaseSky();
725
+ this.ProceduralSky = Sky;
726
+ if ( Sky && this.envParams.mode === 'procedural' ) this.generateProceduralSkyTexture();
727
+
728
+ }
729
+
730
+ /**
731
+ * Installs the physical sky on first use: `load()` resolves to the class setProceduralSky() takes, the first time
732
+ * 'procedural' mode is baked.
733
+ * @param {function(): Promise<Function>} load
734
+ */
735
+ setProceduralSkyLoader( load ) {
736
+
737
+ this._loadProceduralSky = load;
738
+
739
+ }
740
+
690
741
  /** @private */
691
742
  _installSky( texture, cdfTexture ) {
692
743
 
@@ -701,6 +752,7 @@ export class EnvironmentManager {
701
752
 
702
753
  // So an HDRI coming back rebuilds its own table.
703
754
  this._cdfSignature = null;
755
+ if ( ! this._exactTable?.sky ) this._exactTable = { radianceIntegral: 0, sky: true };
704
756
  texture._isGeneratedProcedural = true;
705
757
  this.scene.environment = texture;
706
758
  this.environmentTexture = texture;
@@ -18,6 +18,7 @@ import { resolveMaterialTextures, MATERIAL_VALUE_SOURCE } from '../Processor/Geo
18
18
  import { convertLinearTriple, convertLinearTriples, getWorkingMatrixSpace } from '../Color/WorkingMatrix.js';
19
19
  import { createLogger, fmt } from '../utils/Logger.js';
20
20
  import { toPortable } from '../SceneState/portable.js';
21
+ import { MATERIAL_LAYERS } from '../TSL/SceneResources.js';
21
22
 
22
23
  /** Material buffers already moved into the working space, so a re-init cannot convert twice. */
23
24
  const convertedBuffers = new WeakSet();
@@ -41,6 +42,7 @@ const TEXTURE_POOLS = [
41
42
  [ 'displacementMap', false ], [ 'anisotropyMap', false ], [ 'transmissionMap', false ],
42
43
  [ 'clearcoatMap', false ], [ 'clearcoatRoughnessMap', false ], [ 'sheenRoughnessMap', false ],
43
44
  [ 'iridescenceMap', false ], [ 'iridescenceThicknessMap', false ], [ 'specularIntensityMap', false ],
45
+ [ 'diffuseTransmissionColorMap', true ], [ 'diffuseTransmissionMap', false ],
44
46
  ];
45
47
 
46
48
  // Scalar slots readable via getMaterialProperty().
@@ -67,6 +69,7 @@ const SCALAR_PROPERTY_OFFSETS = {
67
69
  subsurfaceAnisotropy: M.SUBSURFACE_ANISOTROPY,
68
70
  anisotropy: M.ANISOTROPY,
69
71
  anisotropyRotation: M.ANISOTROPY_ROTATION, // radians, unlike the degrees hosts usually show
72
+ diffuseTransmission: M.DIFFUSE_TRANSMISSION,
70
73
  };
71
74
 
72
75
  export class MaterialDataManager {
@@ -119,6 +122,7 @@ export class MaterialDataManager {
119
122
  */
120
123
  static COLOR_OFFSETS = [
121
124
  M.COLOR, M.EMISSIVE, M.ATTENUATION_COLOR, M.SHEEN_COLOR, M.SPECULAR_COLOR, M.SUBSURFACE_COLOR,
125
+ M.DIFFUSE_TRANSMISSION_COLOR,
122
126
  ].filter( o => Number.isInteger( o ) );
123
127
 
124
128
  /**
@@ -186,6 +190,7 @@ export class MaterialDataManager {
186
190
  this.materialCount = Math.floor( vec4Count / PIXELS_PER_MATERIAL );
187
191
  this._sources = [ ...sources ];
188
192
  this._hostSet = [];
193
+ this.callbacks.onMaterialFeaturesChanged?.();
189
194
  log.debug( `${fmt.n( this.materialCount )} materials (storage buffer)` );
190
195
 
191
196
  }
@@ -300,6 +305,26 @@ export class MaterialDataManager {
300
305
 
301
306
  }
302
307
 
308
+ /**
309
+ * Which MATERIAL_LAYERS some material uses (its factor above 0): the kernels compile the others out.
310
+ * @returns {Object<string, boolean>}
311
+ */
312
+ materialLayers() {
313
+
314
+ const layers = Object.fromEntries( MATERIAL_LAYERS.map( ( layer ) => [ layer, false ] ) );
315
+ const data = this.materialStorageAttr?.array;
316
+ if ( ! data ) return layers;
317
+ for ( let i = 0; i < this.materialCount; i ++ ) {
318
+
319
+ const base = i * M.FLOATS_PER_MATERIAL;
320
+ for ( const layer of MATERIAL_LAYERS ) if ( data[ base + SCALAR_PROPERTY_OFFSETS[ layer ] ] > 0 ) layers[ layer ] = true;
321
+
322
+ }
323
+
324
+ return layers;
325
+
326
+ }
327
+
303
328
  /**
304
329
  * Where a material property's value came from, as a MATERIAL_VALUE_SOURCE. `default` means the
305
330
  * model never said and the engine filled it in.
@@ -466,6 +491,19 @@ export class MaterialDataManager {
466
491
  case 'subsurfaceAnisotropy': data[ stride + M.SUBSURFACE_ANISOTROPY ] = value; break;
467
492
  case 'anisotropy': data[ stride + M.ANISOTROPY ] = value; break;
468
493
  case 'anisotropyRotation': data[ stride + M.ANISOTROPY_ROTATION ] = value; break;
494
+ case 'diffuseTransmission': data[ stride + M.DIFFUSE_TRANSMISSION ] = value; break;
495
+ case 'diffuseTransmissionColor':
496
+ if ( value.r !== undefined ) {
497
+
498
+ this._writeColor( data, stride + M.DIFFUSE_TRANSMISSION_COLOR, value.r, value.g, value.b );
499
+
500
+ } else if ( Array.isArray( value ) ) {
501
+
502
+ this._writeColor( data, stride + M.DIFFUSE_TRANSMISSION_COLOR, value[ 0 ], value[ 1 ], value[ 2 ] );
503
+
504
+ }
505
+
506
+ break;
469
507
  case 'subsurfaceColor':
470
508
  if ( value.r !== undefined ) {
471
509
 
@@ -510,6 +548,9 @@ export class MaterialDataManager {
510
548
 
511
549
  }
512
550
 
551
+ // A material layer is compiled in only while some material uses it.
552
+ if ( MATERIAL_LAYERS.includes( property ) ) this.callbacks.onMaterialFeaturesChanged?.();
553
+
513
554
  this._notifyReset();
514
555
 
515
556
  }
@@ -545,6 +586,7 @@ export class MaterialDataManager {
545
586
  // Both read back the block, so they must follow the write.
546
587
  this._patchTriangleSideForMaterial( materialIndex, data[ base + M.SIDE ] );
547
588
  this._recomputeOpaqueBlockerForMaterial( materialIndex );
589
+ this.callbacks.onMaterialFeaturesChanged?.();
548
590
 
549
591
  this.materialStorageAttr.needsUpdate = true;
550
592
  this._notifyReset();
@@ -265,7 +265,6 @@ export class UniformManager {
265
265
  ub( 'enableEnvironment', DEFAULT_STATE.enableEnvironment );
266
266
  u( 'environmentMatrix', new Matrix4(), 'mat4' );
267
267
  u( 'envTotalSum', 0.0, 'float' );
268
- u( 'envCompensationDelta', 0.0, 'float' );
269
268
  u( 'envResolution', new Vector2( 1, 1 ), 'vec2' );
270
269
  ub( 'groundProjectionEnabled', DEFAULT_STATE.groundProjectionEnabled );
271
270
  u( 'groundProjectionRadius', DEFAULT_STATE.groundProjectionRadius, 'float' );