rayzee 11.2.0 → 11.3.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 (63) hide show
  1. package/README.md +62 -33
  2. package/dist/ArchiveImporter-D735xX-4.js +7860 -0
  3. package/dist/BidirectionalIntegrator-M89c0AlP.js +954 -0
  4. package/dist/{BufferLayout-D_IBo2ci.js → BufferLayout-xPRH9jYD.js} +41 -5
  5. package/dist/{NeuralRetouch-DFGbnmLb.js → NeuralRetouch-DiBH63kz.js} +1 -1
  6. package/dist/{NeuralSuperRes-BtXvyJQl.js → NeuralSuperRes-CZNQP13W.js} +1 -1
  7. package/dist/{PathTracer-BHXGJtPN.js → PathTracer-BXCCVrPK.js} +4371 -4612
  8. package/dist/{PhysicalSky-DN6u8UX4.js → PhysicalSky-DqmaS0lb.js} +2 -2
  9. package/dist/{RayzeeRenderer-Dw1x8mVL.js → RayzeeRenderer-DUSMFhmc.js} +426 -363
  10. package/dist/SolarLimb-bZE6y63S.js +762 -0
  11. package/dist/addons/archives.es.js +1 -1
  12. package/dist/addons/bidirectional.es.js +1 -1
  13. package/dist/addons/physical-sky.es.js +1 -1
  14. package/dist/rayzee-core.es.js +7 -7
  15. package/dist/rayzee.es.js +472 -468
  16. package/dist/rayzee.umd.js +106 -105
  17. package/dist/sceneCachePolicy-BAib5fCp.js +1602 -0
  18. package/package.json +1 -1
  19. package/src/EngineDefaults.js +1 -1
  20. package/src/PathTracerApp.js +4 -3
  21. package/src/Processor/ArchiveImporter.js +173 -94
  22. package/src/Processor/ArchiveReader.js +20 -23
  23. package/src/Processor/AssetLoader.js +48 -1
  24. package/src/Processor/BVHRefitter.js +109 -13
  25. package/src/Processor/BufferLayout.js +115 -0
  26. package/src/Processor/ChunkedRecords.js +3 -2
  27. package/src/Processor/EmissiveTriangleBuilder.js +6 -6
  28. package/src/Processor/GeometryExtractor.js +189 -79
  29. package/src/Processor/HostMemory.js +39 -9
  30. package/src/Processor/InstanceTable.js +714 -42
  31. package/src/Processor/PBRT/PBRTCurves.js +224 -6
  32. package/src/Processor/PBRT/PBRTMaterials.js +3 -0
  33. package/src/Processor/PBRT/PBRTSceneBuilder.js +82 -25
  34. package/src/Processor/PackedRayBuffer.js +5 -2
  35. package/src/Processor/SceneProcessor.js +747 -141
  36. package/src/Processor/TLASBuilder.js +32 -17
  37. package/src/Processor/USD/USDLayer.js +1149 -0
  38. package/src/Processor/USD/USDScene.js +1655 -0
  39. package/src/Processor/USD/USDStage.js +682 -0
  40. package/src/Processor/USD/USDText.js +674 -0
  41. package/src/Processor/USD/index.js +138 -0
  42. package/src/Processor/Workers/BVHRefitWorker.js +3 -3
  43. package/src/RayzeeRenderer.js +168 -27
  44. package/src/Stages/NormalDepth.js +7 -12
  45. package/src/Stages/PathTracer.js +12 -17
  46. package/src/Stages/PathTracerStage.js +273 -96
  47. package/src/Storage/GeometrySpill.js +46 -14
  48. package/src/Storage/SceneGraphCodec.js +17 -3
  49. package/src/Storage/SpillStore.js +9 -0
  50. package/src/TSL/BVHTraversal.js +183 -7
  51. package/src/TSL/Common.js +57 -13
  52. package/src/TSL/ShadeKernel.js +3 -2
  53. package/src/TSL/TextureSampling.js +11 -8
  54. package/src/TSL/patches.js +84 -0
  55. package/src/integrators/BidirectionalIntegrator.js +20 -2
  56. package/src/managers/DenoisingManager.js +4 -4
  57. package/src/managers/InteractionManager.js +19 -6
  58. package/src/managers/MaterialDataManager.js +22 -5
  59. package/src/managers/UniformManager.js +3 -0
  60. package/dist/ArchiveImporter-eNlu0_6F.js +0 -5198
  61. package/dist/BidirectionalIntegrator-D1-6UySB.js +0 -946
  62. package/dist/SolarLimb-CPaeZVwz.js +0 -721
  63. package/dist/sceneCachePolicy-DA7iGoL4.js +0 -485
@@ -147,6 +147,90 @@ export function uploadStorageChunks( renderer, attr, chunks ) {
147
147
 
148
148
  }
149
149
 
150
+ /**
151
+ * GPU-only storage attributes standing for one array too large for a single binding: lane l lives in part
152
+ * ⌊l / lanesPerPart⌋, and a part holds whole records, so no record straddles two. Read through `splitStorage`
153
+ * (TSL/Common.js).
154
+ */
155
+ export class StorageParts {
156
+
157
+ /**
158
+ * @param {number} totalLanes
159
+ * @param {number} lanesPerRecord
160
+ * @param {number} maxPartBytes - at most maxStorageBufferBindingSize
161
+ * @param {Function} [LaneType]
162
+ * @param {number} [itemSize] - lanes an element: 4 for vec4 storage, 2 for vec2
163
+ */
164
+ constructor( totalLanes, lanesPerRecord, maxPartBytes, LaneType = Float32Array, itemSize = 4 ) {
165
+
166
+ this.LaneType = LaneType;
167
+ this.itemSize = itemSize;
168
+ this.lanesPerPart = Math.max( 1, Math.floor( maxPartBytes / ( lanesPerRecord * LaneType.BYTES_PER_ELEMENT ) ) ) * lanesPerRecord;
169
+ this.totalLanes = totalLanes;
170
+ this.attrs = [];
171
+ for ( let at = 0; at < totalLanes || this.attrs.length === 0; at += this.lanesPerPart ) {
172
+
173
+ const lanes = Math.max( lanesPerRecord, Math.min( this.lanesPerPart, totalLanes - at ) );
174
+ this.attrs.push( gpuOnlyStorageAttribute( lanes / itemSize, itemSize, LaneType ) );
175
+
176
+ }
177
+
178
+ }
179
+
180
+ /** Elements a full part holds. */
181
+ get elementsPerPart() {
182
+
183
+ return this.lanesPerPart / this.itemSize;
184
+
185
+ }
186
+
187
+ /** Creates every part's GPU buffer now. */
188
+ create( backend ) {
189
+
190
+ for ( const attr of this.attrs ) backend.createStorageAttribute( attr );
191
+
192
+ }
193
+
194
+ /** Writes `count` lanes of `src` from `srcOffset` to lane `lane`, across a part boundary if need be. */
195
+ write( backend, lane, src, srcOffset, count ) {
196
+
197
+ const bytes = this.LaneType.BYTES_PER_ELEMENT;
198
+ while ( count > 0 ) {
199
+
200
+ const p = Math.floor( lane / this.lanesPerPart );
201
+ const local = lane - p * this.lanesPerPart;
202
+ const n = Math.min( count, this.lanesPerPart - local );
203
+ const attr = this.attrs[ p ];
204
+ backend.createStorageAttribute( attr );
205
+ backend.device.queue.writeBuffer( backend.get( attr ).buffer, local * bytes, src, srcOffset, n );
206
+ lane += n;
207
+ srcOffset += n;
208
+ count -= n;
209
+
210
+ }
211
+
212
+ }
213
+
214
+ /** Writes lanes [startLane, startLane + laneCount) of chunked `records`. */
215
+ writeRecords( backend, records, startLane, laneCount ) {
216
+
217
+ const lanesPerChunk = records.recordsPerChunk * records.lanesPerRecord;
218
+ for ( let lane = startLane, end = startLane + laneCount; lane < end; ) {
219
+
220
+ const ci = Math.floor( lane / lanesPerChunk );
221
+ const chunk = records.chunks[ ci ];
222
+ if ( ! chunk ) throw new Error( `chunk ${ci} is on disk and was never uploaded` );
223
+ const local = lane - ci * lanesPerChunk;
224
+ const n = Math.min( chunk.length - local, end - lane );
225
+ this.write( backend, lane, chunk, local, n );
226
+ lane += n;
227
+
228
+ }
229
+
230
+ }
231
+
232
+ }
233
+
150
234
  /**
151
235
  * Re-upload one lane range of a chunked storage attribute, spanning chunks as needed.
152
236
  *
@@ -13,7 +13,7 @@ import { LIGHT_FLOATS } from '../managers/UniformManager.js';
13
13
  import { HIT_STRIDE_BIDIRECTIONAL, LIGHT_VERTEX_STRIDE, freeStorageAttribute } from '../Processor/PackedRayBuffer.js';
14
14
  import { COUNTER, ENERGY_SCALE } from '../Processor/QueueManager.js';
15
15
  import { CAMERA_PROJECTION_IDS } from '../EngineDefaults.js';
16
- import { BVH_MAX_INDEX } from '../Processor/BufferLayout.js';
16
+ import { BVH_MAX_INDEX, BVH_LEAF_MARKERS, CLUSTER_COUNT_SHIFT, CLUSTER_COPY_HIDDEN } from '../Processor/BufferLayout.js';
17
17
  import { BOUNCE_KERNELS, LIST_WG_SIZE } from '../Stages/PathTracer.js';
18
18
  import { buildLightGenerateKernel, LIGHT_GENERATE_WG_SIZE } from '../TSL/LightGenerateKernel.js';
19
19
  import { buildGuideKernel, buildGuideClearKernel, guidedDiscPdf, recordEscape, GUIDE_BINS, GUIDE_ROW_STRIDE, GUIDE_TEXTURE_WIDTH } from '../TSL/LightGuide.js';
@@ -652,6 +652,14 @@ export class BidirectionalIntegrator {
652
652
  const records = this.pt._bvhRecords;
653
653
  const flat = records ? null : this.pt.bvhStorageAttr?.array;
654
654
  if ( ! records && ! ( flat?.length >= 16 ) ) return null;
655
+ // A spilled TLAS comes back for the next frame's disc.
656
+ const blasBase = this.pt._instanceTable?.blasBase ?? 1;
657
+ if ( records && ! records.isResident( 0, blasBase ) ) {
658
+
659
+ this.pt._whenTLASResident( () => {} );
660
+ return null;
661
+
662
+ }
655
663
 
656
664
  const view = ( f ) => {
657
665
 
@@ -668,6 +676,16 @@ export class BidirectionalIntegrator {
668
676
 
669
677
  };
670
678
 
679
+ // A copy cluster is visible while any of its copies is (BufferLayout CLUSTER_LEAF).
680
+ const leafVisible = ( n ) => {
681
+
682
+ if ( n.u[ n.o + 3 ] !== BVH_LEAF_MARKERS.CLUSTER_LEAF ) return n.f[ n.o + 2 ] !== 0;
683
+ const count = ( ( n.u[ n.o ] >>> CLUSTER_COUNT_SHIFT ) & 3 ) + 1;
684
+ for ( let k = 0; k < count; k ++ ) if ( ! ( n.u[ n.o + 12 + k ] & CLUSTER_COPY_HIDDEN ) ) return true;
685
+ return false;
686
+
687
+ };
688
+
671
689
  const root = node( 0 );
672
690
  if ( root.u[ root.o + 3 ] >= BVH_MAX_INDEX ) return null;
673
691
  // A tree that reaches a node twice is malformed; no search visits more nodes than the tree holds.
@@ -699,7 +717,7 @@ export class BidirectionalIntegrator {
699
717
  const n = node( index );
700
718
  if ( n.u[ n.o + 3 ] >= BVH_MAX_INDEX ) {
701
719
 
702
- if ( n.f[ n.o + 2 ] !== 0 ) best = s;
720
+ if ( leafVisible( n ) ) best = s;
703
721
 
704
722
  } else push( n );
705
723
 
@@ -988,10 +988,10 @@ export class DenoisingManager extends EventDispatcher {
988
988
  * @param {Float32Array} world - the placement matrix pool
989
989
  * @param {number} offset - where this placement's matrix starts in it
990
990
  */
991
- notePlacementMoving( leaf, world, offset ) {
991
+ notePlacementMoving( leaf, matrixOf ) {
992
992
 
993
993
  if ( leaf < 0 || ! this.historyActive || this._movedPlacements.has( leaf ) ) return;
994
- this._movedPlacements.set( leaf, { world, offset, prev: world.slice( offset, offset + 16 ) } );
994
+ this._movedPlacements.set( leaf, { matrixOf, prev: matrixOf().slice() } );
995
995
 
996
996
  }
997
997
 
@@ -1013,8 +1013,8 @@ export class DenoisingManager extends EventDispatcher {
1013
1013
  const leaves = [ ...moved.keys() ].sort( ( a, b ) => a - b );
1014
1014
  for ( let i = 0; i < leaves.length; i ++ ) {
1015
1015
 
1016
- const { world, offset, prev } = moved.get( leaves[ i ] );
1017
- this._matB.fromArray( world, offset ).invert();
1016
+ const { matrixOf, prev } = moved.get( leaves[ i ] );
1017
+ this._matB.fromArray( matrixOf() ).invert();
1018
1018
  this._matA.fromArray( prev ).multiply( this._matB ).toArray( upload.toPrev, i * 16 );
1019
1019
  upload.leaves[ i ] = leaves[ i ];
1020
1020
 
@@ -38,12 +38,13 @@ export function focusDistanceOfHit( hit, camera, panorama = false ) {
38
38
  */
39
39
  export class InteractionManager extends EventDispatcher {
40
40
 
41
- constructor( { scene, camera, canvas, assetLoader, pathTracer, floorPlane } ) {
41
+ constructor( { scene, camera, canvas, assetLoader, pathTracer, floorPlane, isGeometryOnDisk = null } ) {
42
42
 
43
43
  super();
44
44
 
45
45
  // Core dependencies
46
46
  this.scene = scene;
47
+ this.isGeometryOnDisk = isGeometryOnDisk;
47
48
  this.camera = camera;
48
49
  this.canvas = canvas;
49
50
  this.assetLoader = assetLoader;
@@ -125,7 +126,8 @@ export class InteractionManager extends EventDispatcher {
125
126
  select( object ) {
126
127
 
127
128
  const outline = this._overlayManager?.getHelper( 'outline' );
128
- if ( outline ) outline.setSelectedObjects( object ? [ object ] : [] );
129
+ // A spilled model's geometry is on disk: there is nothing to draw the outline from.
130
+ if ( outline ) outline.setSelectedObjects( object && ! this.isGeometryOnDisk?.() ? [ object ] : [] );
129
131
 
130
132
  this.selectedObject = object || null;
131
133
 
@@ -221,7 +223,7 @@ export class InteractionManager extends EventDispatcher {
221
223
  const mouseCoords = this.getMouseCoordinates( event );
222
224
  this.raycaster.setFromCamera( mouseCoords, this.camera );
223
225
 
224
- const intersects = this.raycaster.intersectObjects( this.scene.children, true );
226
+ const intersects = this._intersectScene();
225
227
 
226
228
  if ( intersects.length > 0 ) {
227
229
 
@@ -490,7 +492,7 @@ export class InteractionManager extends EventDispatcher {
490
492
  const mouseCoords = this.getMouseCoordinates( event );
491
493
  this.raycaster.setFromCamera( mouseCoords, this.camera );
492
494
 
493
- const intersects = this.raycaster.intersectObjects( this.scene.children, true );
495
+ const intersects = this._intersectScene();
494
496
  const validIntersects = this.filterValidIntersects( intersects );
495
497
  const lightHit = this._pickLightHelper();
496
498
 
@@ -576,7 +578,7 @@ export class InteractionManager extends EventDispatcher {
576
578
  const mouseCoords = this.getMouseCoordinates( event );
577
579
  this.raycaster.setFromCamera( mouseCoords, this.camera );
578
580
 
579
- const intersects = this.raycaster.intersectObjects( this.scene.children, true );
581
+ const intersects = this._intersectScene();
580
582
  const validIntersects = this.filterValidIntersects( intersects );
581
583
 
582
584
  if ( validIntersects.length > 0 ) {
@@ -695,11 +697,22 @@ export class InteractionManager extends EventDispatcher {
695
697
 
696
698
  }
697
699
 
700
+ /**
701
+ * The scene under the raycaster. Nothing while a spilled model's geometry is on disk: its arrays are empty, and
702
+ * testing tens of millions of empty copies held the page for seconds a pick.
703
+ * @private
704
+ */
705
+ _intersectScene() {
706
+
707
+ return this.isGeometryOnDisk?.() ? [] : this.raycaster.intersectObjects( this.scene.children, true );
708
+
709
+ }
710
+
698
711
  /** The nearest scene surface under an NDC point, skipping the floor and helpers. */
699
712
  pickSurface( ndcX, ndcY ) {
700
713
 
701
714
  this.raycaster.setFromCamera( { x: ndcX, y: ndcY }, this.camera );
702
- return this.filterValidIntersects( this.raycaster.intersectObjects( this.scene.children, true ) )[ 0 ] ?? null;
715
+ return this.filterValidIntersects( this._intersectScene() )[ 0 ] ?? null;
703
716
 
704
717
  }
705
718
 
@@ -173,8 +173,17 @@ export class MaterialDataManager {
173
173
  this._convertColorsToWorkingSpace( matImageData );
174
174
 
175
175
  const vec4Count = matImageData.length / 4;
176
+ this.materialCount = Math.floor( vec4Count / PIXELS_PER_MATERIAL );
177
+
178
+ // The owning stage keeps materials at the front of its scene data buffer, the light data behind them: one
179
+ // binding for both, which the shading kernel's ten could not spare.
180
+ const shared = this.callbacks.adoptMaterials?.( matImageData );
181
+ if ( shared ) {
176
182
 
177
- if ( this.materialStorageNode ) {
183
+ this.materialStorageAttr = shared.attr;
184
+ this.materialStorageNode = shared.node;
185
+
186
+ } else if ( this.materialStorageNode ) {
178
187
 
179
188
  this.materialStorageAttr = new StorageInstancedBufferAttribute( matImageData, 4 );
180
189
  this.materialStorageNode.value = this.materialStorageAttr;
@@ -187,7 +196,6 @@ export class MaterialDataManager {
187
196
 
188
197
  }
189
198
 
190
- this.materialCount = Math.floor( vec4Count / PIXELS_PER_MATERIAL );
191
199
  this._sources = [ ...sources ];
192
200
  this._hostSet = [];
193
201
  this.callbacks.onMaterialFeaturesChanged?.();
@@ -195,6 +203,15 @@ export class MaterialDataManager {
195
203
 
196
204
  }
197
205
 
206
+ /** Sends the materials to the GPU: only their region when the buffer carries the light data too. @private */
207
+ _uploadMaterials() {
208
+
209
+ const attr = this.materialStorageAttr;
210
+ if ( this.callbacks.adoptMaterials ) attr.addUpdateRange( 0, this.materialCount * PIXELS_PER_MATERIAL * 4 );
211
+ attr.needsUpdate = true;
212
+
213
+ }
214
+
198
215
  /**
199
216
  * Get the material storage attribute (for dependent stages).
200
217
  * @returns {StorageInstancedBufferAttribute|null}
@@ -538,7 +555,7 @@ export class MaterialDataManager {
538
555
 
539
556
  }
540
557
 
541
- this.materialStorageAttr.needsUpdate = true;
558
+ this._uploadMaterials();
542
559
  ( this._hostSet[ materialIndex ] ??= new Map() ).set( property, toPortable( value ) );
543
560
 
544
561
  // Recompute triangle-data opaque-blocker flag when any input to it changes.
@@ -588,7 +605,7 @@ export class MaterialDataManager {
588
605
  this._recomputeOpaqueBlockerForMaterial( materialIndex );
589
606
  this.callbacks.onMaterialFeaturesChanged?.();
590
607
 
591
- this.materialStorageAttr.needsUpdate = true;
608
+ this._uploadMaterials();
592
609
  this._notifyReset();
593
610
 
594
611
  }
@@ -668,7 +685,7 @@ export class MaterialDataManager {
668
685
 
669
686
  }
670
687
 
671
- this.materialStorageAttr.needsUpdate = true;
688
+ this._uploadMaterials();
672
689
  this._notifyReset();
673
690
 
674
691
  }
@@ -333,6 +333,9 @@ export class UniformManager {
333
333
  u( 'emissiveTriangleCount', 0, 'int' );
334
334
  u( 'emissiveTotalPower', 0.0, 'float' );
335
335
  u( 'lightBVHNodeCount', 0, 'int' );
336
+ // Where the light data starts in the scene data buffer, after the materials (vec4 elements). Every light read
337
+ // adds it; the offsets below are within the light data.
338
+ u( 'lightVec4Offset', 0, 'int' );
336
339
  // Offset (in vec4 elements) within the packed light buffer where emissive
337
340
  // triangle data starts. Equals lightBVHNodeCount * LBVH_STRIDE; computed on upload.
338
341
  u( 'emissiveVec4Offset', 0, 'int' );