rayzee 10.0.0 → 11.0.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 (173) hide show
  1. package/README.md +252 -44
  2. package/dist/ActiveColor-YvuQq44O.js +13 -0
  3. package/dist/ArchiveImporter-BRQvLggb.js +4300 -0
  4. package/dist/AssetConfig-DSEntE65.js +30 -0
  5. package/dist/BidirectionalIntegrator-9lZQxFVH.js +934 -0
  6. package/dist/BufferLayout-D_IBo2ci.js +150 -0
  7. package/dist/ColorManagement-oeEk7oSu.js +1488 -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-dDDGj9Av.js +97 -0
  12. package/dist/{NeuralRetouch-CYgkQY8Z.js → NeuralRetouch-BCXylThH.js} +8 -6
  13. package/dist/{NeuralSuperRes-PkgDLQcD.js → NeuralSuperRes-sZAeS142.js} +55 -120
  14. package/dist/PathTracer-CQPW06Mm.js +29971 -0
  15. package/dist/PhysicalSky-BvqCw_dy.js +1219 -0
  16. package/dist/RayzeeRenderer-COpwotpU.js +3551 -0
  17. package/dist/SolarLimb-CPaeZVwz.js +721 -0
  18. package/dist/ToneMapGPU-CjU3osDd.js +177 -0
  19. package/dist/ViewTransforms-CYN7bq2w.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 +2551 -44024
  33. package/dist/rayzee.umd.js +528 -487
  34. package/dist/sceneCachePolicy-BSSsKY98.js +443 -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/Color/ViewTransforms.js +5 -1
  41. package/src/EngineDefaults.js +62 -1029
  42. package/src/EngineEvents.js +2 -0
  43. package/src/EngineIssues.js +2 -0
  44. package/src/Headless.js +6 -5
  45. package/src/Passes/AIUpscaler.js +58 -141
  46. package/src/Passes/OIDNDenoiser.js +92 -65
  47. package/src/Passes/OIDNTemporalHistory.js +1 -1
  48. package/src/PathTracerApp.js +620 -4026
  49. package/src/Pipeline/PipelineContext.js +0 -4
  50. package/src/Platform.js +36 -0
  51. package/src/Processor/ArchiveCache.js +1 -1
  52. package/src/Processor/ArchiveImporter.js +1259 -0
  53. package/src/Processor/AssetLoader.js +327 -1763
  54. package/src/Processor/AtmosphereModel.js +1 -2
  55. package/src/Processor/BVHBuilder.js +14 -126
  56. package/src/Processor/BVHLeafFold.js +4 -6
  57. package/src/Processor/BVHRefitter.js +67 -46
  58. package/src/Processor/BufferLayout.js +291 -0
  59. package/src/Processor/EmissiveTriangleBuilder.js +1 -1
  60. package/src/Processor/EnvironmentExactTable.js +187 -0
  61. package/src/Processor/EquirectHDRInfo.js +30 -223
  62. package/src/Processor/FileFormats.js +116 -0
  63. package/src/Processor/GLTFDecoders.js +85 -0
  64. package/src/Processor/GLTFDiffuseTransmission.js +63 -0
  65. package/src/Processor/GeometryExtractor.js +15 -5
  66. package/src/Processor/HostMemory.js +7 -1
  67. package/src/Processor/IESParser.js +47 -35
  68. package/src/Processor/InstanceTable.js +1 -1
  69. package/src/Processor/KernelManager.js +35 -2
  70. package/src/Processor/MaterialPacking.js +10 -8
  71. package/src/Processor/PBRT/PBRTMaterials.js +129 -41
  72. package/src/Processor/PBRT/PBRTParser.js +30 -17
  73. package/src/Processor/PBRT/PBRTSceneBuilder.js +548 -37
  74. package/src/Processor/PBRT/PBRTTextureBake.js +154 -0
  75. package/src/Processor/PBRT/PFM.js +59 -0
  76. package/src/Processor/PackedRayBuffer.js +116 -9
  77. package/src/Processor/ParallelBVHBuilder.js +3 -6
  78. package/src/Processor/PhysicalSky.js +5 -4
  79. package/src/Processor/PlatformImageLoader.js +50 -6
  80. package/src/Processor/QueueManager.js +39 -5
  81. package/src/Processor/ReinsertionOptimizer.js +2 -2
  82. package/src/Processor/SceneProcessor.js +53 -62
  83. package/src/Processor/ShaderBuilder.js +6 -16
  84. package/src/Processor/SimpleSky.js +1 -62
  85. package/src/Processor/SolarLimb.js +3 -0
  86. package/src/Processor/SplitBLAS.js +1 -1
  87. package/src/Processor/StorageTexturePool.js +22 -1
  88. package/src/Processor/TLASBuilder.js +1 -3
  89. package/src/Processor/TextureBuckets.js +237 -0
  90. package/src/Processor/TextureCreator.js +181 -8
  91. package/src/Processor/ToneMapGPU.js +64 -15
  92. package/src/Processor/Workers/BVHRefitWorker.js +5 -2
  93. package/src/Processor/Workers/BVHSubtreeWorker.js +1 -34
  94. package/src/Processor/Workers/BVHWorker.js +5 -18
  95. package/src/Processor/Workers/CDFWorker.js +6 -183
  96. package/src/Processor/Workers/TexturesWorker.js +103 -18
  97. package/src/Processor/archiveFormats.js +7 -0
  98. package/src/Processor/utils.js +2 -1
  99. package/src/RayzeeRenderer.js +3899 -0
  100. package/src/RenderSettings.js +68 -62
  101. package/src/Stages/ASVGF.js +13 -14
  102. package/src/Stages/AutoExposure.js +17 -17
  103. package/src/Stages/BilateralFilter.js +2 -1
  104. package/src/Stages/Compositor.js +8 -23
  105. package/src/Stages/DenoiserSettings.js +182 -0
  106. package/src/Stages/EdgeFilter.js +2 -1
  107. package/src/Stages/MotionVector.js +1 -1
  108. package/src/Stages/NRD.js +11 -5
  109. package/src/Stages/NormalDepth.js +12 -8
  110. package/src/Stages/PathTracer.js +338 -43
  111. package/src/Stages/PathTracerStage.js +69 -66
  112. package/src/Stages/Variance.js +1 -1
  113. package/src/Storage/BLASCache.js +1 -1
  114. package/src/Storage/CDFCache.js +18 -17
  115. package/src/Storage/DownloadCache.js +1 -1
  116. package/src/Storage/SceneGraphCodec.js +46 -3
  117. package/src/Storage/SpillStore.js +1 -1
  118. package/src/Storage/StorageManager.js +45 -9
  119. package/src/Storage/areas.js +4 -0
  120. package/src/TSL/BVHTraversal.js +50 -25
  121. package/src/TSL/Bidirectional.js +204 -0
  122. package/src/TSL/BidirectionalLamps.js +132 -0
  123. package/src/TSL/Clearcoat.js +15 -6
  124. package/src/TSL/Common.js +20 -15
  125. package/src/TSL/ConnectKernel.js +147 -0
  126. package/src/TSL/Debugger.js +1 -1
  127. package/src/TSL/Displacement.js +4 -3
  128. package/src/TSL/EmissiveSampling.js +65 -18
  129. package/src/TSL/Environment.js +77 -106
  130. package/src/TSL/EnvironmentCDF.js +85 -39
  131. package/src/TSL/ExtendKernel.js +8 -3
  132. package/src/TSL/FinalWriteKernel.js +4 -4
  133. package/src/TSL/GenerateKernel.js +18 -4
  134. package/src/TSL/HitFacet.js +35 -5
  135. package/src/TSL/LightBVHSampling.js +40 -9
  136. package/src/TSL/LightGenerateKernel.js +293 -0
  137. package/src/TSL/LightGuide.js +198 -0
  138. package/src/TSL/LightSplatKernel.js +165 -0
  139. package/src/TSL/LightVisibility.js +111 -0
  140. package/src/TSL/LightsCore.js +13 -38
  141. package/src/TSL/LightsDirect.js +28 -29
  142. package/src/TSL/LightsIndirect.js +3 -1
  143. package/src/TSL/LightsSampling.js +239 -51
  144. package/src/TSL/MaterialEvaluation.js +113 -31
  145. package/src/TSL/MaterialProperties.js +44 -17
  146. package/src/TSL/MaterialTransmission.js +52 -47
  147. package/src/TSL/MergeKernel.js +218 -0
  148. package/src/TSL/PathTracerCore.js +65 -29
  149. package/src/TSL/Random.js +5 -1
  150. package/src/TSL/SceneResources.js +51 -0
  151. package/src/TSL/ShadeKernel.js +964 -101
  152. package/src/TSL/Struct.js +10 -0
  153. package/src/TSL/Sun.js +1 -1
  154. package/src/TSL/TextureSampling.js +66 -61
  155. package/src/addons/archives.js +14 -0
  156. package/src/addons/bidirectional.js +13 -0
  157. package/src/addons/color.js +32 -0
  158. package/src/addons/formats.js +16 -0
  159. package/src/addons/physicalSky.js +14 -0
  160. package/src/addons/storage.js +19 -0
  161. package/src/core.js +31 -0
  162. package/src/index.js +13 -23
  163. package/src/integrators/BidirectionalIntegrator.js +833 -0
  164. package/src/managers/AnimationManager.js +88 -6
  165. package/src/managers/CameraManager.js +103 -46
  166. package/src/managers/DenoisingManager.js +14 -10
  167. package/src/managers/EnvironmentManager.js +125 -103
  168. package/src/managers/GoboManager.js +9 -0
  169. package/src/managers/MaterialDataManager.js +48 -37
  170. package/src/managers/UniformManager.js +32 -10
  171. package/dist/ToneMapGPU-B6XIe3DU.js +0 -544
  172. package/dist/rolldown-runtime-CAFD8bLK.js +0 -11
  173. package/src/Processor/TreeletOptimizer.js +0 -574
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * EnvironmentManager.js
3
- * Manages HDRI loading, CDF importance sampling, procedural/gradient/solid sky
3
+ * Manages HDRI loading, CDF importance sampling, procedural/solid sky
4
4
  * generation, and environment rotation for the path tracing pipeline.
5
5
  *
6
6
  * Storage buffer nodes are created once and never replaced — only .value
@@ -8,18 +8,38 @@
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
+ import { sunPosition, dayOfYearForMonth } from '../Processor/SunPosition.js';
19
+ import { getActiveColorManagement } from '../Color/ActiveColor.js';
20
+ import { loadCDF, saveCDF } from '../Storage/CDFCache.js';
21
+ import { ISSUE_CODES } from '../EngineIssues.js';
18
22
 
19
23
  const log = createLogger( 'env' );
20
- import { ENGINE_DEFAULTS as DEFAULT_STATE } from '../EngineDefaults.js';
21
- import { getActiveColorManagement } from '../Color/ColorManagement.js';
22
- import { loadCDF, saveCDF } from '../Storage/CDFCache.js';
24
+
25
+ /** The default sun as a time of day (solar hours), month and latitude; north lies along −Z. */
26
+ export const DEFAULT_SUN_PATH = Object.freeze( { time: 10, month: 3, latitude: 40 } );
27
+
28
+ const DEFAULT_SUN = sunPosition( { hours: DEFAULT_SUN_PATH.time, dayOfYear: dayOfYearForMonth( DEFAULT_SUN_PATH.month ), latitude: DEFAULT_SUN_PATH.latitude } );
29
+
30
+ /** The skies an environment starts with: a solid colour, and the physical sky's Clear Day. */
31
+ export const SKY_DEFAULTS = Object.freeze( {
32
+ solidSkyColor: '#87CEEB',
33
+ skySunAzimuth: 180 - DEFAULT_SUN.azimuth,
34
+ skySunElevation: DEFAULT_SUN.elevation,
35
+ skySunStrength: 1, // artistic multiplier on the disc and its light; 1 is physical
36
+ skySunSize: 0.53, // angular diameter, degrees
37
+ skyTurbidity: 2, // 1 is aerosol-free air, ~2 a clear day, 6+ hazy
38
+ skyOzone: 300, // Dobson units
39
+ skyAirDensity: 1,
40
+ skyGroundAlbedo: '#959595', // 0.3 linear
41
+ skyAltitude: 50, // metres
42
+ } );
23
43
 
24
44
  const SKY_WIDTH = 1024;
25
45
  const SKY_HEIGHT = 512;
@@ -45,7 +65,12 @@ export class EnvironmentManager {
45
65
  // CDF computation engine
46
66
  this.equirectHdrInfo = new EquirectHDRInfo();
47
67
 
48
- // Sky renderers (lazy init)
68
+ // Sky renderers (lazy init). The physical sky is a capability: setProceduralSky() installs its class.
69
+ this.ProceduralSky = null;
70
+ this._loadProceduralSky = null;
71
+ this._proceduralSkyLoading = null;
72
+ /** @type {?import('../EngineIssues.js').IssueLog} */
73
+ this.issues = null;
49
74
  this.physicalSky = null;
50
75
  this.simpleSkyRenderer = null;
51
76
  this._sun = null;
@@ -83,28 +108,23 @@ export class EnvironmentManager {
83
108
  this.envParams = {
84
109
  mode: 'hdri',
85
110
 
86
- // Gradient Sky
87
- gradientZenithColor: new Color( DEFAULT_STATE.gradientZenithColor ),
88
- gradientHorizonColor: new Color( DEFAULT_STATE.gradientHorizonColor ),
89
- gradientGroundColor: new Color( DEFAULT_STATE.gradientGroundColor ),
90
-
91
111
  // Solid Color Sky
92
- solidSkyColor: new Color( DEFAULT_STATE.solidSkyColor ),
112
+ solidSkyColor: new Color( SKY_DEFAULTS.solidSkyColor ),
93
113
 
94
114
  // Physical sky. The sun direction is in the sky's own frame; environment rotation turns both.
95
115
  skySunDirection: this._calculateInitialSunDirection(),
96
- skySunStrength: DEFAULT_STATE.skySunStrength,
97
- skySunSize: DEFAULT_STATE.skySunSize,
98
- skyTurbidity: DEFAULT_STATE.skyTurbidity,
99
- skyOzone: DEFAULT_STATE.skyOzone,
100
- skyAirDensity: DEFAULT_STATE.skyAirDensity,
101
- skyGroundAlbedo: new Color( DEFAULT_STATE.skyGroundAlbedo ),
102
- skyAltitude: DEFAULT_STATE.skyAltitude,
116
+ skySunStrength: SKY_DEFAULTS.skySunStrength,
117
+ skySunSize: SKY_DEFAULTS.skySunSize,
118
+ skyTurbidity: SKY_DEFAULTS.skyTurbidity,
119
+ skyOzone: SKY_DEFAULTS.skyOzone,
120
+ skyAirDensity: SKY_DEFAULTS.skyAirDensity,
121
+ skyGroundAlbedo: new Color( SKY_DEFAULTS.skyGroundAlbedo ),
122
+ skyAltitude: SKY_DEFAULTS.skyAltitude,
103
123
  };
104
124
 
105
125
  /**
106
126
  * Optional callbacks set by the owning stage.
107
- * @type {{ onReset?: Function, onAutoExposureReset?: Function, getSceneTextureNodes?: Function }}
127
+ * @type {{ onReset?: Function, onLightingChanged?: Function, getSceneTextureNodes?: Function }}
108
128
  */
109
129
  this.callbacks = {};
110
130
 
@@ -116,9 +136,9 @@ export class EnvironmentManager {
116
136
  // ===== MODE STATE MACHINE =====
117
137
 
118
138
  /**
119
- * Switches the environment mode (hdri, gradient, color, procedural — the physical sky).
139
+ * Switches the environment mode (hdri, color, procedural — the physical sky).
120
140
  * Preserves the HDRI texture when switching away, restores when switching back.
121
- * @param {'hdri'|'gradient'|'color'|'procedural'} mode
141
+ * @param {'hdri'|'color'|'procedural'} mode
122
142
  */
123
143
  async setMode( mode ) {
124
144
 
@@ -132,11 +152,7 @@ export class EnvironmentManager {
132
152
 
133
153
  }
134
154
 
135
- if ( mode === 'gradient' ) {
136
-
137
- await this.generateGradientTexture();
138
-
139
- } else if ( mode === 'color' ) {
155
+ if ( mode === 'color' ) {
140
156
 
141
157
  await this.generateSolidColorTexture();
142
158
 
@@ -154,7 +170,7 @@ export class EnvironmentManager {
154
170
  if ( mode !== 'procedural' && prev === 'procedural' ) this._releaseSky();
155
171
 
156
172
  this.markDirty();
157
- this.callbacks.onAutoExposureReset?.();
173
+ this.callbacks.onLightingChanged?.();
158
174
  this._notifyReset();
159
175
 
160
176
  }
@@ -185,9 +201,6 @@ export class EnvironmentManager {
185
201
  return {
186
202
  mode: p.mode,
187
203
  hdri: this.hdriSource,
188
- gradientZenithColor: p.gradientZenithColor.toArray(),
189
- gradientHorizonColor: p.gradientHorizonColor.toArray(),
190
- gradientGroundColor: p.gradientGroundColor.toArray(),
191
204
  solidSkyColor: p.solidSkyColor.toArray(),
192
205
  skyModel: 'atmosphere',
193
206
  skySunDirection: p.skySunDirection.toArray(),
@@ -212,7 +225,7 @@ export class EnvironmentManager {
212
225
  const p = this.envParams;
213
226
  // Sessions saved before the physical sky gave the same names other meanings.
214
227
  const atmosphere = state.skyModel === 'atmosphere';
215
- const colors = [ 'gradientZenithColor', 'gradientHorizonColor', 'gradientGroundColor', 'solidSkyColor', 'skySunDirection' ];
228
+ const colors = [ 'solidSkyColor', 'skySunDirection' ];
216
229
  for ( const key of atmosphere ? [ ...colors, 'skyGroundAlbedo' ] : colors ) {
217
230
 
218
231
  if ( Array.isArray( state[ key ] ) ) p[ key ].fromArray( state[ key ] );
@@ -229,7 +242,9 @@ export class EnvironmentManager {
229
242
 
230
243
  }
231
244
 
232
- if ( state.mode && ( state.mode !== 'hdri' || p.mode !== 'hdri' ) ) await this.setMode( state.mode );
245
+ // A session saved with the removed gradient sky keeps the sky it is already showing.
246
+ const known = [ 'hdri', 'color', 'procedural' ].includes( state.mode );
247
+ if ( known && ( state.mode !== 'hdri' || p.mode !== 'hdri' ) ) await this.setMode( state.mode );
233
248
 
234
249
  }
235
250
 
@@ -249,13 +264,6 @@ export class EnvironmentManager {
249
264
 
250
265
  }
251
266
 
252
- /** @see generateGradientTexture */
253
- generateGradient() {
254
-
255
- return this.generateGradientTexture();
256
-
257
- }
258
-
259
267
  /** @see generateSolidColorTexture */
260
268
  generateSolid() {
261
269
 
@@ -276,42 +284,43 @@ export class EnvironmentManager {
276
284
  * Initialize the packed CDF storage buffer with placeholder data.
277
285
  * Must be called before shader compilation so the node exists in the graph.
278
286
  *
279
- * 1×1 R32F placeholder until a real env CDF is built (env IS is off meanwhile).
287
+ * 1×1 RGBA32F placeholder until a real env table is built (env IS is off meanwhile).
280
288
  * @private
281
289
  */
282
290
  _initCDFTexture() {
283
291
 
284
- this.envCDFTexture = new DataTexture( new Float32Array( [ 0 ] ), 1, 1, RedFormat, FloatType );
292
+ this.envCDFTexture = new DataTexture( new Float32Array( 4 ), 1, 1, RGBAFormat, FloatType );
285
293
  this.envCDFTexture.needsUpdate = true;
294
+ this._exactTable = null;
286
295
 
287
296
  }
288
297
 
289
298
  /**
290
- * Rebuild the CDF texture from equirectHdrInfo. Packs the 2D conditional + 1D marginal into one
291
- * R32F texture of (W+1)×H: conditional[cy*W+cx] at texel (cx, cy); marginal[cy] at texel (W, cy).
299
+ * Rebuild the CDF texture from equirectHdrInfo's table, laid out as packExactTable (EnvironmentExactTable.js)
300
+ * has it.
292
301
  * @private
293
302
  */
294
303
  _updateCDFTexture() {
295
304
 
296
- const marginal = this.equirectHdrInfo.marginalData;
297
- const conditional = this.equirectHdrInfo.conditionalData;
298
- if ( ! marginal || ! conditional ) return;
305
+ const info = this.equirectHdrInfo;
306
+ if ( ! info.exactConditional || ! ( info.exactWidth > 0 ) ) return;
299
307
 
300
- const W = this.equirectHdrInfo.width;
301
- const H = this.equirectHdrInfo.height;
302
- const texW = W + 1;
303
- const data = new Float32Array( texW * H );
304
- for ( let cy = 0; cy < H; cy ++ ) {
308
+ const { data, width, height } = packExactTable( info );
309
+ if ( ! this.envCDFTexture?._isPhysicalSky ) this.envCDFTexture?.dispose?.();
310
+ this.envCDFTexture = new DataTexture( data, width, height, RGBAFormat, FloatType );
311
+ this.envCDFTexture.needsUpdate = true;
312
+ this._exactTable = { radianceIntegral: info.radianceIntegral };
305
313
 
306
- const dstRow = cy * texW;
307
- data.set( conditional.subarray( cy * W, cy * W + W ), dstRow ); // conditional → columns [0,W)
308
- data[ dstRow + W ] = marginal[ cy ]; // marginal → column W
314
+ }
309
315
 
310
- }
316
+ /**
317
+ * The environment as a bidirectional light source: ∫ luminance dω of the map, before intensity, while the
318
+ * CDF texture carries its table; null otherwise.
319
+ * @returns {?{ radianceIntegral: number }}
320
+ */
321
+ get exactTable() {
311
322
 
312
- if ( ! this.envCDFTexture?._isPhysicalSky ) this.envCDFTexture?.dispose?.();
313
- this.envCDFTexture = new DataTexture( data, texW, H, RedFormat, FloatType );
314
- this.envCDFTexture.needsUpdate = true;
323
+ return this.scene.environment ? this._exactTable ?? null : null;
315
324
 
316
325
  }
317
326
 
@@ -388,7 +397,6 @@ export class EnvironmentManager {
388
397
  this._cdfSignature = null;
389
398
  this._updateCDFTexture();
390
399
  this.uniforms.set( 'envTotalSum', 0.0 );
391
- this.uniforms.set( 'envCompensationDelta', 0.0 );
392
400
  return;
393
401
 
394
402
  }
@@ -406,7 +414,6 @@ export class EnvironmentManager {
406
414
  this._cdfSignature = null;
407
415
  this._updateCDFTexture();
408
416
  this.uniforms.set( 'envTotalSum', 0.0 );
409
- this.uniforms.set( 'envCompensationDelta', 0.0 );
410
417
  return;
411
418
 
412
419
  }
@@ -436,7 +443,6 @@ export class EnvironmentManager {
436
443
 
437
444
  this._updateCDFTexture();
438
445
  this.uniforms.set( 'envTotalSum', this.equirectHdrInfo.totalSum );
439
- this.uniforms.set( 'envCompensationDelta', this.equirectHdrInfo.compensationDelta );
440
446
 
441
447
  const { width, height } = this.equirectHdrInfo;
442
448
  if ( width && height ) {
@@ -455,7 +461,6 @@ export class EnvironmentManager {
455
461
  this._cdfSignature = null;
456
462
  log.error( 'CDF build failed:', error );
457
463
  this.uniforms.set( 'envTotalSum', 0.0 );
458
- this.uniforms.set( 'envCompensationDelta', 0.0 );
459
464
 
460
465
  }
461
466
 
@@ -517,7 +522,6 @@ export class EnvironmentManager {
517
522
  this._cdfSignature = null;
518
523
  this._updateCDFTexture();
519
524
  this.uniforms.set( 'envTotalSum', 0.0 );
520
- this.uniforms.set( 'envCompensationDelta', 0.0 );
521
525
 
522
526
  }
523
527
 
@@ -547,7 +551,7 @@ export class EnvironmentManager {
547
551
 
548
552
  /**
549
553
  * Enter 'hdri' mode ahead of an async HDRI install, and drop the texture stashed by a
550
- * previous setMode( 'gradient' | 'color' | 'procedural' ): a new HDRI makes it
554
+ * previous setMode( 'color' | 'procedural' ): a new HDRI makes it
551
555
  * unreachable, so leaving it in place both leaks it and lets a setMode( 'hdri' )
552
556
  * arriving mid-download restore it over the map we are about to install.
553
557
  * @param {import('three').Texture} [incoming] - Never disposed, even if it is the stash.
@@ -576,38 +580,6 @@ export class EnvironmentManager {
576
580
 
577
581
  // ===== SKY GENERATORS =====
578
582
 
579
- /**
580
- * Generate gradient sky texture and set as environment.
581
- */
582
- async generateGradientTexture() {
583
-
584
- if ( ! this.simpleSkyRenderer ) {
585
-
586
- this.simpleSkyRenderer = new SimpleSky( 512, 256 );
587
-
588
- }
589
-
590
- const params = {
591
- zenithColor: this.envParams.gradientZenithColor,
592
- horizonColor: this.envParams.gradientHorizonColor,
593
- groundColor: this.envParams.gradientGroundColor,
594
- };
595
-
596
- try {
597
-
598
- const texture = this.simpleSkyRenderer.renderGradient( params );
599
- texture._isGeneratedProcedural = true;
600
- await this.setEnvironmentMap( texture );
601
- this.uniforms.set( 'hasSun', 0 );
602
-
603
- } catch ( error ) {
604
-
605
- log.error( 'gradient sky generation failed:', error );
606
-
607
- }
608
-
609
- }
610
-
611
583
  /**
612
584
  * Generate solid color sky texture and set as environment.
613
585
  */
@@ -645,6 +617,29 @@ export class EnvironmentManager {
645
617
  */
646
618
  generateProceduralSkyTexture() {
647
619
 
620
+ if ( ! this.ProceduralSky && this._loadProceduralSky ) {
621
+
622
+ this._proceduralSkyLoading ??= this._loadProceduralSky().then( ( Sky ) => {
623
+
624
+ this._proceduralSkyLoading = null;
625
+ if ( ! this.ProceduralSky ) this.ProceduralSky = Sky;
626
+
627
+ } );
628
+ return this._proceduralSkyLoading.then( () => this.generateProceduralSkyTexture() );
629
+
630
+ }
631
+
632
+ if ( ! this.ProceduralSky ) {
633
+
634
+ this.issues?.record(
635
+ ISSUE_CODES.CAPABILITY_MISSING,
636
+ 'procedural mode needs the physical sky: environmentManager.setProceduralSky( PhysicalSky ), from rayzee/addons/physical-sky',
637
+ { capability: 'physical-sky' }
638
+ );
639
+ return Promise.resolve();
640
+
641
+ }
642
+
648
643
  this._skyRequested = true;
649
644
  this._skyCaughtUp ??= new Promise( resolve => void ( this._resolveSky = resolve ) );
650
645
  this._pumpSky();
@@ -688,7 +683,7 @@ export class EnvironmentManager {
688
683
 
689
684
  }
690
685
 
691
- const sky = this.physicalSky ??= new PhysicalSky( SKY_WIDTH, SKY_HEIGHT );
686
+ const sky = this.physicalSky ??= new this.ProceduralSky( SKY_WIDTH, SKY_HEIGHT );
692
687
  const albedo = p.skyGroundAlbedo;
693
688
  const bake = ++ this._skyBakes;
694
689
  const latest = () => bake === this._skyBakes && ! this._skyRequested;
@@ -710,12 +705,12 @@ export class EnvironmentManager {
710
705
  this._installSky( texture, cdfTexture );
711
706
 
712
707
  // Frames until they land normalise by an older bake's.
713
- const { totalSum, compensationDelta } = await stats;
708
+ const { totalSum, radianceIntegral } = await stats;
714
709
  if ( p.mode === 'procedural' && sky === this.physicalSky && bake > this._skyStatsBake ) {
715
710
 
716
711
  this._skyStatsBake = bake;
717
712
  this.uniforms.set( 'envTotalSum', totalSum );
718
- this.uniforms.set( 'envCompensationDelta', compensationDelta );
713
+ if ( this._exactTable?.sky ) this._exactTable.radianceIntegral = radianceIntegral;
719
714
  if ( latest() ) this._notifyReset();
720
715
 
721
716
  }
@@ -736,6 +731,32 @@ export class EnvironmentManager {
736
731
 
737
732
  }
738
733
 
734
+ /**
735
+ * Installs the class that bakes 'procedural' mode — `PhysicalSky` from rayzee/addons/physical-sky: constructed with
736
+ * (width, height), `bake( renderer, params )` returns `{ texture, cdfTexture, sun, stats }`, and `dispose( renderer )`.
737
+ * @param {?Function} Sky
738
+ */
739
+ setProceduralSky( Sky ) {
740
+
741
+ this._loadProceduralSky = null;
742
+ if ( Sky === this.ProceduralSky ) return;
743
+ this._releaseSky();
744
+ this.ProceduralSky = Sky;
745
+ if ( Sky && this.envParams.mode === 'procedural' ) this.generateProceduralSkyTexture();
746
+
747
+ }
748
+
749
+ /**
750
+ * Installs the physical sky on first use: `load()` resolves to the class setProceduralSky() takes, the first time
751
+ * 'procedural' mode is baked.
752
+ * @param {function(): Promise<Function>} load
753
+ */
754
+ setProceduralSkyLoader( load ) {
755
+
756
+ this._loadProceduralSky = load;
757
+
758
+ }
759
+
739
760
  /** @private */
740
761
  _installSky( texture, cdfTexture ) {
741
762
 
@@ -750,6 +771,7 @@ export class EnvironmentManager {
750
771
 
751
772
  // So an HDRI coming back rebuilds its own table.
752
773
  this._cdfSignature = null;
774
+ if ( ! this._exactTable?.sky ) this._exactTable = { radianceIntegral: 0, sky: true };
753
775
  texture._isGeneratedProcedural = true;
754
776
  this.scene.environment = texture;
755
777
  this.environmentTexture = texture;
@@ -798,8 +820,8 @@ export class EnvironmentManager {
798
820
  /** @private */
799
821
  _calculateInitialSunDirection() {
800
822
 
801
- const azimuth = DEFAULT_STATE.skySunAzimuth * ( Math.PI / 180 );
802
- const elevation = DEFAULT_STATE.skySunElevation * ( Math.PI / 180 );
823
+ const azimuth = SKY_DEFAULTS.skySunAzimuth * ( Math.PI / 180 );
824
+ const elevation = SKY_DEFAULTS.skySunElevation * ( Math.PI / 180 );
803
825
  return new Vector3(
804
826
  Math.cos( elevation ) * Math.sin( azimuth ),
805
827
  Math.sin( elevation ),
@@ -71,6 +71,7 @@ export class GoboManager {
71
71
  cnv.width = width;
72
72
  cnv.height = height;
73
73
  const ctx = cnv.getContext( '2d', { willReadFrequently: true } );
74
+ let lastYield = performance.now();
74
75
 
75
76
  for ( let i = 0; i < depth; i ++ ) {
76
77
 
@@ -79,6 +80,14 @@ export class GoboManager {
79
80
  const img = ctx.getImageData( 0, 0, width, height );
80
81
  data.set( img.data, i * width * height * 4 );
81
82
 
83
+ // Each first draw decodes its image, so the library packs in short tasks.
84
+ if ( performance.now() - lastYield > 16 ) {
85
+
86
+ await new Promise( resolve => setTimeout( resolve, 0 ) );
87
+ lastYield = performance.now();
88
+
89
+ }
90
+
82
91
  }
83
92
 
84
93
  const tex = new DataArrayTexture( data, width, height, depth );
@@ -10,14 +10,15 @@
10
10
  import { StorageInstancedBufferAttribute } from 'three/webgpu';
11
11
  import { storage } from 'three/tsl';
12
12
  import {
13
- MATERIAL_DATA_LAYOUT as M, TRIANGLE_DATA_LAYOUT as T, normalizeAttenuationDistance,
14
- TRI_MATERIAL_MASK, TRI_SIDE_SHIFT, TRI_BLOCKER_SHIFT, shadowBlockerBits
15
- } from '../EngineDefaults.js';
13
+ TRIANGLE_DATA_LAYOUT as T, TRI_MATERIAL_MASK, TRI_SIDE_SHIFT, TRI_BLOCKER_SHIFT, shadowBlockerBits,
14
+ MATERIAL_DATA_LAYOUT as M, normalizeAttenuationDistance,
15
+ } from '../Processor/BufferLayout.js';
16
16
  import { packMaterial, UNIT_RANGE_PROPERTIES, clampUnit } from '../Processor/MaterialPacking.js';
17
17
  import { resolveMaterialTextures, MATERIAL_VALUE_SOURCE } from '../Processor/GeometryExtractor.js';
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();
@@ -672,43 +714,12 @@ export class MaterialDataManager {
672
714
 
673
715
  }
674
716
 
675
- /**
676
- * One side for several materials in a single pass over the triangles; per-material edits scan
677
- * every triangle once each.
678
- * @param {Iterable<number>} materialIndices
679
- * @param {number} side - 0 front, 1 back, 2 double, MATERIAL_SIDE_CUTAWAY
680
- */
681
- setMaterialsSide( materialIndices, side ) {
682
-
683
- if ( ! this.materialStorageAttr ) return;
684
-
685
- const indices = new Set( materialIndices );
686
- const data = this.materialStorageAttr.array;
687
- for ( const i of indices ) {
688
-
689
- data[ i * M.FLOATS_PER_MATERIAL + M.SIDE ] = side;
690
- ( this._hostSet[ i ] ??= new Map() ).set( 'side', side );
691
-
692
- }
693
-
694
- this._patchTriangleFlagForMaterials( indices, TRI_SIDE_SHIFT, 2, side );
695
- this.materialStorageAttr.needsUpdate = true;
696
- this._notifyReset();
697
-
698
- }
699
-
700
- _patchTriangleFlagForMaterial( materialIndex, shift, width, value ) {
701
-
702
- this._patchTriangleFlagForMaterials( new Set( [ materialIndex ] ), shift, width, value );
703
-
704
- }
705
-
706
717
  /**
707
718
  * Generic helper: rewrite `width` bits at `shift` in the flags lane of every triangle
708
- * whose materialIndex is in `indices`, then fire onTriangleDataChanged.
719
+ * whose materialIndex matches, then fire onTriangleDataChanged.
709
720
  * @private
710
721
  */
711
- _patchTriangleFlagForMaterials( indices, shift, width, value ) {
722
+ _patchTriangleFlagForMaterial( materialIndex, shift, width, value ) {
712
723
 
713
724
  const triInfo = this.callbacks.getTriangleData?.();
714
725
  // Chunked past the ~2 GB array cap; a flat array is the single-chunk case.
@@ -725,7 +736,7 @@ export class MaterialDataManager {
725
736
 
726
737
  const triData = records ? records.chunkFor( i ) : flat;
727
738
  const base = records ? records.baseOf( i ) : i * stride;
728
- if ( indices.has( triData[ base + TRI_FLAGS_OFFSET ] & TRI_MATERIAL_MASK ) ) {
739
+ if ( ( triData[ base + TRI_FLAGS_OFFSET ] & TRI_MATERIAL_MASK ) === materialIndex ) {
729
740
 
730
741
  triData[ base + TRI_FLAGS_OFFSET ] = ( ( triData[ base + TRI_FLAGS_OFFSET ] & ~ mask ) | bits ) >>> 0;
731
742
  patched ++;