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
@@ -10,7 +10,10 @@
10
10
  */
11
11
 
12
12
  import { isFoldedRef, foldedFirst, foldedCount } from './BVHLeafFold.js';
13
- import { TRIANGLE_DATA_LAYOUT, packNormalOct, BVH_LEAF_MARKERS } from './BufferLayout.js';
13
+ import {
14
+ TRIANGLE_DATA_LAYOUT, packNormalOct, BVH_LEAF_MARKERS, BVH_EMPTY_BOX,
15
+ CLUSTER_FIRST_MASK, CLUSTER_COUNT_SHIFT, CLUSTER_ROOT_MASK, CLUSTER_SIZE, RECORD_VEC4, TLAS_LEAF_IDENTITY, encodeClusterBoxes,
16
+ } from './BufferLayout.js';
14
17
 
15
18
  const FPT = TRIANGLE_DATA_LAYOUT.FLOATS_PER_TRIANGLE;
16
19
 
@@ -54,6 +57,7 @@ const triAccess = ( triangleData ) => {
54
57
  const FLOATS_PER_NODE = 16; // 4 vec4s per BVH node
55
58
  const LEAF_MARKER = BVH_LEAF_MARKERS.TRIANGLE_LEAF;
56
59
  const BLAS_POINTER_MARKER = BVH_LEAF_MARKERS.BLAS_POINTER_LEAF;
60
+ const CLUSTER_MARKER = BVH_LEAF_MARKERS.CLUSTER_LEAF;
57
61
 
58
62
  // Relative to the matrix's own magnitude, never an absolute floor — see transformBoundsToWorld.
59
63
  const SINGULAR_REL_EPS = 1e-12;
@@ -64,6 +68,13 @@ const SINGULAR_REL_EPS = 1e-12;
64
68
  */
65
69
  function transformBoundsToWorld( bvhData, nodeOff, src, srcOff, dst, dstOff ) {
66
70
 
71
+ if ( src[ srcOff ] > src[ srcOff + 3 ] ) {
72
+
73
+ for ( let i = 0; i < 6; i ++ ) dst[ dstOff + i ] = src[ srcOff + i ];
74
+ return;
75
+
76
+ }
77
+
67
78
  const a0 = bvhData[ nodeOff + 4 ], a1 = bvhData[ nodeOff + 5 ], a2 = bvhData[ nodeOff + 6 ], tx = bvhData[ nodeOff + 7 ];
68
79
  const a3 = bvhData[ nodeOff + 8 ], a4 = bvhData[ nodeOff + 9 ], a5 = bvhData[ nodeOff + 10 ], ty = bvhData[ nodeOff + 11 ];
69
80
  const a6 = bvhData[ nodeOff + 12 ], a7 = bvhData[ nodeOff + 13 ], a8 = bvhData[ nodeOff + 14 ], tz = bvhData[ nodeOff + 15 ];
@@ -151,6 +162,19 @@ function triangleBounds( acc, first, count, out, off ) {
151
162
 
152
163
  const folded = new Float32Array( 12 );
153
164
 
165
+ // Empty bounds (a hidden group member, an empty leaf) are min > max while computed — neutral in a union — and stored
166
+ // as the far point box no ray enters, read back as empty.
167
+ const isEmptyStored = ( f, o ) => f[ o ] >= BVH_EMPTY_BOX * 0.5;
168
+
169
+ /** A child's computed box into its slot at `o`: as it is, or the far point box when empty. */
170
+ function writeChildSlot( f, o, src, s ) {
171
+
172
+ const empty = src[ s ] > src[ s + 3 ];
173
+ f[ o ] = empty ? BVH_EMPTY_BOX : src[ s ]; f[ o + 1 ] = empty ? BVH_EMPTY_BOX : src[ s + 1 ]; f[ o + 2 ] = empty ? BVH_EMPTY_BOX : src[ s + 2 ];
174
+ f[ o + 4 ] = empty ? BVH_EMPTY_BOX : src[ s + 3 ]; f[ o + 5 ] = empty ? BVH_EMPTY_BOX : src[ s + 4 ]; f[ o + 6 ] = empty ? BVH_EMPTY_BOX : src[ s + 5 ];
175
+
176
+ }
177
+
154
178
  /** A node's own box from what it stores — its two child boxes, or a leaf's triangles — into out[ 0 .. 6 ). */
155
179
  function storedBounds( nodes, n, acc, out ) {
156
180
 
@@ -165,13 +189,56 @@ function storedBounds( nodes, n, acc, out ) {
165
189
 
166
190
  }
167
191
 
192
+ const left = ! isEmptyStored( f, o ), right = ! isEmptyStored( f, o + 8 );
168
193
  for ( let a = 0; a < 3; a ++ ) {
169
194
 
170
- out[ a ] = Math.min( f[ o + a ], f[ o + 8 + a ] );
171
- out[ 3 + a ] = Math.max( f[ o + 4 + a ], f[ o + 12 + a ] );
195
+ out[ a ] = Math.min( left ? f[ o + a ] : Infinity, right ? f[ o + 8 + a ] : Infinity );
196
+ out[ 3 + a ] = Math.max( left ? f[ o + 4 + a ] : - Infinity, right ? f[ o + 12 + a ] : - Infinity );
197
+
198
+ }
199
+
200
+ }
201
+
202
+ const recordRows = new Float32Array( 16 );
203
+ const rootBox = new Float32Array( 6 );
204
+ const copyBoxes = new Float32Array( CLUSTER_SIZE * 6 );
205
+
206
+ /**
207
+ * A cluster leaf's boxes from its copies now: each copy's root's stored box carried to world through its record
208
+ * (behind the BLASes from node `recordNodeStart`), encoded into the leaf; the union into `out` at `outOff`.
209
+ */
210
+ function refitClusterLeaf( nodes, i, acc, recordNodeStart, out, outOff ) {
211
+
212
+ const f = nodeF( nodes, i ), idx = nodeIdx( nodes, i ), o = nodeBase( nodes, i );
213
+ const first = idx[ o ] & CLUSTER_FIRST_MASK;
214
+ const count = ( ( idx[ o ] >>> CLUSTER_COUNT_SHIFT ) & 3 ) + 1;
215
+
216
+ for ( let k = 0; k < count; k ++ ) {
217
+
218
+ const word = idx[ o + 12 + k ];
219
+ storedBounds( nodes, word & CLUSTER_ROOT_MASK, acc, rootBox );
220
+ if ( word & TLAS_LEAF_IDENTITY ) {
221
+
222
+ for ( let a = 0; a < 6; a ++ ) copyBoxes[ k * 6 + a ] = rootBox[ a ];
223
+ continue;
224
+
225
+ }
226
+
227
+ // A record's 12 floats may straddle a node, and so a chunk.
228
+ const lane = recordNodeStart * 16 + ( first + k ) * RECORD_VEC4 * 4;
229
+ for ( let l = 0; l < 12; l ++ ) {
230
+
231
+ const n = ( lane + l ) >> 4;
232
+ recordRows[ 4 + l ] = nodeF( nodes, n )[ nodeBase( nodes, n ) + ( ( lane + l ) & 15 ) ];
233
+
234
+ }
235
+
236
+ transformBoundsToWorld( recordRows, 0, rootBox, 0, copyBoxes, k * 6 );
172
237
 
173
238
  }
174
239
 
240
+ encodeClusterBoxes( f, idx, o, copyBoxes, count, out, outOff );
241
+
175
242
  }
176
243
 
177
244
  /**
@@ -179,7 +246,7 @@ function storedBounds( nodes, n, acc, out ) {
179
246
  * node's two child boxes rewritten from theirs. A leaf folded into its parent takes its box from
180
247
  * its triangles.
181
248
  */
182
- function refitNode( nodes, i, acc, bounds, base ) {
249
+ function refitNode( nodes, i, acc, bounds, base, recordNodeStart = - 1 ) {
183
250
 
184
251
  const bvhF = nodeF( nodes, i );
185
252
  const idx = nodeIdx( nodes, i );
@@ -194,6 +261,13 @@ function refitNode( nodes, i, acc, bounds, base ) {
194
261
 
195
262
  }
196
263
 
264
+ if ( marker === CLUSTER_MARKER ) {
265
+
266
+ refitClusterLeaf( nodes, i, acc, recordNodeStart, bounds, b );
267
+ return;
268
+
269
+ }
270
+
197
271
  if ( marker === BLAS_POINTER_MARKER ) {
198
272
 
199
273
  // BLAS-pointer leaf (TLAS): the BLAS root's bounds are in the instance's own
@@ -222,10 +296,8 @@ function refitNode( nodes, i, acc, bounds, base ) {
222
296
 
223
297
  }
224
298
 
225
- bvhF[ o ] = ls[ lb ]; bvhF[ o + 1 ] = ls[ lb + 1 ]; bvhF[ o + 2 ] = ls[ lb + 2 ];
226
- bvhF[ o + 4 ] = ls[ lb + 3 ]; bvhF[ o + 5 ] = ls[ lb + 4 ]; bvhF[ o + 6 ] = ls[ lb + 5 ];
227
- bvhF[ o + 8 ] = rs[ rb ]; bvhF[ o + 9 ] = rs[ rb + 1 ]; bvhF[ o + 10 ] = rs[ rb + 2 ];
228
- bvhF[ o + 12 ] = rs[ rb + 3 ]; bvhF[ o + 13 ] = rs[ rb + 4 ]; bvhF[ o + 14 ] = rs[ rb + 5 ];
299
+ writeChildSlot( bvhF, o, ls, lb );
300
+ writeChildSlot( bvhF, o + 8, rs, rb );
229
301
 
230
302
  for ( let a = 0; a < 3; a ++ ) {
231
303
 
@@ -346,15 +418,15 @@ export class BVHRefitter {
346
418
  * @param {ArrayLike<number>} blasRanges - flat [ startNode, nodeCount, ... ]
347
419
  * @param {number} tlasNodeCount - the TLAS occupies nodes [0, tlasNodeCount)
348
420
  */
349
- refitPartial( bvhData, triangleData, blasRanges, tlasNodeCount ) {
421
+ refitPartial( bvhData, triangleData, blasRanges, tlasNodeCount, groupRange = null, recordNodeStart = - 1 ) {
350
422
 
351
423
  for ( let r = 0; r < blasRanges.length; r += 2 ) this.refitRange( bvhData, triangleData, blasRanges[ r ], blasRanges[ r + 1 ] );
352
424
 
353
425
  const acc = triAccess( triangleData );
354
426
  const nodes = nodeAccess( bvhData );
427
+ if ( groupRange ) this.refitGroups( nodes, acc, groupRange[ 0 ], groupRange[ 1 ] );
355
428
  if ( ! this._tlasBounds || this._tlasBounds.length < tlasNodeCount * 6 ) this._tlasBounds = new Float32Array( tlasNodeCount * 6 );
356
429
  const bounds = this._tlasBounds;
357
- const rootBox = this._rootBox ||= new Float32Array( 6 );
358
430
 
359
431
  for ( let i = tlasNodeCount - 1; i >= 0; i -- ) {
360
432
 
@@ -368,7 +440,7 @@ export class BVHRefitter {
368
440
 
369
441
  } else {
370
442
 
371
- refitNode( nodes, i, acc, bounds, 0 );
443
+ refitNode( nodes, i, acc, bounds, 0, recordNodeStart );
372
444
 
373
445
  }
374
446
 
@@ -376,6 +448,30 @@ export class BVHRefitter {
376
448
 
377
449
  }
378
450
 
451
+ /**
452
+ * Group trees between the TLAS and the BLASes: each inner node's child boxes from what its children store, last
453
+ * node first, so a child group node is current before its parent reads it. The empty leaf is left alone.
454
+ * @private
455
+ */
456
+ refitGroups( nodes, acc, start, count ) {
457
+
458
+ const box = this._groupBox ||= new Float32Array( 6 );
459
+ for ( let i = start + count - 1; i >= start; i -- ) {
460
+
461
+ const f = nodeF( nodes, i );
462
+ const idx = nodeIdx( nodes, i );
463
+ const o = nodeBase( nodes, i );
464
+ if ( idx[ o + 3 ] === LEAF_MARKER ) continue;
465
+
466
+ storedBounds( nodes, idx[ o + 3 ], acc, box );
467
+ writeChildSlot( f, o, box, 0 );
468
+ storedBounds( nodes, idx[ o + 7 ], acc, box );
469
+ writeChildSlot( f, o + 8, box, 0 );
470
+
471
+ }
472
+
473
+ }
474
+
379
475
  /**
380
476
  * Bottom-up refit of all BVH node AABBs.
381
477
  * Reverse pre-order iteration gives valid bottom-up order (children have higher
@@ -385,7 +481,7 @@ export class BVHRefitter {
385
481
  * @param {Uint32Array} triangleData - Updated triangle records
386
482
  * @param {number} nodeCount - Total number of BVH nodes
387
483
  */
388
- refit( bvhData, triangleData, nodeCount ) {
484
+ refit( bvhData, triangleData, nodeCount, recordNodeStart = - 1 ) {
389
485
 
390
486
  const acc = triAccess( triangleData );
391
487
 
@@ -398,7 +494,7 @@ export class BVHRefitter {
398
494
  }
399
495
 
400
496
  const nodes = nodeAccess( bvhData );
401
- for ( let i = nodeCount - 1; i >= 0; i -- ) refitNode( nodes, i, acc, this._bounds, 0 );
497
+ for ( let i = nodeCount - 1; i >= 0; i -- ) refitNode( nodes, i, acc, this._bounds, 0, recordNodeStart );
402
498
 
403
499
  }
404
500
 
@@ -244,8 +244,116 @@ export const BVH_LEAF_MARKERS = {
244
244
  TRIANGLE_LEAF: 0x40000000, // leaf containing triangle references
245
245
  BLAS_POINTER_LEAF: 0x40000001, // TLAS leaf pointing to a BLAS root node
246
246
  FRONTIER: 0x40000002, // parallel-build placeholder, overwritten during assembly
247
+ CLUSTER_LEAF: 0x40000003, // TLAS leaf over up to CLUSTER_SIZE copies of one object (copy clusters)
247
248
  };
248
249
 
250
+ /**
251
+ * Copy clusters, for a TLAS over millions of copies: a leaf holds up to CLUSTER_SIZE neighbouring copies, and each
252
+ * copy's world-to-object rows are a record of RECORD_VEC4 vec4s behind the BLASes (48 B, against 64 B of leaf node and
253
+ * 64 B of inner node a copy without them). Leaf slots:
254
+ * 0 first record | ( count − 1 ) << CLUSTER_COUNT_SHIFT; copy k's record is first + k
255
+ * 1 byte a: the f32 exponent field of axis a's step, a power of two
256
+ * 2, 4, 5 the union box's min (x, y, z)
257
+ * 3 CLUSTER_LEAF
258
+ * 6–8 copy k's box min on axis 0–2, in steps from the union's min, byte k
259
+ * 9–11 the same for its max
260
+ * 12 + k copy k's object root | TLAS_LEAF_IDENTITY (its record is identity) | CLUSTER_COPY_HIDDEN
261
+ * A traversal stack entry `COPY_ENTRY | leaf << 2 | k` enters copy k: node indices stay below it, folded leaves start at
262
+ * 2^31, so a leaf index and a record index must each stay below 2^28.
263
+ */
264
+ export const CLUSTER_SIZE = 4;
265
+ export const CLUSTER_FIRST_MASK = 0x0fffffff;
266
+ export const CLUSTER_COUNT_SHIFT = 28;
267
+ export const CLUSTER_ROOT_MASK = 0x3fffffff;
268
+ export const CLUSTER_COPY_HIDDEN = 0x80000000;
269
+ export const COPY_ENTRY = 0x40000000;
270
+ export const RECORD_VEC4 = 3;
271
+
272
+ const _f32 = new Float32Array( 1 );
273
+ const _u32 = new Uint32Array( _f32.buffer );
274
+
275
+ /** The f32 nearest `x` that is not above it. */
276
+ function f32Down( x ) {
277
+
278
+ const f = Math.fround( x );
279
+ if ( f <= x ) return f;
280
+ _f32[ 0 ] = f;
281
+ _u32[ 0 ] += f > 0 ? - 1 : 1;
282
+ return _f32[ 0 ];
283
+
284
+ }
285
+
286
+ /** Four f32 steps at the larger magnitude of `a` and `b`: what an f32 sum near them can be off by, with room. */
287
+ function f32Pad( a, b ) {
288
+
289
+ return Math.max( Math.abs( a ), Math.abs( b ), 1e-30 ) * 2 ** - 21;
290
+
291
+ }
292
+
293
+ const BASE_SLOT = [ 2, 4, 5 ];
294
+
295
+ /**
296
+ * A cluster leaf's boxes (slots 1, 2 and 4–11 at float offset `o`): the union of `count` copies' world boxes (`boxes`,
297
+ * 6 a copy), then each as bytes of it. Everything is rounded outward by a few f32 steps of its own size, so the GPU's
298
+ * f32 decode never culls a copy from a ray that reaches it — far from the origin a byte step can be finer than one f32
299
+ * step. The exact union goes to `out` at `outOff`.
300
+ */
301
+ export function encodeClusterBoxes( data, idx, o, boxes, count, out, outOff ) {
302
+
303
+ let exponents = 0;
304
+ for ( let a = 0; a < 3; a ++ ) {
305
+
306
+ let lo = Infinity, hi = - Infinity;
307
+ for ( let k = 0; k < count; k ++ ) {
308
+
309
+ lo = Math.min( lo, boxes[ k * 6 + a ] );
310
+ hi = Math.max( hi, boxes[ k * 6 + 3 + a ] );
311
+
312
+ }
313
+
314
+ if ( out ) {
315
+
316
+ out[ outOff + a ] = lo;
317
+ out[ outOff + 3 + a ] = hi;
318
+
319
+ }
320
+
321
+ // Every copy hidden (a group's members all are): the far point box no ray enters.
322
+ if ( ! ( lo <= hi ) ) {
323
+
324
+ data[ o + BASE_SLOT[ a ] ] = BVH_EMPTY_BOX;
325
+ idx[ o + 6 + a ] = 0;
326
+ idx[ o + 9 + a ] = 0;
327
+ continue;
328
+
329
+ }
330
+
331
+ const pad = f32Pad( lo, hi );
332
+ const base = f32Down( lo - pad );
333
+ let e = Math.max( - 126, Math.ceil( Math.log2( ( hi + pad - base ) / 255 ) ) );
334
+ while ( Math.fround( base + 255 * 2 ** e ) < hi + pad ) e ++;
335
+ const step = 2 ** e;
336
+ data[ o + BASE_SLOT[ a ] ] = base;
337
+ exponents |= ( e + 127 ) << ( a * 8 );
338
+ let qLo = 0, qHi = 0;
339
+ for ( let k = 0; k < count; k ++ ) {
340
+
341
+ const kLo = boxes[ k * 6 + a ], kHi = boxes[ k * 6 + 3 + a ];
342
+ const kPad = f32Pad( kLo, kHi );
343
+ qLo |= Math.max( 0, Math.min( 255, Math.floor( ( kLo - kPad - base ) / step ) ) ) << ( k * 8 );
344
+ qHi |= Math.max( 0, Math.min( 255, Math.ceil( ( kHi + kPad - base ) / step ) ) ) << ( k * 8 );
345
+
346
+ }
347
+
348
+ idx[ o + 6 + a ] = qLo >>> 0;
349
+ idx[ o + 9 + a ] = qHi >>> 0;
350
+
351
+ }
352
+
353
+ idx[ o + 1 ] = exponents >>> 0;
354
+
355
+ }
356
+
249
357
  /**
250
358
  * A triangle leaf of at most this many triangles is folded into its parent: the child's slot ([3]
251
359
  * or [7]) holds `~( first << 4 | count )`, the very value traversal pushes, so an inner node is
@@ -256,6 +364,13 @@ export const BVH_LEAF_MARKERS = {
256
364
  export const BVH_FOLDED_LEAF_MAX = 15;
257
365
  export const BVH_FOLDED_FIRST_LIMIT = 1 << 27;
258
366
 
367
+ /**
368
+ * A child no ray may enter — a hidden part of a grouped object — is stored as a point box this far out: its distance
369
+ * along any ray exceeds every hit, so it is never pushed. Refits read it back as empty. An inverted box (min > max)
370
+ * will not do: the slab test swaps the planes and the ray enters it everywhere.
371
+ */
372
+ export const BVH_EMPTY_BOX = 1e30;
373
+
259
374
  /** A u32 view over a float buffer, for writing index fields as exact bit patterns. */
260
375
  export function bvhIndexView( f32 ) {
261
376
 
@@ -241,9 +241,10 @@ export class ChunkedRecords {
241
241
 
242
242
  if ( ! this._spilled.has( k ) ) continue;
243
243
  const lanes = Math.min( this.recordsPerChunk, this.recordCount - k * this.recordsPerChunk ) * this.lanesPerRecord;
244
- const bytes = await this._store.read( k, lanes * this.LaneType.BYTES_PER_ELEMENT );
245
244
  const chunk = allocChunk( this.LaneType, lanes, this.shared );
246
- new Uint8Array( chunk.buffer, chunk.byteOffset, chunk.byteLength ).set( new Uint8Array( bytes ) );
245
+ const target = new Uint8Array( chunk.buffer, chunk.byteOffset, chunk.byteLength );
246
+ if ( this._store.readInto ) await this._store.readInto( k, target );
247
+ else target.set( new Uint8Array( await this._store.read( k, target.byteLength ) ) );
247
248
  this.chunks[ k ] = chunk;
248
249
  for ( const v of this._liveViews() ) v.chunks[ k ] = new v.LaneType( chunk.buffer, chunk.byteOffset, chunk.byteLength / v.LaneType.BYTES_PER_ELEMENT );
249
250
  this._spilled.delete( k );
@@ -132,8 +132,8 @@ export class EmissiveTriangleBuilder {
132
132
  // another object's light as soon as any instanced mesh came earlier.
133
133
  const placement = table?.placementRunOf?.( meshIndex )?.start ?? - 1;
134
134
  const hasInstance = placement >= 0 && table.isSet[ placement ];
135
- const m = hasInstance ? table.world : null;
136
- const mo = hasInstance ? placement * 16 : 0;
135
+ const mo = hasInstance ? table.matrixRow( placement ) : 0;
136
+ const m = hasInstance ? table.rowArray : null;
137
137
  const px = ( x, y, z ) => ( m ? m[ mo ] * x + m[ mo + 4 ] * y + m[ mo + 8 ] * z + m[ mo + 12 ] : x );
138
138
  const py = ( x, y, z ) => ( m ? m[ mo + 1 ] * x + m[ mo + 5 ] * y + m[ mo + 9 ] * z + m[ mo + 13 ] : y );
139
139
  const pz = ( x, y, z ) => ( m ? m[ mo + 2 ] * x + m[ mo + 6 ] * y + m[ mo + 10 ] * z + m[ mo + 14 ] : z );
@@ -231,8 +231,6 @@ export class EmissiveTriangleBuilder {
231
231
  const chunkedF = chunked ? chunked.viewAs( Float32Array ) : null;
232
232
  const flatF = chunked ? null : new Float32Array( triangleData.buffer, triangleData.byteOffset, triangleData.length );
233
233
  const FLOATS_PER_TRIANGLE = TRIANGLE_DATA_LAYOUT.FLOATS_PER_TRIANGLE;
234
- const m = table.world;
235
-
236
234
  this.totalEmissivePower = 0;
237
235
 
238
236
  for ( const tri of this.emissiveTriangles ) {
@@ -245,7 +243,7 @@ export class EmissiveTriangleBuilder {
245
243
  const placement = table.placementRunOf?.( tri.meshIndex )?.start ?? - 1;
246
244
  if ( placement >= 0 && table.isSet[ placement ] ) {
247
245
 
248
- const o = placement * 16;
246
+ const o = table.matrixRow( placement ), m = table.rowArray;
249
247
  for ( let k = 0; k < 9; k += 3 ) {
250
248
 
251
249
  const x = v[ k ], y = v[ k + 1 ], z = v[ k + 2 ];
@@ -671,7 +669,9 @@ export class EmissiveTriangleBuilder {
671
669
  this.lightBVHNodeData[ 14 ] = 1.0; // cone axis z
672
670
  this.lightBVHNodeData[ 15 ] = - 1.0; // cosThetaO = whole sphere
673
671
  this.lightBVHNodeCount = 1;
674
- this.emissiveBitTrailMap = new Float32Array( Math.max( this._totalTriangleCount, 1 ) ).fill( - 1 );
672
+ // Shade reads the map only while emissiveTriangleCount > 0: one over every triangle was 446 MB on the Moana
673
+ // island, in the tab, on the GPU and in Chrome's upload pool.
674
+ this.emissiveBitTrailMap = new Float32Array( 4 ).fill( - 1 );
675
675
  this.emissiveIndicesArray = new Int32Array( 0 );
676
676
  this.emissivePowerArray = new Float32Array( 0 );
677
677
  this._buildCDF();