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,574 +0,0 @@
1
- // No external dependencies — uses inline float fields for bounds (no Vector3)
2
-
3
- /**
4
- * Treelet optimization for BVH trees (Bittner et al. 2013).
5
- *
6
- * For each small subtree ("treelet") of N leaves, enumerates all
7
- * Catalan(N-1) distinct binary tree topologies, tries all leaf
8
- * permutations (or greedy assignment for N > 5), and replaces the
9
- * subtree with the arrangement that minimises SAH cost.
10
- */
11
- export class TreeletOptimizer {
12
-
13
- constructor( traversalCost, intersectionCost ) {
14
-
15
- this.traversalCost = traversalCost;
16
- this.intersectionCost = intersectionCost;
17
- this.maxTreeletLeaves = 7;
18
- this.minImprovement = 0.02; // relative improvement threshold
19
-
20
- // Precompute topologies for leaf counts 3..maxTreeletLeaves
21
- this.topologyCache = new Map();
22
- for ( let n = 3; n <= this.maxTreeletLeaves; n ++ ) {
23
-
24
- this.topologyCache.set( n, this.generateTopologies( n ) );
25
-
26
- }
27
-
28
- // Stats
29
- this.stats = {
30
- treeletsProcessed: 0,
31
- treeletsImproved: 0,
32
- totalSAHImprovement: 0,
33
- averageSAHImprovement: 0,
34
- optimizationTime: 0
35
- };
36
-
37
- }
38
-
39
- // ------------------------------------------------------------------
40
- // Topology generation — nested binary trees via Catalan decomposition
41
- // ------------------------------------------------------------------
42
-
43
- /**
44
- * Generate all distinct binary tree topologies for `n` leaves.
45
- * Leaves are represented as integers 0..n-1 (slot indices).
46
- * Internal nodes are 2-element arrays [left, right].
47
- *
48
- * Examples for n=3 (Catalan(2)=2):
49
- * [[0,1],2] and [0,[1,2]]
50
- */
51
- generateTopologies( n ) {
52
-
53
- if ( n === 1 ) return [ 0 ];
54
- if ( n === 2 ) return [[ 0, 1 ]];
55
-
56
- const results = [];
57
-
58
- // Split n leaves into left (k) and right (n-k) groups.
59
- // Base cases return arrays of topologies:
60
- // n=1 → [0] (one topology: leaf 0)
61
- // n=2 → [[0,1]] (one topology: pair [0,1])
62
- // So `for (const t of topos)` yields individual topologies in all cases.
63
- for ( let k = 1; k < n; k ++ ) {
64
-
65
- const leftTopos = this.generateTopologies( k );
66
- const rightTopos = this.generateTopologies( n - k );
67
-
68
- for ( const lt of leftTopos ) {
69
-
70
- for ( const rt of rightTopos ) {
71
-
72
- results.push( [ lt, this.offsetTopology( rt, k ) ] );
73
-
74
- }
75
-
76
- }
77
-
78
- }
79
-
80
- return results;
81
-
82
- }
83
-
84
- /**
85
- * Offset all leaf indices in a topology by `offset`.
86
- */
87
- offsetTopology( topo, offset ) {
88
-
89
- if ( typeof topo === 'number' ) return topo + offset;
90
- return [ this.offsetTopology( topo[ 0 ], offset ), this.offsetTopology( topo[ 1 ], offset ) ];
91
-
92
- }
93
-
94
- // ------------------------------------------------------------------
95
- // Main entry point
96
- // ------------------------------------------------------------------
97
-
98
- optimizeBVH( bvhRoot ) {
99
-
100
- const startTime = performance.now();
101
- const maxTime = 30000; // 30s safety timeout
102
-
103
- this.stats = {
104
- treeletsProcessed: 0,
105
- treeletsImproved: 0,
106
- totalSAHImprovement: 0,
107
- averageSAHImprovement: 0,
108
- optimizationTime: 0
109
- };
110
-
111
- // Identify all treelet roots (bottom-up, non-overlapping)
112
- const treeletRoots = this.identifyTreeletRoots( bvhRoot );
113
-
114
- for ( let i = 0; i < treeletRoots.length; i ++ ) {
115
-
116
- if ( performance.now() - startTime > maxTime ) {
117
-
118
- console.warn( `TreeletOptimizer: timeout after ${i}/${treeletRoots.length} treelets` );
119
- break;
120
-
121
- }
122
-
123
- this.optimizeTreelet( treeletRoots[ i ] );
124
-
125
- }
126
-
127
- this.stats.optimizationTime = performance.now() - startTime;
128
- this.stats.averageSAHImprovement = this.stats.treeletsProcessed > 0
129
- ? this.stats.totalSAHImprovement / this.stats.treeletsProcessed
130
- : 0;
131
-
132
- return bvhRoot;
133
-
134
- }
135
-
136
- // ------------------------------------------------------------------
137
- // Treelet identification — bottom-up, non-overlapping
138
- // ------------------------------------------------------------------
139
-
140
- identifyTreeletRoots( bvhRoot ) {
141
-
142
- const roots = [];
143
- const processed = new Set();
144
-
145
- // Post-order traversal: children before parents so we process
146
- // bottom-up and skip subtrees already covered by a deeper treelet.
147
- const stack = [ { node: bvhRoot, visited: false } ];
148
-
149
- while ( stack.length > 0 ) {
150
-
151
- const top = stack[ stack.length - 1 ];
152
-
153
- if ( top.visited ) {
154
-
155
- stack.pop();
156
- const node = top.node;
157
-
158
- // Skip leaves
159
- if ( node.triangleCount > 0 ) continue;
160
- // Skip if already inside a processed treelet
161
- if ( processed.has( node ) ) continue;
162
-
163
- const leafCount = this.countLeaves( node );
164
- if ( leafCount >= 3 && leafCount <= this.maxTreeletLeaves ) {
165
-
166
- roots.push( node );
167
- this.markSubtree( node, processed );
168
-
169
- }
170
-
171
- } else {
172
-
173
- top.visited = true;
174
- const node = top.node;
175
- if ( node.triangleCount > 0 ) continue;
176
- if ( node.rightChild ) stack.push( { node: node.rightChild, visited: false } );
177
- if ( node.leftChild ) stack.push( { node: node.leftChild, visited: false } );
178
-
179
- }
180
-
181
- }
182
-
183
- return roots;
184
-
185
- }
186
-
187
- countLeaves( node ) {
188
-
189
- if ( ! node ) return 0;
190
- if ( node.triangleCount > 0 ) return 1;
191
- return this.countLeaves( node.leftChild ) + this.countLeaves( node.rightChild );
192
-
193
- }
194
-
195
- markSubtree( node, set ) {
196
-
197
- if ( ! node ) return;
198
- set.add( node );
199
- if ( node.triangleCount > 0 ) return;
200
- this.markSubtree( node.leftChild, set );
201
- this.markSubtree( node.rightChild, set );
202
-
203
- }
204
-
205
- // ------------------------------------------------------------------
206
- // Single treelet optimization
207
- // ------------------------------------------------------------------
208
-
209
- optimizeTreelet( treeletRoot ) {
210
-
211
- // Extract leaves
212
- const leaves = [];
213
- this.extractLeaves( treeletRoot, leaves );
214
- const n = leaves.length;
215
-
216
- if ( n < 3 || n > this.maxTreeletLeaves ) return;
217
-
218
- this.stats.treeletsProcessed ++;
219
-
220
- const originalCost = this.evaluateSubtreeSAH( treeletRoot );
221
- const topologies = this.topologyCache.get( n );
222
- if ( ! topologies || topologies.length === 0 ) return;
223
-
224
- let bestCost = originalCost;
225
- let bestTopo = null;
226
- let bestPerm = null;
227
-
228
- if ( n <= 5 ) {
229
-
230
- // Full permutation search — feasible for n<=5 (max 120 perms × 14 topos)
231
- const perms = this.generatePermutations( n );
232
-
233
- for ( const topo of topologies ) {
234
-
235
- for ( const perm of perms ) {
236
-
237
- const cost = this.evaluateTopology( topo, leaves, perm );
238
- if ( cost < bestCost ) {
239
-
240
- bestCost = cost;
241
- bestTopo = topo;
242
- bestPerm = perm;
243
-
244
- }
245
-
246
- }
247
-
248
- }
249
-
250
- } else {
251
-
252
- // Greedy assignment for n>5: for each topology, use identity perm
253
- // plus a few SAH-guided swaps
254
- const identityPerm = Array.from( { length: n }, ( _, i ) => i );
255
-
256
- for ( const topo of topologies ) {
257
-
258
- // Try identity permutation
259
- const cost = this.evaluateTopology( topo, leaves, identityPerm );
260
- if ( cost < bestCost ) {
261
-
262
- bestCost = cost;
263
- bestTopo = topo;
264
- bestPerm = identityPerm;
265
-
266
- }
267
-
268
- // Try greedy swap improvements
269
- const result = this.greedySwapOptimize( topo, leaves, identityPerm, cost );
270
- if ( result.cost < bestCost ) {
271
-
272
- bestCost = result.cost;
273
- bestTopo = topo;
274
- bestPerm = result.perm;
275
-
276
- }
277
-
278
- }
279
-
280
- }
281
-
282
- // Apply if relative improvement exceeds threshold
283
- const relativeImprovement = ( originalCost - bestCost ) / originalCost;
284
- if ( bestTopo && relativeImprovement > this.minImprovement ) {
285
-
286
- this.reconstructTreelet( treeletRoot, bestTopo, leaves, bestPerm );
287
- this.stats.treeletsImproved ++;
288
- this.stats.totalSAHImprovement += relativeImprovement;
289
-
290
- }
291
-
292
- }
293
-
294
- extractLeaves( node, leaves ) {
295
-
296
- if ( ! node ) return;
297
-
298
- if ( node.triangleCount > 0 ) {
299
-
300
- // BVHNode uses inline floats (minX/maxX etc.)
301
- leaves.push( {
302
- minX: node.minX, minY: node.minY, minZ: node.minZ,
303
- maxX: node.maxX, maxY: node.maxY, maxZ: node.maxZ,
304
- triangleOffset: node.triangleOffset,
305
- triangleCount: node.triangleCount
306
- } );
307
- return;
308
-
309
- }
310
-
311
- this.extractLeaves( node.leftChild, leaves );
312
- this.extractLeaves( node.rightChild, leaves );
313
-
314
- }
315
-
316
- // ------------------------------------------------------------------
317
- // SAH evaluation
318
- // ------------------------------------------------------------------
319
-
320
- evaluateSubtreeSAH( node ) {
321
-
322
- if ( ! node ) return 0;
323
-
324
- if ( node.triangleCount > 0 ) {
325
-
326
- return this.surfaceAreaFlat( node.minX, node.minY, node.minZ, node.maxX, node.maxY, node.maxZ ) * node.triangleCount * this.intersectionCost;
327
-
328
- }
329
-
330
- const leftCost = this.evaluateSubtreeSAH( node.leftChild );
331
- const rightCost = this.evaluateSubtreeSAH( node.rightChild );
332
- const sa = this.surfaceAreaFlat( node.minX, node.minY, node.minZ, node.maxX, node.maxY, node.maxZ );
333
- return sa * this.traversalCost + leftCost + rightCost;
334
-
335
- }
336
-
337
- /**
338
- * Evaluate SAH cost for a topology with a given leaf permutation.
339
- * Returns { cost, boundsMin, boundsMax } for internal use, or just cost number.
340
- */
341
- evaluateTopology( topo, leaves, perm ) {
342
-
343
- const result = this.evalTopoRecursive( topo, leaves, perm );
344
- return result.cost;
345
-
346
- }
347
-
348
- evalTopoRecursive( topo, leaves, perm ) {
349
-
350
- if ( typeof topo === 'number' ) {
351
-
352
- // Leaf slot — look up actual leaf via permutation
353
- const leaf = leaves[ perm[ topo ] ];
354
- return {
355
- cost: this.surfaceAreaFlat( leaf.minX, leaf.minY, leaf.minZ, leaf.maxX, leaf.maxY, leaf.maxZ ) * leaf.triangleCount * this.intersectionCost,
356
- minX: leaf.minX, minY: leaf.minY, minZ: leaf.minZ,
357
- maxX: leaf.maxX, maxY: leaf.maxY, maxZ: leaf.maxZ
358
- };
359
-
360
- }
361
-
362
- const left = this.evalTopoRecursive( topo[ 0 ], leaves, perm );
363
- const right = this.evalTopoRecursive( topo[ 1 ], leaves, perm );
364
-
365
- const mnX = Math.min( left.minX, right.minX );
366
- const mnY = Math.min( left.minY, right.minY );
367
- const mnZ = Math.min( left.minZ, right.minZ );
368
- const mxX = Math.max( left.maxX, right.maxX );
369
- const mxY = Math.max( left.maxY, right.maxY );
370
- const mxZ = Math.max( left.maxZ, right.maxZ );
371
-
372
- const sa = this.surfaceAreaFlat( mnX, mnY, mnZ, mxX, mxY, mxZ );
373
- return {
374
- cost: sa * this.traversalCost + left.cost + right.cost,
375
- minX: mnX, minY: mnY, minZ: mnZ,
376
- maxX: mxX, maxY: mxY, maxZ: mxZ
377
- };
378
-
379
- }
380
-
381
- surfaceAreaFlat( minX, minY, minZ, maxX, maxY, maxZ ) {
382
-
383
- const dx = maxX - minX;
384
- const dy = maxY - minY;
385
- const dz = maxZ - minZ;
386
- return 2 * ( dx * dy + dy * dz + dz * dx );
387
-
388
- }
389
-
390
- // ------------------------------------------------------------------
391
- // Permutation helpers
392
- // ------------------------------------------------------------------
393
-
394
- generatePermutations( n ) {
395
-
396
- const result = [];
397
- const arr = Array.from( { length: n }, ( _, i ) => i );
398
-
399
- const permute = ( start ) => {
400
-
401
- if ( start === n ) {
402
-
403
- result.push( [ ...arr ] );
404
- return;
405
-
406
- }
407
-
408
- for ( let i = start; i < n; i ++ ) {
409
-
410
- [ arr[ start ], arr[ i ] ] = [ arr[ i ], arr[ start ] ];
411
- permute( start + 1 );
412
- [ arr[ start ], arr[ i ] ] = [ arr[ i ], arr[ start ] ];
413
-
414
- }
415
-
416
- };
417
-
418
- permute( 0 );
419
- return result;
420
-
421
- }
422
-
423
- /**
424
- * Greedy pairwise swap optimization for large treelet sizes.
425
- * Starting from an initial permutation, try all pairwise swaps
426
- * and accept any that improve cost. Repeat until no improvement.
427
- */
428
- greedySwapOptimize( topo, leaves, initialPerm, initialCost ) {
429
-
430
- const perm = [ ...initialPerm ];
431
- let cost = initialCost;
432
- let improved = true;
433
-
434
- while ( improved ) {
435
-
436
- improved = false;
437
-
438
- for ( let i = 0; i < perm.length - 1; i ++ ) {
439
-
440
- for ( let j = i + 1; j < perm.length; j ++ ) {
441
-
442
- // Swap
443
- [ perm[ i ], perm[ j ] ] = [ perm[ j ], perm[ i ] ];
444
- const newCost = this.evaluateTopology( topo, leaves, perm );
445
-
446
- if ( newCost < cost ) {
447
-
448
- cost = newCost;
449
- improved = true;
450
-
451
- } else {
452
-
453
- // Swap back
454
- [ perm[ i ], perm[ j ] ] = [ perm[ j ], perm[ i ] ];
455
-
456
- }
457
-
458
- }
459
-
460
- }
461
-
462
- }
463
-
464
- return { perm, cost };
465
-
466
- }
467
-
468
- // ------------------------------------------------------------------
469
- // Reconstruction — builds proper BVHNode instances
470
- // ------------------------------------------------------------------
471
-
472
- reconstructTreelet( treeletRoot, topo, leaves, perm ) {
473
-
474
- const built = this.buildSubtree( topo, leaves, perm );
475
-
476
- // Copy built structure into the existing treelet root node (inline floats)
477
- treeletRoot.minX = built.minX; treeletRoot.minY = built.minY; treeletRoot.minZ = built.minZ;
478
- treeletRoot.maxX = built.maxX; treeletRoot.maxY = built.maxY; treeletRoot.maxZ = built.maxZ;
479
- treeletRoot.leftChild = built.leftChild;
480
- treeletRoot.rightChild = built.rightChild;
481
- treeletRoot.triangleOffset = built.triangleOffset;
482
- treeletRoot.triangleCount = built.triangleCount;
483
-
484
- }
485
-
486
- /**
487
- * Recursively build a BVHNode subtree from a topology + leaf permutation.
488
- */
489
- buildSubtree( topo, leaves, perm ) {
490
-
491
- if ( typeof topo === 'number' ) {
492
-
493
- // Leaf — create a lightweight node with inline bounds
494
- const leaf = leaves[ perm[ topo ] ];
495
- const node = new TreeletBVHNode();
496
- node.minX = leaf.minX; node.minY = leaf.minY; node.minZ = leaf.minZ;
497
- node.maxX = leaf.maxX; node.maxY = leaf.maxY; node.maxZ = leaf.maxZ;
498
- node.triangleOffset = leaf.triangleOffset;
499
- node.triangleCount = leaf.triangleCount;
500
- return node;
501
-
502
- }
503
-
504
- const left = this.buildSubtree( topo[ 0 ], leaves, perm );
505
- const right = this.buildSubtree( topo[ 1 ], leaves, perm );
506
-
507
- const node = new TreeletBVHNode();
508
- node.leftChild = left;
509
- node.rightChild = right;
510
- node.minX = Math.min( left.minX, right.minX );
511
- node.minY = Math.min( left.minY, right.minY );
512
- node.minZ = Math.min( left.minZ, right.minZ );
513
- node.maxX = Math.max( left.maxX, right.maxX );
514
- node.maxY = Math.max( left.maxY, right.maxY );
515
- node.maxZ = Math.max( left.maxZ, right.maxZ );
516
-
517
- return node;
518
-
519
- }
520
-
521
- // ------------------------------------------------------------------
522
- // Configuration
523
- // ------------------------------------------------------------------
524
-
525
- setTreeletSize( size ) {
526
-
527
- this.maxTreeletLeaves = Math.max( 3, Math.min( 7, size ) );
528
-
529
- // Regenerate topology cache if needed
530
- for ( let n = 3; n <= this.maxTreeletLeaves; n ++ ) {
531
-
532
- if ( ! this.topologyCache.has( n ) ) {
533
-
534
- this.topologyCache.set( n, this.generateTopologies( n ) );
535
-
536
- }
537
-
538
- }
539
-
540
- }
541
-
542
- setMinImprovement( threshold ) {
543
-
544
- this.minImprovement = Math.max( 0.001, threshold );
545
-
546
- }
547
-
548
- getStatistics() {
549
-
550
- return { ...this.stats };
551
-
552
- }
553
-
554
- }
555
-
556
- /**
557
- * Lightweight BVH node used during reconstruction.
558
- * Duck-type compatible with the main BVHNode class —
559
- * uses inline float fields (minX/maxX etc.).
560
- */
561
- class TreeletBVHNode {
562
-
563
- constructor() {
564
-
565
- this.minX = 0; this.minY = 0; this.minZ = 0;
566
- this.maxX = 0; this.maxY = 0; this.maxZ = 0;
567
- this.leftChild = null;
568
- this.rightChild = null;
569
- this.triangleOffset = 0;
570
- this.triangleCount = 0;
571
-
572
- }
573
-
574
- }