rayzee 5.3.6 → 5.3.7
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.
- package/dist/assets/BVHSubtreeWorker-D9GImjGj.js +2 -0
- package/dist/assets/BVHSubtreeWorker-D9GImjGj.js.map +1 -0
- package/dist/assets/BVHWorker-CNJ0UBQz.js +2 -0
- package/dist/assets/BVHWorker-CNJ0UBQz.js.map +1 -0
- package/dist/rayzee.es.js +852 -889
- package/dist/rayzee.es.js.map +1 -1
- package/dist/rayzee.umd.js +41 -41
- package/dist/rayzee.umd.js.map +1 -1
- package/package.json +1 -1
- package/src/Passes/AIUpscaler.js +3 -7
- package/src/Processor/BVHBuilder.js +3 -7
- package/src/Processor/EquirectHDRInfo.js +3 -7
- package/src/Processor/ParallelBVHBuilder.js +8 -21
- package/src/Processor/SceneProcessor.js +15 -22
- package/src/Processor/TextureCreator.js +3 -7
- package/src/Processor/Workers/fetchAsWorker.js +6 -1
- package/dist/assets/BVHSubtreeWorker-CTHfS54a.js +0 -2
- package/dist/assets/BVHSubtreeWorker-CTHfS54a.js.map +0 -1
- package/dist/assets/BVHWorker-BarjE67Z.js +0 -2
- package/dist/assets/BVHWorker-BarjE67Z.js.map +0 -1
package/package.json
CHANGED
package/src/Passes/AIUpscaler.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { EventDispatcher, ACESFilmicToneMapping } from 'three';
|
|
2
2
|
import { TONE_MAP_FNS, SRGB_GAMMA, applySaturation } from '../Processor/ToneMapCPU.js';
|
|
3
3
|
import { fetchAsWorker } from '../Processor/Workers/fetchAsWorker.js';
|
|
4
|
+
import AI_UPSCALER_WORKER_URL from '../Processor/Workers/AIUpscalerWorker.js?worker&url';
|
|
4
5
|
|
|
5
6
|
|
|
6
7
|
// ─── Model Configuration ───────────────────────────────────────────────────────
|
|
@@ -140,17 +141,12 @@ export class AIUpscaler extends EventDispatcher {
|
|
|
140
141
|
|
|
141
142
|
try {
|
|
142
143
|
|
|
143
|
-
this._worker = new Worker(
|
|
144
|
-
new URL( '../Processor/Workers/AIUpscalerWorker.js', import.meta.url ),
|
|
145
|
-
{ type: 'module' }
|
|
146
|
-
);
|
|
144
|
+
this._worker = new Worker( AI_UPSCALER_WORKER_URL, { type: 'module' } );
|
|
147
145
|
|
|
148
146
|
} catch ( e ) {
|
|
149
147
|
|
|
150
148
|
if ( e.name !== 'SecurityError' ) throw e;
|
|
151
|
-
this._worker = await fetchAsWorker(
|
|
152
|
-
new URL( '../Processor/Workers/AIUpscalerWorker.js', import.meta.url )
|
|
153
|
-
);
|
|
149
|
+
this._worker = await fetchAsWorker( AI_UPSCALER_WORKER_URL );
|
|
154
150
|
|
|
155
151
|
}
|
|
156
152
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { TreeletOptimizer } from './TreeletOptimizer.js';
|
|
2
2
|
import { ReinsertionOptimizer } from './ReinsertionOptimizer.js';
|
|
3
3
|
import { fetchAsWorker } from './Workers/fetchAsWorker.js';
|
|
4
|
+
import BVH_WORKER_URL from './Workers/BVHWorker.js?worker&url';
|
|
4
5
|
|
|
5
6
|
// Inline copy of TRIANGLE_DATA_LAYOUT (mirrors Constants.js).
|
|
6
7
|
// Cannot import Constants.js because BVHBuilder runs inside BVHWorker
|
|
@@ -482,18 +483,13 @@ export class BVHBuilder {
|
|
|
482
483
|
|
|
483
484
|
try {
|
|
484
485
|
|
|
485
|
-
setupWorker( new Worker(
|
|
486
|
-
new URL( './Workers/BVHWorker.js', import.meta.url ),
|
|
487
|
-
{ type: 'module' }
|
|
488
|
-
) );
|
|
486
|
+
setupWorker( new Worker( BVH_WORKER_URL, { type: 'module' } ) );
|
|
489
487
|
|
|
490
488
|
} catch ( error ) {
|
|
491
489
|
|
|
492
490
|
if ( error.name === 'SecurityError' ) {
|
|
493
491
|
|
|
494
|
-
fetchAsWorker(
|
|
495
|
-
new URL( './Workers/BVHWorker.js', import.meta.url )
|
|
496
|
-
).then( setupWorker ).catch( () => {
|
|
492
|
+
fetchAsWorker( BVH_WORKER_URL ).then( setupWorker ).catch( () => {
|
|
497
493
|
|
|
498
494
|
console.warn( 'Worker fetch fallback failed, using synchronous build' );
|
|
499
495
|
resolve( this._buildSyncAndFlatten( triangles, depth, progressCallback ) );
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { DataUtils, HalfFloatType, FloatType, SRGBColorSpace } from 'three';
|
|
2
2
|
import { fetchAsWorker } from './Workers/fetchAsWorker.js';
|
|
3
|
+
import CDF_WORKER_URL from './Workers/CDFWorker.js?worker&url';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Binary search to find the closest index
|
|
@@ -224,17 +225,12 @@ export class EquirectHDRInfo {
|
|
|
224
225
|
|
|
225
226
|
try {
|
|
226
227
|
|
|
227
|
-
this._worker = new Worker(
|
|
228
|
-
new URL( './Workers/CDFWorker.js', import.meta.url ),
|
|
229
|
-
{ type: 'module' }
|
|
230
|
-
);
|
|
228
|
+
this._worker = new Worker( CDF_WORKER_URL, { type: 'module' } );
|
|
231
229
|
|
|
232
230
|
} catch ( e ) {
|
|
233
231
|
|
|
234
232
|
if ( e.name !== 'SecurityError' ) throw e;
|
|
235
|
-
this._worker = await fetchAsWorker(
|
|
236
|
-
new URL( './Workers/CDFWorker.js', import.meta.url )
|
|
237
|
-
);
|
|
233
|
+
this._worker = await fetchAsWorker( CDF_WORKER_URL );
|
|
238
234
|
|
|
239
235
|
}
|
|
240
236
|
|
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { fetchAsWorker } from './Workers/fetchAsWorker.js';
|
|
11
|
+
import BVH_WORKER_URL from './Workers/BVHWorker.js?worker&url';
|
|
12
|
+
import BVH_SUBTREE_WORKER_URL from './Workers/BVHSubtreeWorker.js?worker&url';
|
|
11
13
|
|
|
12
14
|
const FPT = 32; // FLOATS_PER_TRIANGLE
|
|
13
15
|
const PARALLEL_THRESHOLD = 50000;
|
|
@@ -53,17 +55,12 @@ export function buildBVHParallel( triangles, depth, progressCallback, config ) {
|
|
|
53
55
|
let coordinatorWorker;
|
|
54
56
|
try {
|
|
55
57
|
|
|
56
|
-
coordinatorWorker = new Worker(
|
|
57
|
-
new URL( './Workers/BVHWorker.js', import.meta.url ),
|
|
58
|
-
{ type: 'module' }
|
|
59
|
-
);
|
|
58
|
+
coordinatorWorker = new Worker( BVH_WORKER_URL, { type: 'module' } );
|
|
60
59
|
|
|
61
60
|
} catch ( e ) {
|
|
62
61
|
|
|
63
62
|
if ( e.name !== 'SecurityError' ) throw e;
|
|
64
|
-
coordinatorWorker = await fetchAsWorker(
|
|
65
|
-
new URL( './Workers/BVHWorker.js', import.meta.url )
|
|
66
|
-
);
|
|
63
|
+
coordinatorWorker = await fetchAsWorker( BVH_WORKER_URL );
|
|
67
64
|
|
|
68
65
|
}
|
|
69
66
|
|
|
@@ -316,17 +313,12 @@ async function handlePhase2(
|
|
|
316
313
|
let subtreeWorker;
|
|
317
314
|
try {
|
|
318
315
|
|
|
319
|
-
subtreeWorker = new Worker(
|
|
320
|
-
new URL( './Workers/BVHSubtreeWorker.js', import.meta.url ),
|
|
321
|
-
{ type: 'module' }
|
|
322
|
-
);
|
|
316
|
+
subtreeWorker = new Worker( BVH_SUBTREE_WORKER_URL, { type: 'module' } );
|
|
323
317
|
|
|
324
318
|
} catch ( e ) {
|
|
325
319
|
|
|
326
320
|
if ( e.name !== 'SecurityError' ) throw e;
|
|
327
|
-
subtreeWorker = await fetchAsWorker(
|
|
328
|
-
new URL( './Workers/BVHSubtreeWorker.js', import.meta.url )
|
|
329
|
-
);
|
|
321
|
+
subtreeWorker = await fetchAsWorker( BVH_SUBTREE_WORKER_URL );
|
|
330
322
|
|
|
331
323
|
}
|
|
332
324
|
|
|
@@ -436,17 +428,12 @@ function buildSingleWorker( triangles, depth, progressCallback, config ) {
|
|
|
436
428
|
let worker;
|
|
437
429
|
try {
|
|
438
430
|
|
|
439
|
-
worker = new Worker(
|
|
440
|
-
new URL( './Workers/BVHWorker.js', import.meta.url ),
|
|
441
|
-
{ type: 'module' }
|
|
442
|
-
);
|
|
431
|
+
worker = new Worker( BVH_WORKER_URL, { type: 'module' } );
|
|
443
432
|
|
|
444
433
|
} catch ( e ) {
|
|
445
434
|
|
|
446
435
|
if ( e.name !== 'SecurityError' ) throw e;
|
|
447
|
-
worker = await fetchAsWorker(
|
|
448
|
-
new URL( './Workers/BVHWorker.js', import.meta.url )
|
|
449
|
-
);
|
|
436
|
+
worker = await fetchAsWorker( BVH_WORKER_URL );
|
|
450
437
|
|
|
451
438
|
}
|
|
452
439
|
|
|
@@ -12,6 +12,8 @@ import { updateLoading } from '../Processor/utils.js';
|
|
|
12
12
|
import { BuildTimer } from './BuildTimer.js';
|
|
13
13
|
import { TRIANGLE_DATA_LAYOUT } from '../EngineDefaults.js';
|
|
14
14
|
import { fetchAsWorker } from './Workers/fetchAsWorker.js';
|
|
15
|
+
import BVH_WORKER_URL from './Workers/BVHWorker.js?worker&url';
|
|
16
|
+
import BVH_REFIT_WORKER_URL from './Workers/BVHRefitWorker.js?worker&url';
|
|
15
17
|
|
|
16
18
|
/**
|
|
17
19
|
* SceneProcessor - Processes scene geometry into GPU-ready data:
|
|
@@ -671,17 +673,18 @@ export class SceneProcessor {
|
|
|
671
673
|
let worker;
|
|
672
674
|
try {
|
|
673
675
|
|
|
674
|
-
worker = new Worker(
|
|
675
|
-
new URL( './Workers/BVHWorker.js', import.meta.url ),
|
|
676
|
-
{ type: 'module' }
|
|
677
|
-
);
|
|
676
|
+
worker = new Worker( BVH_WORKER_URL, { type: 'module' } );
|
|
678
677
|
|
|
679
678
|
} catch ( e ) {
|
|
680
679
|
|
|
681
|
-
if ( e.name !== 'SecurityError' ) {
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
680
|
+
if ( e.name !== 'SecurityError' ) {
|
|
681
|
+
|
|
682
|
+
reject( e );
|
|
683
|
+
return;
|
|
684
|
+
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
worker = await fetchAsWorker( BVH_WORKER_URL );
|
|
685
688
|
|
|
686
689
|
}
|
|
687
690
|
|
|
@@ -1156,17 +1159,12 @@ export class SceneProcessor {
|
|
|
1156
1159
|
|
|
1157
1160
|
try {
|
|
1158
1161
|
|
|
1159
|
-
this._refitWorker = new Worker(
|
|
1160
|
-
new URL( './Workers/BVHRefitWorker.js', import.meta.url ),
|
|
1161
|
-
{ type: 'module' }
|
|
1162
|
-
);
|
|
1162
|
+
this._refitWorker = new Worker( BVH_REFIT_WORKER_URL, { type: 'module' } );
|
|
1163
1163
|
|
|
1164
1164
|
} catch ( e ) {
|
|
1165
1165
|
|
|
1166
1166
|
if ( e.name !== 'SecurityError' ) throw e;
|
|
1167
|
-
this._refitWorker = await fetchAsWorker(
|
|
1168
|
-
new URL( './Workers/BVHRefitWorker.js', import.meta.url )
|
|
1169
|
-
);
|
|
1167
|
+
this._refitWorker = await fetchAsWorker( BVH_REFIT_WORKER_URL );
|
|
1170
1168
|
|
|
1171
1169
|
}
|
|
1172
1170
|
|
|
@@ -1759,18 +1757,13 @@ export class SceneProcessor {
|
|
|
1759
1757
|
let worker;
|
|
1760
1758
|
try {
|
|
1761
1759
|
|
|
1762
|
-
worker = new Worker(
|
|
1763
|
-
new URL( './Workers/BVHWorker.js', import.meta.url ),
|
|
1764
|
-
{ type: 'module' }
|
|
1765
|
-
);
|
|
1760
|
+
worker = new Worker( BVH_WORKER_URL, { type: 'module' } );
|
|
1766
1761
|
dispatchRebuild( meshIdx, entry, worker );
|
|
1767
1762
|
|
|
1768
1763
|
} catch ( e ) {
|
|
1769
1764
|
|
|
1770
1765
|
if ( e.name !== 'SecurityError' ) throw e;
|
|
1771
|
-
fetchAsWorker(
|
|
1772
|
-
new URL( './Workers/BVHWorker.js', import.meta.url )
|
|
1773
|
-
).then( w => dispatchRebuild( meshIdx, entry, w ) );
|
|
1766
|
+
fetchAsWorker( BVH_WORKER_URL ).then( w => dispatchRebuild( meshIdx, entry, w ) );
|
|
1774
1767
|
|
|
1775
1768
|
}
|
|
1776
1769
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { DataArrayTexture, RGBAFormat, LinearFilter, UnsignedByteType, SRGBColorSpace } from "three";
|
|
2
2
|
import { TEXTURE_CONSTANTS, MEMORY_CONSTANTS, DEFAULT_TEXTURE_MATRIX, MATERIAL_DATA_LAYOUT } from '../EngineDefaults.js';
|
|
3
3
|
import { fetchAsWorker } from './Workers/fetchAsWorker.js';
|
|
4
|
+
import TEXTURES_WORKER_URL from './Workers/TexturesWorker.js?worker&url';
|
|
4
5
|
|
|
5
6
|
// Canvas pooling for efficient reuse of canvas elements
|
|
6
7
|
class CanvasPool {
|
|
@@ -606,17 +607,12 @@ export class TextureCreator {
|
|
|
606
607
|
let worker;
|
|
607
608
|
try {
|
|
608
609
|
|
|
609
|
-
worker = new Worker(
|
|
610
|
-
new URL( './Workers/TexturesWorker.js', import.meta.url ),
|
|
611
|
-
{ type: 'module' }
|
|
612
|
-
);
|
|
610
|
+
worker = new Worker( TEXTURES_WORKER_URL, { type: 'module' } );
|
|
613
611
|
|
|
614
612
|
} catch ( e ) {
|
|
615
613
|
|
|
616
614
|
if ( e.name !== 'SecurityError' ) throw e;
|
|
617
|
-
worker = await fetchAsWorker(
|
|
618
|
-
new URL( './Workers/TexturesWorker.js', import.meta.url )
|
|
619
|
-
);
|
|
615
|
+
worker = await fetchAsWorker( TEXTURES_WORKER_URL );
|
|
620
616
|
|
|
621
617
|
}
|
|
622
618
|
|
|
@@ -11,6 +11,11 @@
|
|
|
11
11
|
* The Blob URL is intentionally **not** revoked — workers may reference
|
|
12
12
|
* `self.location.href` to spawn sub-workers (e.g. BVHWorker).
|
|
13
13
|
*
|
|
14
|
+
* Always spawns the worker as a module. In bundled builds Vite inlines
|
|
15
|
+
* the fallback URL as a `data:` containing the original ESM source
|
|
16
|
+
* (with top-level `import` statements), which requires `type: 'module'`.
|
|
17
|
+
* IIFE-bundled assets also load correctly as module workers.
|
|
18
|
+
*
|
|
14
19
|
* @param {URL|string} url Worker script URL (may be cross-origin)
|
|
15
20
|
* @returns {Promise<Worker>}
|
|
16
21
|
*/
|
|
@@ -25,6 +30,6 @@ export async function fetchAsWorker( url ) {
|
|
|
25
30
|
}
|
|
26
31
|
|
|
27
32
|
const blob = new Blob( [ await response.text() ], { type: 'application/javascript' } );
|
|
28
|
-
return new Worker( URL.createObjectURL( blob ) );
|
|
33
|
+
return new Worker( URL.createObjectURL( blob ), { type: 'module' } );
|
|
29
34
|
|
|
30
35
|
}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
(function(){var e=class{constructor(e,t){this.traversalCost=e,this.intersectionCost=t,this.maxTreeletLeaves=7,this.minImprovement=.02,this.topologyCache=new Map;for(let e=3;e<=this.maxTreeletLeaves;e++)this.topologyCache.set(e,this.generateTopologies(e));this.stats={treeletsProcessed:0,treeletsImproved:0,totalSAHImprovement:0,averageSAHImprovement:0,optimizationTime:0}}generateTopologies(e){if(e===1)return[0];if(e===2)return[[0,1]];let t=[];for(let n=1;n<e;n++){let r=this.generateTopologies(n),i=this.generateTopologies(e-n);for(let e of r)for(let r of i)t.push([e,this.offsetTopology(r,n)])}return t}offsetTopology(e,t){return typeof e==`number`?e+t:[this.offsetTopology(e[0],t),this.offsetTopology(e[1],t)]}optimizeBVH(e){let t=performance.now();this.stats={treeletsProcessed:0,treeletsImproved:0,totalSAHImprovement:0,averageSAHImprovement:0,optimizationTime:0};let n=this.identifyTreeletRoots(e);for(let e=0;e<n.length;e++){if(performance.now()-t>3e4){console.warn(`TreeletOptimizer: timeout after ${e}/${n.length} treelets`);break}this.optimizeTreelet(n[e])}return this.stats.optimizationTime=performance.now()-t,this.stats.averageSAHImprovement=this.stats.treeletsProcessed>0?this.stats.totalSAHImprovement/this.stats.treeletsProcessed:0,e}identifyTreeletRoots(e){let t=[],n=new Set,r=[{node:e,visited:!1}];for(;r.length>0;){let e=r[r.length-1];if(e.visited){r.pop();let i=e.node;if(i.triangleCount>0||n.has(i))continue;let a=this.countLeaves(i);a>=3&&a<=this.maxTreeletLeaves&&(t.push(i),this.markSubtree(i,n))}else{e.visited=!0;let t=e.node;if(t.triangleCount>0)continue;t.rightChild&&r.push({node:t.rightChild,visited:!1}),t.leftChild&&r.push({node:t.leftChild,visited:!1})}}return t}countLeaves(e){return e?e.triangleCount>0?1:this.countLeaves(e.leftChild)+this.countLeaves(e.rightChild):0}markSubtree(e,t){e&&(t.add(e),!(e.triangleCount>0)&&(this.markSubtree(e.leftChild,t),this.markSubtree(e.rightChild,t)))}optimizeTreelet(e){let t=[];this.extractLeaves(e,t);let n=t.length;if(n<3||n>this.maxTreeletLeaves)return;this.stats.treeletsProcessed++;let r=this.evaluateSubtreeSAH(e),i=this.topologyCache.get(n);if(!i||i.length===0)return;let a=r,o=null,s=null;if(n<=5){let e=this.generatePermutations(n);for(let n of i)for(let r of e){let e=this.evaluateTopology(n,t,r);e<a&&(a=e,o=n,s=r)}}else{let e=Array.from({length:n},(e,t)=>t);for(let n of i){let r=this.evaluateTopology(n,t,e);r<a&&(a=r,o=n,s=e);let i=this.greedySwapOptimize(n,t,e,r);i.cost<a&&(a=i.cost,o=n,s=i.perm)}}let c=(r-a)/r;o&&c>this.minImprovement&&(this.reconstructTreelet(e,o,t,s),this.stats.treeletsImproved++,this.stats.totalSAHImprovement+=c)}extractLeaves(e,t){if(e){if(e.triangleCount>0){t.push({minX:e.minX,minY:e.minY,minZ:e.minZ,maxX:e.maxX,maxY:e.maxY,maxZ:e.maxZ,triangleOffset:e.triangleOffset,triangleCount:e.triangleCount});return}this.extractLeaves(e.leftChild,t),this.extractLeaves(e.rightChild,t)}}evaluateSubtreeSAH(e){if(!e)return 0;if(e.triangleCount>0)return this.surfaceAreaFlat(e.minX,e.minY,e.minZ,e.maxX,e.maxY,e.maxZ)*e.triangleCount*this.intersectionCost;let t=this.evaluateSubtreeSAH(e.leftChild),n=this.evaluateSubtreeSAH(e.rightChild);return this.surfaceAreaFlat(e.minX,e.minY,e.minZ,e.maxX,e.maxY,e.maxZ)*this.traversalCost+t+n}evaluateTopology(e,t,n){return this.evalTopoRecursive(e,t,n).cost}evalTopoRecursive(e,t,n){if(typeof e==`number`){let r=t[n[e]];return{cost:this.surfaceAreaFlat(r.minX,r.minY,r.minZ,r.maxX,r.maxY,r.maxZ)*r.triangleCount*this.intersectionCost,minX:r.minX,minY:r.minY,minZ:r.minZ,maxX:r.maxX,maxY:r.maxY,maxZ:r.maxZ}}let r=this.evalTopoRecursive(e[0],t,n),i=this.evalTopoRecursive(e[1],t,n),a=Math.min(r.minX,i.minX),o=Math.min(r.minY,i.minY),s=Math.min(r.minZ,i.minZ),c=Math.max(r.maxX,i.maxX),l=Math.max(r.maxY,i.maxY),u=Math.max(r.maxZ,i.maxZ);return{cost:this.surfaceAreaFlat(a,o,s,c,l,u)*this.traversalCost+r.cost+i.cost,minX:a,minY:o,minZ:s,maxX:c,maxY:l,maxZ:u}}surfaceAreaFlat(e,t,n,r,i,a){let o=r-e,s=i-t,c=a-n;return 2*(o*s+s*c+c*o)}generatePermutations(e){let t=[],n=Array.from({length:e},(e,t)=>t),r=i=>{if(i===e){t.push([...n]);return}for(let t=i;t<e;t++)[n[i],n[t]]=[n[t],n[i]],r(i+1),[n[i],n[t]]=[n[t],n[i]]};return r(0),t}greedySwapOptimize(e,t,n,r){let i=[...n],a=r,o=!0;for(;o;){o=!1;for(let n=0;n<i.length-1;n++)for(let r=n+1;r<i.length;r++){[i[n],i[r]]=[i[r],i[n]];let s=this.evaluateTopology(e,t,i);s<a?(a=s,o=!0):[i[n],i[r]]=[i[r],i[n]]}}return{perm:i,cost:a}}reconstructTreelet(e,t,n,r){let i=this.buildSubtree(t,n,r);e.minX=i.minX,e.minY=i.minY,e.minZ=i.minZ,e.maxX=i.maxX,e.maxY=i.maxY,e.maxZ=i.maxZ,e.leftChild=i.leftChild,e.rightChild=i.rightChild,e.triangleOffset=i.triangleOffset,e.triangleCount=i.triangleCount}buildSubtree(e,n,r){if(typeof e==`number`){let i=n[r[e]],a=new t;return a.minX=i.minX,a.minY=i.minY,a.minZ=i.minZ,a.maxX=i.maxX,a.maxY=i.maxY,a.maxZ=i.maxZ,a.triangleOffset=i.triangleOffset,a.triangleCount=i.triangleCount,a}let i=this.buildSubtree(e[0],n,r),a=this.buildSubtree(e[1],n,r),o=new t;return o.leftChild=i,o.rightChild=a,o.minX=Math.min(i.minX,a.minX),o.minY=Math.min(i.minY,a.minY),o.minZ=Math.min(i.minZ,a.minZ),o.maxX=Math.max(i.maxX,a.maxX),o.maxY=Math.max(i.maxY,a.maxY),o.maxZ=Math.max(i.maxZ,a.maxZ),o}setTreeletSize(e){this.maxTreeletLeaves=Math.max(3,Math.min(7,e));for(let e=3;e<=this.maxTreeletLeaves;e++)this.topologyCache.has(e)||this.topologyCache.set(e,this.generateTopologies(e))}setMinImprovement(e){this.minImprovement=Math.max(.001,e)}setMaxTreelets(){}getStatistics(){return{...this.stats}}},t=class{constructor(){this.minX=0,this.minY=0,this.minZ=0,this.maxX=0,this.maxY=0,this.maxZ=0,this.leftChild=null,this.rightChild=null,this.triangleOffset=0,this.triangleCount=0}},n=class{constructor(e,t){this.traversalCost=e,this.intersectionCost=t,this.batchSizeRatio=.02,this.maxIterations=2,this.timeBudgetMs=15e3,this.stats={reinsertionsApplied:0,iterations:0,timeMs:0}}setBatchSizeRatio(e){this.batchSizeRatio=Math.max(.005,Math.min(.1,e))}setMaxIterations(e){this.maxIterations=Math.max(1,Math.min(5,e))}getStatistics(){return{...this.stats}}surfaceArea(e){let t=e.maxX-e.minX,n=e.maxY-e.minY,r=e.maxZ-e.minZ;return t*n+n*r+r*t}buildParentMap(e){let t=new Map;t.set(e,{parent:null,isLeft:!1});let n=[e];for(;n.length>0;){let e=n.pop();e.triangleCount>0||(e.leftChild&&(t.set(e.leftChild,{parent:e,isLeft:!0}),n.push(e.leftChild)),e.rightChild&&(t.set(e.rightChild,{parent:e,isLeft:!1}),n.push(e.rightChild)))}return t}findCandidates(e,t,n){let r=[],i=[e];for(;i.length>0;){let a=i.pop();if(a!==e&&n.get(a).parent!==e){let e=this.surfaceArea(a);r.length<t?(r.push({node:a,cost:e}),r.length===t&&this._heapify(r)):e>r[0].cost&&(r[0]={node:a,cost:e},this._siftDown(r,0))}a.triangleCount===0&&(a.leftChild&&i.push(a.leftChild),a.rightChild&&i.push(a.rightChild))}return r}_heapify(e){for(let t=(e.length>>1)-1;t>=0;t--)this._siftDown(e,t)}_siftDown(e,t){let n=e.length;for(;;){let r=t,i=2*t+1,a=2*t+2;if(i<n&&e[i].cost<e[r].cost&&(r=i),a<n&&e[a].cost<e[r].cost&&(r=a),r===t)break;let o=e[t];e[t]=e[r],e[r]=o,t=r}}findReinsertion(e,t,n){let r=n.get(e),i=r.parent;if(!i)return null;let a=r.isLeft?i.rightChild:i.leftChild,o=this.surfaceArea(e),s=this.surfaceArea(i),c=null,l=0,u=s,d=a.minX,f=a.minY,p=a.minZ,m=a.maxX,h=a.maxY,g=a.maxZ,_=a,v=i,y=[];do{for(y.length=0,y.push(u,_);y.length>0;){let t=y.pop(),n=y.pop();if(n-o<=l)continue;let r=Math.min(t.minX,e.minX),i=Math.min(t.minY,e.minY),a=Math.min(t.minZ,e.minZ),s=Math.max(t.maxX,e.maxX),u=Math.max(t.maxY,e.maxY),d=Math.max(t.maxZ,e.maxZ),f=s-r,p=u-i,m=d-a,h=n-(f*p+p*m+m*f);if(h>l&&(c=t,l=h),t.triangleCount===0&&t.leftChild&&t.rightChild){let e=h+this.surfaceArea(t);y.push(e,t.leftChild),y.push(e,t.rightChild)}}let t=n.get(v);if(!t||t.parent===null)break;if(v!==i){d=Math.min(d,_.minX),f=Math.min(f,_.minY),p=Math.min(p,_.minZ),m=Math.max(m,_.maxX),h=Math.max(h,_.maxY),g=Math.max(g,_.maxZ);let e=m-d,t=h-f,n=g-p,r=e*t+t*n+n*e;u+=this.surfaceArea(v)-r}let r=t.parent;_=t.isLeft?r.rightChild:r.leftChild,v=r}while(n.get(v).parent!==null);return c===a||c===i?null:c?{from:e,to:c,areaDiff:l}:null}getConflicts(e,t,n){let r=n.get(e);return[t,e,r.isLeft?r.parent.rightChild:r.parent.leftChild,n.get(t).parent,r.parent]}reinsertNode(e,t,n){let r=n.get(e),i=r.parent,a=r.isLeft?i.rightChild:i.leftChild,o=n.get(i),s=o.parent,c=n.get(t),l=c.parent;o.isLeft?s.leftChild=a:s.rightChild=a,i.leftChild=e,i.rightChild=t,i.triangleOffset=0,i.triangleCount=0,i.minX=Math.min(e.minX,t.minX),i.minY=Math.min(e.minY,t.minY),i.minZ=Math.min(e.minZ,t.minZ),i.maxX=Math.max(e.maxX,t.maxX),i.maxY=Math.max(e.maxY,t.maxY),i.maxZ=Math.max(e.maxZ,t.maxZ),c.isLeft?l.leftChild=i:l.rightChild=i,n.set(a,{parent:s,isLeft:o.isLeft}),n.set(i,{parent:l,isLeft:c.isLeft}),n.set(e,{parent:i,isLeft:!0}),n.set(t,{parent:i,isLeft:!1}),this.refitFrom(s,n),this.refitFrom(l,n)}refitFrom(e,t){let n=e;for(;n;){if(n.triangleCount===0&&n.leftChild&&n.rightChild){let e=n.leftChild,t=n.rightChild;n.minX=Math.min(e.minX,t.minX),n.minY=Math.min(e.minY,t.minY),n.minZ=Math.min(e.minZ,t.minZ),n.maxX=Math.max(e.maxX,t.maxX),n.maxY=Math.max(e.maxY,t.maxY),n.maxZ=Math.max(e.maxZ,t.maxZ)}let e=t.get(n);n=e?e.parent:null}}optimizeBVH(e,t){let n=performance.now();this.stats={reinsertionsApplied:0,iterations:0,timeMs:0};for(let r=0;r<this.maxIterations&&!(performance.now()-n>this.timeBudgetMs);r++){let i=this.buildParentMap(e),a=i.size,o=Math.max(1,Math.floor(a*this.batchSizeRatio));t&&t(`Reinsertion iter ${r+1}/${this.maxIterations}: selecting ${o} candidates`);let s=this.findCandidates(e,o,i),c=[];for(let t=0;t<s.length&&!(performance.now()-n>this.timeBudgetMs);t++){let n=this.findReinsertion(s[t].node,e,i);n&&n.areaDiff>0&&c.push(n)}c.sort((e,t)=>t.areaDiff-e.areaDiff);let l=new Set,u=0;for(let e of c){let t=this.getConflicts(e.from,e.to,i);if(!t.some(e=>l.has(e))){for(let e of t)l.add(e);this.reinsertNode(e.from,e.to,i),u++}}if(this.stats.reinsertionsApplied+=u,this.stats.iterations=r+1,t&&t(`Reinsertion iter ${r+1}: applied ${u} reinsertions`),u===0)break}return this.stats.timeMs=performance.now()-n,this.stats}};async function r(e){let t=e instanceof URL?e.href:e,n=await fetch(t);if(!n.ok)throw Error(`Failed to fetch worker script: ${n.status}`);let r=new Blob([await n.text()],{type:`application/javascript`});return new Worker(URL.createObjectURL(r))}let i={FLOATS_PER_TRIANGLE:32,POSITION_A_OFFSET:0,POSITION_B_OFFSET:4,POSITION_C_OFFSET:8,NORMAL_A_OFFSET:12,NORMAL_B_OFFSET:16,NORMAL_C_OFFSET:20,UV_AB_OFFSET:24,UV_C_MAT_OFFSET:28},a=i.FLOATS_PER_TRIANGLE;var o=class{constructor(){this.minX=0,this.minY=0,this.minZ=0,this.maxX=0,this.maxY=0,this.maxZ=0,this.leftChild=null,this.rightChild=null,this.triangleOffset=0,this.triangleCount=0}},s=class{constructor(){this.useWorker=!0,this.maxLeafSize=8,this.numBins=32,this.minBins=8,this.maxBins=64,this.totalNodes=0,this.processedTriangles=0,this.totalTriangles=0,this.lastProgressUpdate=0,this.progressUpdateInterval=100,this.traversalCost=1,this.intersectionCost=2.5,this.useMortonCodes=!0,this.mortonBits=10,this.mortonClusterThreshold=128,this.enableObjectMedianFallback=!0,this.enableSpatialMedianFallback=!0,this.splitStats={sahSplits:0,objectMedianSplits:0,spatialMedianSplits:0,failedSplits:0,avgBinsUsed:0,totalSplitAttempts:0,mortonSortTime:0,totalBuildTime:0,treeletOptimizationTime:0,treeletsProcessed:0,treeletsImproved:0,averageSAHImprovement:0,reinsertionOptimizationTime:0,reinsertionsApplied:0,reinsertionIterations:0},this.enableTreeletOptimization=!0,this.treeletSize=5,this.treeletOptimizationPasses=1,this.treeletMinImprovement=.02,this.maxTreeletDepth=3,this.maxTreeletsPerScene=20,this.treeletComplexityThreshold=5e4,this.enableReinsertionOptimization=!0,this.reinsertionBatchSizeRatio=.02,this.reinsertionMaxIterations=2,this.initializeBinArrays(),this._partResult={mid:0,lMinX:0,lMinY:0,lMinZ:0,lMaxX:0,lMaxY:0,lMaxZ:0,rMinX:0,rMinY:0,rMinZ:0,rMaxX:0,rMaxY:0,rMaxZ:0},this.centroids=null,this.bMin=null,this.bMax=null,this.indices=null,this.mortonCodes=null,this.triangles=null,this.reorderedTriangleData=null}initializeBinArrays(){let e=this.maxBins;this.binBoundsMin=new Float32Array(e*3),this.binBoundsMax=new Float32Array(e*3),this.binCounts=new Uint32Array(e),this.leftPrefixMin=new Float32Array(e*3),this.leftPrefixMax=new Float32Array(e*3),this.leftPrefixCount=new Uint32Array(e),this.rightPrefixMin=new Float32Array(e*3),this.rightPrefixMax=new Float32Array(e*3),this.rightPrefixCount=new Uint32Array(e)}getOptimalBinCount(e){return e<=16?this.minBins:e<=64?16:e<=256?32:e<=1024?48:this.maxBins}setAdaptiveBinConfig(e){e.minBins!==void 0&&(this.minBins=Math.max(4,e.minBins)),e.maxBins!==void 0&&(this.maxBins=Math.min(128,e.maxBins)),e.baseBins!==void 0&&(this.numBins=e.baseBins),e.maxBins!==void 0&&this.initializeBinArrays()}setMortonConfig(e){e.enabled!==void 0&&(this.useMortonCodes=e.enabled),e.bits!==void 0&&(this.mortonBits=Math.max(6,Math.min(10,e.bits))),e.threshold!==void 0&&(this.mortonClusterThreshold=Math.max(16,e.threshold))}setFallbackConfig(e){e.objectMedian!==void 0&&(this.enableObjectMedianFallback=e.objectMedian),e.spatialMedian!==void 0&&(this.enableSpatialMedianFallback=e.spatialMedian)}setTreeletConfig(e){e.enabled!==void 0&&(this.enableTreeletOptimization=e.enabled),e.size!==void 0&&(this.treeletSize=Math.max(3,Math.min(12,e.size))),e.passes!==void 0&&(this.treeletOptimizationPasses=Math.max(1,Math.min(3,e.passes))),e.minImprovement!==void 0&&(this.treeletMinImprovement=Math.max(.001,e.minImprovement))}disableTreeletOptimization(){this.enableTreeletOptimization=!1}setReinsertionConfig(e){e.enabled!==void 0&&(this.enableReinsertionOptimization=e.enabled),e.batchSizeRatio!==void 0&&(this.reinsertionBatchSizeRatio=Math.max(.005,Math.min(.1,e.batchSizeRatio))),e.maxIterations!==void 0&&(this.reinsertionMaxIterations=Math.max(1,Math.min(5,e.maxIterations)))}initializeTriangleArrays(){let e=this.totalTriangles,t=this.triangles,n=i.POSITION_A_OFFSET,r=i.POSITION_B_OFFSET,o=i.POSITION_C_OFFSET;for(let i=0;i<e;i++){let e=i*a,s=t[e+n],c=t[e+n+1],l=t[e+n+2],u=t[e+r],d=t[e+r+1],f=t[e+r+2],p=t[e+o],m=t[e+o+1],h=t[e+o+2],g=i*3;this.centroids[g]=(s+u+p)/3,this.centroids[g+1]=(c+d+m)/3,this.centroids[g+2]=(l+f+h)/3,this.bMin[g]=s<u?s<p?s:p:u<p?u:p,this.bMin[g+1]=c<d?c<m?c:m:d<m?d:m,this.bMin[g+2]=l<f?l<h?l:h:f<h?f:h,this.bMax[g]=s>u?s>p?s:p:u>p?u:p,this.bMax[g+1]=c>d?c>m?c:m:d>m?d:m,this.bMax[g+2]=l>f?l>h?l:h:f>h?f:h,this.indices[i]=i}}expandBits(e){return e=e*65537&4278190335,e=e*257&251719695,e=e*17&3272356035,e=e*5&1227133513,e}morton3D(e,t,n){return(this.expandBits(n)<<2)+(this.expandBits(t)<<1)+this.expandBits(e)}computeMortonCodeForIndex(e,t,n,r,i,a,o){let s=this.centroids,c=e*3,l=(1<<this.mortonBits)-1,u=i>0?(s[c]-t)/i:0,d=a>0?(s[c+1]-n)/a:0,f=o>0?(s[c+2]-r)/o:0,p=Math.max(0,Math.min(l,Math.floor(u*l))),m=Math.max(0,Math.min(l,Math.floor(d*l))),h=Math.max(0,Math.min(l,Math.floor(f*l)));return this.morton3D(p,m,h)}sortTrianglesByMortonCode(){let e=this.totalTriangles;if(!this.useMortonCodes||e<this.mortonClusterThreshold)return;let t=performance.now(),n=this.centroids,r=this.indices,i=1/0,a=1/0,o=1/0,s=-1/0,c=-1/0,l=-1/0;for(let t=0;t<e;t++){let e=r[t]*3,u=n[e],d=n[e+1],f=n[e+2];u<i&&(i=u),d<a&&(a=d),f<o&&(o=f),u>s&&(s=u),d>c&&(c=d),f>l&&(l=f)}let u=s-i,d=c-a,f=l-o,p=this.mortonCodes,m=(1<<this.mortonBits)-1,h=u>0?m/u:0,g=d>0?m/d:0,_=f>0?m/f:0;for(let t=0;t<e;t++){let e=r[t],s=e*3,c=(n[s]-i)*h,l=(n[s+1]-a)*g,u=(n[s+2]-o)*_;c=c<0?0:(c>m?m:c)|0,l=l<0?0:(l>m?m:l)|0,u=u<0?0:(u>m?m:u)|0,c=c*65537&4278190335,c=c*257&251719695,c=c*17&3272356035,c=c*5&1227133513,l=l*65537&4278190335,l=l*257&251719695,l=l*17&3272356035,l=l*5&1227133513,u=u*65537&4278190335,u=u*257&251719695,u=u*17&3272356035,u=u*5&1227133513,p[e]=(u<<2)+(l<<1)+c}let v=new Uint32Array(e),y=new Uint32Array(256);for(let t=0;t<32;t+=8){y.fill(0);for(let n=0;n<e;n++)y[p[r[n]]>>>t&255]++;let n=0;for(let e=0;e<256;e++){let t=y[e];y[e]=n,n+=t}for(let n=0;n<e;n++){let e=p[r[n]]>>>t&255;v[y[e]++]=r[n]}r.set(v)}this.splitStats.mortonSortTime+=performance.now()-t}build(e,t=30,n=null){return this.totalTriangles=e.byteLength/(a*4),this.processedTriangles=0,this.lastProgressUpdate=performance.now(),this.useWorker&&typeof Worker<`u`?new Promise((i,o)=>{let s=r=>{let s=this.totalTriangles,c=typeof SharedArrayBuffer<`u`;console.log(`[BVHBuilder] SharedArrayBuffer: ${c?`enabled`:`unavailable (using transfer fallback)`}`);let l=c?new SharedArrayBuffer(s*a*4):null;r.onmessage=e=>{let{bvhData:t,triangles:a,originalToBvh:s,error:c,progress:u,treeletStats:d}=e.data;if(c){r.terminate(),o(Error(c));return}if(u!==void 0&&n){n(u);return}d&&(this.splitStats=d),r.terminate(),i({bvhData:t,bvhRoot:!0,reorderedTriangles:l?new Float32Array(l):a,originalToBvh:s||null})},r.onerror=e=>{r.terminate(),o(e)};let u=e.buffer,d={triangleData:u,triangleByteOffset:e.byteOffset,triangleByteLength:e.byteLength,triangleCount:s,depth:t,reportProgress:!!n,sharedReorderBuffer:l,treeletOptimization:{enabled:this.enableTreeletOptimization,size:this.treeletSize,passes:this.treeletOptimizationPasses,minImprovement:this.treeletMinImprovement},reinsertionOptimization:{enabled:this.enableReinsertionOptimization,batchSizeRatio:this.reinsertionBatchSizeRatio,maxIterations:this.reinsertionMaxIterations}};r.postMessage(d,[u])};try{s(new Worker(new URL(``+new URL(`BVHWorker-BarjE67Z.js`,self.location.href).href,``+self.location.href),{type:`module`}))}catch(a){a.name===`SecurityError`?r(new URL(`data:text/javascript;base64,aW1wb3J0IHsgQlZIQnVpbGRlciB9IGZyb20gJy4uL0JWSEJ1aWxkZXIuanMnOwoKY29uc3QgRlBUID0gMzI7IC8vIEZMT0FUU19QRVJfVFJJQU5HTEUKCi8vIC0tLSBNZXNzYWdlIGRpc3BhdGNoZXIgLS0tCgpzZWxmLm9ubWVzc2FnZSA9IGZ1bmN0aW9uICggZSApIHsKCgljb25zdCBkYXRhID0gZS5kYXRhOwoJY29uc3QgdHlwZSA9IGRhdGEudHlwZTsKCglpZiAoIHR5cGUgPT09ICdidWlsZFBoYXNlMScgKSB7CgoJCWhhbmRsZVBoYXNlMSggZGF0YSApOwoKCX0gZWxzZSBpZiAoIHR5cGUgPT09ICdhc3NlbWJsZScgKSB7CgoJCWhhbmRsZUFzc2VtYmxlKCBkYXRhICk7CgoJfSBlbHNlIHsKCgkJLy8gTGVnYWN5OiBmdWxsIHNpbmdsZS13b3JrZXIgYnVpbGQgKGJhY2t3YXJkIGNvbXBhdGlibGUpCgkJaGFuZGxlRnVsbEJ1aWxkKCBkYXRhICk7CgoJfQoKfTsKCi8vIC0tLSBQaGFzZSAxOiBJbml0ICsgTW9ydG9uIHNvcnQgKyB0b3AtbGV2ZWwgU0FIIGJ1aWxkIC0tLQoKZnVuY3Rpb24gaGFuZGxlUGhhc2UxKCBkYXRhICkgewoKCWNvbnN0IHsKCQlzaGFyZWRUcmlhbmdsZURhdGEsIHNoYXJlZENlbnRyb2lkcywgc2hhcmVkQk1pbiwgc2hhcmVkQk1heCwKCQlzaGFyZWRJbmRpY2VzLCBzaGFyZWRNb3J0b25Db2RlcywKCQl0cmlhbmdsZUNvdW50LCBkZXB0aCwgcGFyYWxsZWxEZXB0aCwKCQlyZXBvcnRQcm9ncmVzcywgdHJlZWxldE9wdGltaXphdGlvbgoJfSA9IGRhdGE7CgoJdHJ5IHsKCgkJY29uc3QgYnVpbGRlciA9IG5ldyBCVkhCdWlsZGVyKCk7CgoJCWlmICggdHJlZWxldE9wdGltaXphdGlvbiApIHsKCgkJCWJ1aWxkZXIuc2V0VHJlZWxldENvbmZpZyggdHJlZWxldE9wdGltaXphdGlvbiApOwoKCQl9CgoJCWNvbnN0IHByb2dyZXNzQ2FsbGJhY2sgPSByZXBvcnRQcm9ncmVzcyA/ICggcHJvZ3Jlc3MgKSA9PiB7CgoJCQlzZWxmLnBvc3RNZXNzYWdlKCB7IHR5cGU6ICdwcm9ncmVzcycsIHByb2dyZXNzIH0gKTsKCgkJfSA6IG51bGw7CgoJCS8vIEF0dGFjaCBzaGFyZWQgYnVmZmVyIHZpZXdzCgkJYnVpbGRlci50cmlhbmdsZXMgPSBuZXcgRmxvYXQzMkFycmF5KCBzaGFyZWRUcmlhbmdsZURhdGEgKTsKCQlidWlsZGVyLmNlbnRyb2lkcyA9IG5ldyBGbG9hdDMyQXJyYXkoIHNoYXJlZENlbnRyb2lkcyApOwoJCWJ1aWxkZXIuYk1pbiA9IG5ldyBGbG9hdDMyQXJyYXkoIHNoYXJlZEJNaW4gKTsKCQlidWlsZGVyLmJNYXggPSBuZXcgRmxvYXQzMkFycmF5KCBzaGFyZWRCTWF4ICk7CgkJYnVpbGRlci5pbmRpY2VzID0gbmV3IFVpbnQzMkFycmF5KCBzaGFyZWRJbmRpY2VzICk7CgkJYnVpbGRlci5tb3J0b25Db2RlcyA9IG5ldyBVaW50MzJBcnJheSggc2hhcmVkTW9ydG9uQ29kZXMgKTsKCQlidWlsZGVyLnRvdGFsVHJpYW5nbGVzID0gdHJpYW5nbGVDb3VudDsKCgkJLy8gUmVzZXQgc3RhdGUKCQlidWlsZGVyLnRvdGFsTm9kZXMgPSAwOwoJCWJ1aWxkZXIucHJvY2Vzc2VkVHJpYW5nbGVzID0gMDsKCQlidWlsZGVyLmxhc3RQcm9ncmVzc1VwZGF0ZSA9IHBlcmZvcm1hbmNlLm5vdygpOwoKCQlidWlsZGVyLnNwbGl0U3RhdHMgPSB7CgkJCXNhaFNwbGl0czogMCwgb2JqZWN0TWVkaWFuU3BsaXRzOiAwLCBzcGF0aWFsTWVkaWFuU3BsaXRzOiAwLAoJCQlmYWlsZWRTcGxpdHM6IDAsIGF2Z0JpbnNVc2VkOiAwLCB0b3RhbFNwbGl0QXR0ZW1wdHM6IDAsCgkJCW1vcnRvblNvcnRUaW1lOiAwLCB0b3RhbEJ1aWxkVGltZTogMCwgdHJlZWxldE9wdGltaXphdGlvblRpbWU6IDAsCgkJCXRyZWVsZXRzUHJvY2Vzc2VkOiAwLCB0cmVlbGV0c0ltcHJvdmVkOiAwLCBhdmVyYWdlU0FISW1wcm92ZW1lbnQ6IDAsCgkJCWluaXRUaW1lOiAwLCBzYWhCdWlsZFRpbWU6IDAsIHJlb3JkZXJUaW1lOiAwCgkJfTsKCgkJY29uc3Qgc3RhcnRUaW1lID0gcGVyZm9ybWFuY2Uubm93KCk7CgoJCS8vIFBoYXNlIDFhOiBJbml0aWFsaXplIHBlci10cmlhbmdsZSBhcnJheXMgKHdyaXRlcyBpbnRvIHNoYXJlZCBidWZmZXJzKQoJCWNvbnN0IGluaXRTdGFydCA9IHBlcmZvcm1hbmNlLm5vdygpOwoJCWJ1aWxkZXIuaW5pdGlhbGl6ZVRyaWFuZ2xlQXJyYXlzKCk7CgkJYnVpbGRlci5zcGxpdFN0YXRzLmluaXRUaW1lID0gcGVyZm9ybWFuY2Uubm93KCkgLSBpbml0U3RhcnQ7CgoJCS8vIFBoYXNlIDFiOiBNb3J0b24gY29kZSBzcGF0aWFsIGNsdXN0ZXJpbmcKCQlidWlsZGVyLnNvcnRUcmlhbmdsZXNCeU1vcnRvbkNvZGUoKTsKCgkJLy8gUGhhc2UgMWM6IEJ1aWxkIHRvcC1sZXZlbCB0cmVlIHRvIHBhcmFsbGVsRGVwdGgKCQlidWlsZGVyLmZyb250aWVyVGFza3MgPSBbXTsKCQljb25zdCBzYWhTdGFydCA9IHBlcmZvcm1hbmNlLm5vdygpOwoJCWNvbnN0IHJvb3QgPSBidWlsZGVyLmJ1aWxkTm9kZVJlY3Vyc2l2ZVRvRGVwdGgoIDAsIHRyaWFuZ2xlQ291bnQsIGRlcHRoLCBwYXJhbGxlbERlcHRoLCBwcm9ncmVzc0NhbGxiYWNrICk7CgkJYnVpbGRlci5zcGxpdFN0YXRzLnNhaEJ1aWxkVGltZSA9IHBlcmZvcm1hbmNlLm5vdygpIC0gc2FoU3RhcnQ7CgoJCS8vIFBoYXNlIDFkOiBTdXJmYWNlLWFyZWEgY2hpbGQgb3JkZXJpbmcgKERGUyBjYWNoZSBsb2NhbGl0eSkKCQlidWlsZGVyLmFwcGx5U0FPcmRlcmluZyggcm9vdCApOwoKCQkvLyBQaGFzZSAxZTogRmxhdHRlbiB0b3AtbGV2ZWwgdHJlZSB3aXRoIGZyb250aWVyIHNlbnRpbmVscwoJCWNvbnN0IGZsYXR0ZW5TdGFydCA9IHBlcmZvcm1hbmNlLm5vdygpOwoJCWNvbnN0IHsgZmxhdERhdGEsIGZyb250aWVyTWFwLCBub2RlQ291bnQgfSA9IGJ1aWxkZXIuZmxhdHRlbkJWSFdpdGhGcm9udGllciggcm9vdCApOwoJCWNvbnN0IGZsYXR0ZW5UaW1lID0gcGVyZm9ybWFuY2Uubm93KCkgLSBmbGF0dGVuU3RhcnQ7CgoJCWNvbnN0IHRvdGFsVGltZSA9IHBlcmZvcm1hbmNlLm5vdygpIC0gc3RhcnRUaW1lOwoJCWNvbnNvbGUubG9nKCBgW0JWSFdvcmtlcl0gUGhhc2UgMTogJHtNYXRoLnJvdW5kKCB0b3RhbFRpbWUgKX1tcyAoaW5pdDogJHtNYXRoLnJvdW5kKCBidWlsZGVyLnNwbGl0U3RhdHMuaW5pdFRpbWUgKX1tcywgbW9ydG9uOiAke01hdGgucm91bmQoIGJ1aWxkZXIuc3BsaXRTdGF0cy5tb3J0b25Tb3J0VGltZSApfW1zLCBTQUg6ICR7TWF0aC5yb3VuZCggYnVpbGRlci5zcGxpdFN0YXRzLnNhaEJ1aWxkVGltZSApfW1zLCBmbGF0dGVuOiAke01hdGgucm91bmQoIGZsYXR0ZW5UaW1lICl9bXMpLCAke2J1aWxkZXIuZnJvbnRpZXJUYXNrcy5sZW5ndGh9IGZyb250aWVyIHRhc2tzYCApOwoKCQlzZWxmLnBvc3RNZXNzYWdlKCB7CgkJCXR5cGU6ICdwaGFzZTFSZXN1bHQnLAoJCQl0b3BGbGF0RGF0YTogZmxhdERhdGEsCgkJCXRvcE5vZGVDb3VudDogbm9kZUNvdW50LAoJCQlmcm9udGllclRhc2tzOiBidWlsZGVyLmZyb250aWVyVGFza3MsCgkJCWZyb250aWVyTWFwLAoJCQlzcGxpdFN0YXRzOiBidWlsZGVyLnNwbGl0U3RhdHMKCQl9LCBbIGZsYXREYXRhLmJ1ZmZlciBdICk7CgoJfSBjYXRjaCAoIGVycm9yICkgewoKCQljb25zb2xlLmVycm9yKCAnW0JWSFdvcmtlcl0gUGhhc2UgMSBlcnJvcjonLCBlcnJvciApOwoJCXNlbGYucG9zdE1lc3NhZ2UoIHsgdHlwZTogJ2Vycm9yJywgZXJyb3I6IGVycm9yLm1lc3NhZ2UgfSApOwoKCX0KCn0KCi8vIC0tLSBQaGFzZSAzOiBBc3NlbWJsZSBmaW5hbCBCVkggKyByZW9yZGVyIHRyaWFuZ2xlcyAtLS0KCmZ1bmN0aW9uIGhhbmRsZUFzc2VtYmxlKCBkYXRhICkgewoKCWNvbnN0IHsKCQl0b3BGbGF0RGF0YSwgdG9wTm9kZUNvdW50LCBmcm9udGllck1hcCwgc3VidHJlZVJlc3VsdHMsCgkJc2hhcmVkVHJpYW5nbGVEYXRhLCBzaGFyZWRJbmRpY2VzLCBzaGFyZWRSZW9yZGVyQnVmZmVyLAoJCXRyaWFuZ2xlQ291bnQKCX0gPSBkYXRhOwoKCXRyeSB7CgoJCWNvbnN0IHN0YXJ0VGltZSA9IHBlcmZvcm1hbmNlLm5vdygpOwoJCWNvbnN0IGJ1aWxkZXIgPSBuZXcgQlZIQnVpbGRlcigpOwoKCQkvLyBBc3NlbWJsZSB0aGUgZmluYWwgQlZICgkJY29uc3QgYnZoRGF0YSA9IGJ1aWxkZXIuYXNzZW1ibGVQYXJhbGxlbEJWSCgKCQkJdG9wRmxhdERhdGEsIHRvcE5vZGVDb3VudCwgZnJvbnRpZXJNYXAsIHN1YnRyZWVSZXN1bHRzCgkJKTsKCgkJLy8gUmVvcmRlciB0cmlhbmdsZXMgdXNpbmcgZmluYWwgaW5kaWNlcyBmcm9tIFNoYXJlZEFycmF5QnVmZmVyCgkJY29uc3QgaW5kaWNlcyA9IG5ldyBVaW50MzJBcnJheSggc2hhcmVkSW5kaWNlcyApOwoJCWNvbnN0IHNyYyA9IG5ldyBGbG9hdDMyQXJyYXkoIHNoYXJlZFRyaWFuZ2xlRGF0YSApOwoJCWNvbnN0IGRzdCA9IG5ldyBGbG9hdDMyQXJyYXkoIHNoYXJlZFJlb3JkZXJCdWZmZXIgKTsKCgkJZm9yICggbGV0IGkgPSAwOyBpIDwgdHJpYW5nbGVDb3VudDsgaSArKyApIHsKCgkJCWNvbnN0IHNyY09mZiA9IGluZGljZXNbIGkgXSAqIEZQVDsKCQkJY29uc3QgZHN0T2ZmID0gaSAqIEZQVDsKCQkJZHN0LnNldCggc3JjLnN1YmFycmF5KCBzcmNPZmYsIHNyY09mZiArIEZQVCApLCBkc3RPZmYgKTsKCgkJfQoKCQkvLyBCdWlsZCBpbnZlcnNlIGluZGV4IG1hcCBmb3IgQlZIIHJlZml0CgkJY29uc3Qgb3JpZ2luYWxUb0J2aCA9IG5ldyBVaW50MzJBcnJheSggdHJpYW5nbGVDb3VudCApOwoJCWZvciAoIGxldCBpID0gMDsgaSA8IHRyaWFuZ2xlQ291bnQ7IGkgKysgKSB7CgoJCQlvcmlnaW5hbFRvQnZoWyBpbmRpY2VzWyBpIF0gXSA9IGk7CgoJCX0KCgkJY29uc3QgdG90YWxUaW1lID0gcGVyZm9ybWFuY2Uubm93KCkgLSBzdGFydFRpbWU7CgkJY29uc29sZS5sb2coIGBbQlZIV29ya2VyXSBQaGFzZSAzIChhc3NlbWJsZSArIHJlb3JkZXIpOiAke01hdGgucm91bmQoIHRvdGFsVGltZSApfW1zICgkeyggYnZoRGF0YS5ieXRlTGVuZ3RoIC8gMTAyNCAvIDEwMjQgKS50b0ZpeGVkKCAxICl9TUIgQlZIKWAgKTsKCgkJc2VsZi5wb3N0TWVzc2FnZSggewoJCQl0eXBlOiAnYXNzZW1ibGVSZXN1bHQnLAoJCQlidmhEYXRhLAoJCQlvcmlnaW5hbFRvQnZoLAoJCQl0cmlhbmdsZUNvdW50CgkJfSwgWyBidmhEYXRhLmJ1ZmZlciwgb3JpZ2luYWxUb0J2aC5idWZmZXIgXSApOwoKCX0gY2F0Y2ggKCBlcnJvciApIHsKCgkJY29uc29sZS5lcnJvciggJ1tCVkhXb3JrZXJdIEFzc2VtYmx5IGVycm9yOicsIGVycm9yICk7CgkJc2VsZi5wb3N0TWVzc2FnZSggeyB0eXBlOiAnZXJyb3InLCBlcnJvcjogZXJyb3IubWVzc2FnZSB9ICk7CgoJfQoKfQoKLy8gLS0tIExlZ2FjeTogZnVsbCBzaW5nbGUtd29ya2VyIGJ1aWxkIC0tLQoKZnVuY3Rpb24gaGFuZGxlRnVsbEJ1aWxkKCBkYXRhICkgewoKCWNvbnN0IHsgdHJpYW5nbGVEYXRhLCB0cmlhbmdsZUJ5dGVPZmZzZXQsIHRyaWFuZ2xlQnl0ZUxlbmd0aCwgZGVwdGgsIHJlcG9ydFByb2dyZXNzLCB0cmVlbGV0T3B0aW1pemF0aW9uLCByZWluc2VydGlvbk9wdGltaXphdGlvbiwgc2hhcmVkUmVvcmRlckJ1ZmZlciB9ID0gZGF0YTsKCWNvbnN0IGJ1aWxkZXIgPSBuZXcgQlZIQnVpbGRlcigpOwoKCXRyeSB7CgoJCWlmICggdHJlZWxldE9wdGltaXphdGlvbiApIHsKCgkJCWJ1aWxkZXIuc2V0VHJlZWxldENvbmZpZyggdHJlZWxldE9wdGltaXphdGlvbiApOwoKCQl9CgoJCWlmICggcmVpbnNlcnRpb25PcHRpbWl6YXRpb24gKSB7CgoJCQlidWlsZGVyLnNldFJlaW5zZXJ0aW9uQ29uZmlnKCByZWluc2VydGlvbk9wdGltaXphdGlvbiApOwoKCQl9CgoJCWNvbnN0IHByb2dyZXNzQ2FsbGJhY2sgPSByZXBvcnRQcm9ncmVzcyA/ICggcHJvZ3Jlc3MgKSA9PiB7CgoJCQlzZWxmLnBvc3RNZXNzYWdlKCB7IHByb2dyZXNzIH0gKTsKCgkJfSA6IG51bGw7CgoJCWNvbnN0IGlucHV0VHJpYW5nbGVzID0gdHJpYW5nbGVCeXRlT2Zmc2V0ICE9PSB1bmRlZmluZWQKCQkJPyBuZXcgRmxvYXQzMkFycmF5KCB0cmlhbmdsZURhdGEsIHRyaWFuZ2xlQnl0ZU9mZnNldCwgdHJpYW5nbGVCeXRlTGVuZ3RoIC8gNCApCgkJCTogbmV3IEZsb2F0MzJBcnJheSggdHJpYW5nbGVEYXRhICk7CgoJCWNvbnN0IHJlb3JkZXJUYXJnZXQgPSBzaGFyZWRSZW9yZGVyQnVmZmVyCgkJCT8gbmV3IEZsb2F0MzJBcnJheSggc2hhcmVkUmVvcmRlckJ1ZmZlciApCgkJCTogbnVsbDsKCgkJY29uc3QgYnZoUm9vdCA9IGJ1aWxkZXIuYnVpbGRTeW5jKCBpbnB1dFRyaWFuZ2xlcywgZGVwdGgsIHByb2dyZXNzQ2FsbGJhY2ssIHJlb3JkZXJUYXJnZXQgKTsKCgkJY29uc3QgZmxhdHRlblN0YXJ0ID0gcGVyZm9ybWFuY2Uubm93KCk7CgkJY29uc3QgYnZoRGF0YSA9IGJ1aWxkZXIuZmxhdHRlbkJWSCggYnZoUm9vdCApOwoJCWNvbnN0IGZsYXR0ZW5UaW1lID0gcGVyZm9ybWFuY2Uubm93KCkgLSBmbGF0dGVuU3RhcnQ7CgkJY29uc29sZS5sb2coIGBbQlZIV29ya2VyXSBGbGF0dGVuIEJWSDogJHtNYXRoLnJvdW5kKCBmbGF0dGVuVGltZSApfW1zICgkeyggYnZoRGF0YS5ieXRlTGVuZ3RoIC8gMTAyNCAvIDEwMjQgKS50b0ZpeGVkKCAxICl9TUIpYCApOwoKCQljb25zdCBvcmlnaW5hbFRvQnZoID0gYnVpbGRlci5vcmlnaW5hbFRvQnZoTWFwIHx8IG51bGw7CgoJCWlmICggc2hhcmVkUmVvcmRlckJ1ZmZlciApIHsKCgkJCWNvbnN0IHRyYW5zZmVyYWJsZXMgPSBbIGJ2aERhdGEuYnVmZmVyIF07CgkJCWlmICggb3JpZ2luYWxUb0J2aCApIHRyYW5zZmVyYWJsZXMucHVzaCggb3JpZ2luYWxUb0J2aC5idWZmZXIgKTsKCgkJCXNlbGYucG9zdE1lc3NhZ2UoIHsKCQkJCWJ2aERhdGEsCgkJCQlvcmlnaW5hbFRvQnZoLAoJCQkJdHJpYW5nbGVDb3VudDogaW5wdXRUcmlhbmdsZXMubGVuZ3RoIC8gMzIsCgkJCQl0cmVlbGV0U3RhdHM6IGJ1aWxkZXIuc3BsaXRTdGF0cwoJCQl9LCB0cmFuc2ZlcmFibGVzICk7CgoJCX0gZWxzZSB7CgoJCQljb25zdCByZW9yZGVyZWRGbG9hdDMyQXJyYXkgPSBidWlsZGVyLnJlb3JkZXJlZFRyaWFuZ2xlRGF0YTsKCQkJY29uc3QgdHJpYW5nbGVDb3VudCA9IHJlb3JkZXJlZEZsb2F0MzJBcnJheS5ieXRlTGVuZ3RoIC8gKCAzMiAqIDQgKTsKCgkJCWNvbnN0IHRyYW5zZmVyYWJsZXMgPSBbIGJ2aERhdGEuYnVmZmVyLCByZW9yZGVyZWRGbG9hdDMyQXJyYXkuYnVmZmVyIF07CgkJCWlmICggb3JpZ2luYWxUb0J2aCApIHRyYW5zZmVyYWJsZXMucHVzaCggb3JpZ2luYWxUb0J2aC5idWZmZXIgKTsKCgkJCXNlbGYucG9zdE1lc3NhZ2UoIHsKCQkJCWJ2aERhdGEsCgkJCQl0cmlhbmdsZXM6IHJlb3JkZXJlZEZsb2F0MzJBcnJheSwKCQkJCW9yaWdpbmFsVG9CdmgsCgkJCQl0cmlhbmdsZUNvdW50LAoJCQkJdHJlZWxldFN0YXRzOiBidWlsZGVyLnNwbGl0U3RhdHMKCQkJfSwgdHJhbnNmZXJhYmxlcyApOwoKCQl9CgoJfSBjYXRjaCAoIGVycm9yICkgewoKCQljb25zb2xlLmVycm9yKCAnW0JWSFdvcmtlcl0gRXJyb3I6JywgZXJyb3IgKTsKCQlzZWxmLnBvc3RNZXNzYWdlKCB7IGVycm9yOiBlcnJvci5tZXNzYWdlIH0gKTsKCgl9Cgp9Cg==`,``+self.location.href)).then(s).catch(()=>{console.warn(`Worker fetch fallback failed, using synchronous build`),i(this._buildSyncAndFlatten(e,t,n))}):(console.warn(`Worker creation failed, falling back to synchronous build:`,a),i(this._buildSyncAndFlatten(e,t,n)))}}):new Promise(r=>{r(this._buildSyncAndFlatten(e,t,n))})}_buildSyncAndFlatten(e,t,n){let r=this.buildSync(e,t,n);return{bvhData:this.flattenBVH(r),bvhRoot:!0,reorderedTriangles:this.reorderedTriangleData||null,originalToBvh:this.originalToBvhMap||null}}buildSync(t,r=30,i=null,o=null){let s=performance.now();this.totalNodes=0,this.processedTriangles=0,this.triangles=t,this.totalTriangles=t.byteLength/(a*4),this.lastProgressUpdate=performance.now(),this.splitStats={sahSplits:0,objectMedianSplits:0,spatialMedianSplits:0,failedSplits:0,avgBinsUsed:0,totalSplitAttempts:0,mortonSortTime:0,totalBuildTime:0,treeletOptimizationTime:0,treeletsProcessed:0,treeletsImproved:0,averageSAHImprovement:0,reinsertionOptimizationTime:0,reinsertionsApplied:0,reinsertionIterations:0,saOrderTime:0,initTime:0,sahBuildTime:0,reorderTime:0};let c=this.totalTriangles,l=performance.now();this.centroids=new Float32Array(c*3),this.bMin=new Float32Array(c*3),this.bMax=new Float32Array(c*3),this.indices=new Uint32Array(c),this.mortonCodes=new Uint32Array(c),this.initializeTriangleArrays(),this.splitStats.initTime=performance.now()-l,this.sortTrianglesByMortonCode();let u=performance.now(),d=this.buildNodeRecursive(0,c,r,i);if(this.splitStats.sahBuildTime=performance.now()-u,this.enableTreeletOptimization&&this.totalTriangles>1e3){let t=this.totalTriangles>this.treeletComplexityThreshold,n=t?3:this.treeletSize,r=t?10:this.maxTreeletsPerScene,a=new e(this.traversalCost,this.intersectionCost);a.setTreeletSize(n),a.setMinImprovement(this.treeletMinImprovement),a.setMaxTreelets(r);let o=performance.now();for(let e=0;e<this.treeletOptimizationPasses;e++){let t=i?t=>{i(`Treelet optimization pass ${e+1}/${this.treeletOptimizationPasses}: ${t}`)}:null;try{a.optimizeBVH(d,t)}catch(t){console.error(`TreeletOptimizer: Error in pass ${e+1}:`,t);break}let n=a.getStatistics(),r=performance.now()-o;if(n.treeletsImproved===0&&e>0||r>15e3)break}let s=performance.now()-o;this.splitStats.treeletOptimizationTime=s;let c=a.getStatistics();this.splitStats.treeletsProcessed=c.treeletsProcessed,this.splitStats.treeletsImproved=c.treeletsImproved,this.splitStats.averageSAHImprovement=c.averageSAHImprovement}if(this.enableReinsertionOptimization&&this.totalTriangles>1e3){let e=new n(this.traversalCost,this.intersectionCost);e.setBatchSizeRatio(this.reinsertionBatchSizeRatio),e.setMaxIterations(this.reinsertionMaxIterations);let t=i?e=>{i(e)}:null;try{e.optimizeBVH(d,t)}catch(e){console.error(`ReinsertionOptimizer: Error:`,e)}let r=e.getStatistics();this.splitStats.reinsertionOptimizationTime=r.timeMs,this.splitStats.reinsertionsApplied=r.reinsertionsApplied,this.splitStats.reinsertionIterations=r.iterations}let f=performance.now();this.applySAOrdering(d),this.splitStats.saOrderTime=performance.now()-f;let p=performance.now(),m=this.triangles,h=o||new Float32Array(c*a);for(let e=0;e<c;e++){let t=this.indices[e]*a,n=e*a;h.set(m.subarray(t,t+a),n)}this.reorderedTriangleData=h;let g=new Uint32Array(c);for(let e=0;e<c;e++)g[this.indices[e]]=e;this.originalToBvhMap=g,this.splitStats.reorderTime=performance.now()-p,this.splitStats.totalBuildTime=performance.now()-s;let _=this.splitStats.totalBuildTime,v=this.splitStats;return console.log(`[BVH] ${c.toLocaleString()} tris → ${this.totalNodes} nodes in ${Math.round(_)}ms | SAH ${v.sahSplits} objMed ${v.objectMedianSplits} spatMed ${v.spatialMedianSplits} failed ${v.failedSplits}`+(v.treeletsProcessed?` | treelets ${v.treeletsImproved}/${v.treeletsProcessed} improved`:``)+(v.reinsertionsApplied?` | reinsertions ${v.reinsertionsApplied}`:``)),i&&i(100),this.centroids=null,this.bMin=null,this.bMax=null,this.mortonCodes=null,d}updateProgress(e,t){if(!t)return;this.processedTriangles+=e;let n=performance.now();n-this.lastProgressUpdate<this.progressUpdateInterval||(this.lastProgressUpdate=n,t(Math.min(Math.floor(this.processedTriangles/this.totalTriangles*100),99)))}buildNodeRecursiveToDepth(e,t,n,r,i,a,s,c,l,u,d){let f=new o;this.totalNodes++;let p=t-e;if(a===void 0?this.updateNodeBounds(f,e,t):(f.minX=a,f.minY=s,f.minZ=c,f.maxX=l,f.maxY=u,f.maxZ=d),p<=this.maxLeafSize||n<=0)return f.triangleOffset=e,f.triangleCount=p,this.updateProgress(p,i),f;if(r<=0&&p>this.maxLeafSize*16){let r=this.frontierTasks.length;return f.triangleOffset=e,f.triangleCount=p,f.isFrontier=!0,f.frontierTaskId=r,this.frontierTasks.push({taskId:r,start:e,end:t,depth:n,preMinX:f.minX,preMinY:f.minY,preMinZ:f.minZ,preMaxX:f.maxX,preMaxY:f.maxY,preMaxZ:f.maxZ}),f}let m=this.findBestSplitPositionSAH(e,t,f);if(!m.success){if(this.splitStats.failedSplits++,r>0||p<=this.maxLeafSize*16)return f.triangleOffset=e,f.triangleCount=p,this.updateProgress(p,i),f;let a=this.frontierTasks.length;return f.triangleOffset=e,f.triangleCount=p,f.isFrontier=!0,f.frontierTaskId=a,this.frontierTasks.push({taskId:a,start:e,end:t,depth:n,preMinX:f.minX,preMinY:f.minY,preMinZ:f.minZ,preMaxX:f.maxX,preMaxY:f.maxY,preMaxZ:f.maxZ}),f}m.method===`SAH`?this.splitStats.sahSplits++:m.method===`object_median`?this.splitStats.objectMedianSplits++:m.method===`spatial_median`&&this.splitStats.spatialMedianSplits++,this.partitionWithBounds(e,t,m.axis,m.pos);let h=this._partResult,g=h.mid,_=h.lMinX,v=h.lMinY,y=h.lMinZ,b=h.lMaxX,x=h.lMaxY,S=h.lMaxZ,C=h.rMinX,w=h.rMinY,T=h.rMinZ,E=h.rMaxX,D=h.rMaxY,O=h.rMaxZ;return g===e||g===t?(f.triangleOffset=e,f.triangleCount=p,this.updateProgress(p,i),f):(f.leftChild=this.buildNodeRecursiveToDepth(e,g,n-1,r-1,i,_,v,y,b,x,S),f.rightChild=this.buildNodeRecursiveToDepth(g,t,n-1,r-1,i,C,w,T,E,D,O),f)}buildNodeRecursive(e,t,n,r,i,a,s,c,l,u){let d=new o;this.totalNodes++;let f=t-e;if(i===void 0?this.updateNodeBounds(d,e,t):(d.minX=i,d.minY=a,d.minZ=s,d.maxX=c,d.maxY=l,d.maxZ=u),f<=this.maxLeafSize||n<=0)return d.triangleOffset=e,d.triangleCount=f,this.updateProgress(f,r),d;let p=this.findBestSplitPositionSAH(e,t,d);if(!p.success)return this.splitStats.failedSplits++,d.triangleOffset=e,d.triangleCount=f,this.updateProgress(f,r),d;p.method===`SAH`?this.splitStats.sahSplits++:p.method===`object_median`?this.splitStats.objectMedianSplits++:p.method===`spatial_median`&&this.splitStats.spatialMedianSplits++,this.partitionWithBounds(e,t,p.axis,p.pos);let m=this._partResult,h=m.mid,g=m.lMinX,_=m.lMinY,v=m.lMinZ,y=m.lMaxX,b=m.lMaxY,x=m.lMaxZ,S=m.rMinX,C=m.rMinY,w=m.rMinZ,T=m.rMaxX,E=m.rMaxY,D=m.rMaxZ;return h===e||h===t?(d.triangleOffset=e,d.triangleCount=f,this.updateProgress(f,r),d):(d.leftChild=this.buildNodeRecursive(e,h,n-1,r,g,_,v,y,b,x),d.rightChild=this.buildNodeRecursive(h,t,n-1,r,S,C,w,T,E,D),d)}partitionWithBounds(e,t,n,r){let i=this.indices,a=this.centroids,o=this.bMin,s=this.bMax,c=e,l=t-1,u=1/0,d=1/0,f=1/0,p=-1/0,m=-1/0,h=-1/0,g=1/0,_=1/0,v=1/0,y=-1/0,b=-1/0,x=-1/0;for(;c<=l;){let e=i[c],t=e*3;a[t+n]<=r?(o[t]<u&&(u=o[t]),o[t+1]<d&&(d=o[t+1]),o[t+2]<f&&(f=o[t+2]),s[t]>p&&(p=s[t]),s[t+1]>m&&(m=s[t+1]),s[t+2]>h&&(h=s[t+2]),c++):(o[t]<g&&(g=o[t]),o[t+1]<_&&(_=o[t+1]),o[t+2]<v&&(v=o[t+2]),s[t]>y&&(y=s[t]),s[t+1]>b&&(b=s[t+1]),s[t+2]>x&&(x=s[t+2]),i[c]=i[l],i[l]=e,l--)}let S=this._partResult;return S.mid=c,S.lMinX=u,S.lMinY=d,S.lMinZ=f,S.lMaxX=p,S.lMaxY=m,S.lMaxZ=h,S.rMinX=g,S.rMinY=_,S.rMinZ=v,S.rMaxX=y,S.rMaxY=b,S.rMaxZ=x,S}updateNodeBounds(e,t,n){let r=1/0,i=1/0,a=1/0,o=-1/0,s=-1/0,c=-1/0,l=this.indices,u=this.bMin,d=this.bMax;for(let e=t;e<n;e++){let t=l[e]*3;u[t]<r&&(r=u[t]),u[t+1]<i&&(i=u[t+1]),u[t+2]<a&&(a=u[t+2]),d[t]>o&&(o=d[t]),d[t+1]>s&&(s=d[t+1]),d[t+2]>c&&(c=d[t+2])}e.minX=r,e.minY=i,e.minZ=a,e.maxX=o,e.maxY=s,e.maxZ=c}findBestSplitPositionSAH(e,t,n){let r=1/0,i=-1,a=0,o=this.computeSurfaceAreaFlat(n.minX,n.minY,n.minZ,n.maxX,n.maxY,n.maxZ),s=t-e,c=this.intersectionCost*s,l=this.getOptimalBinCount(s);this.splitStats.totalSplitAttempts++,this.splitStats.avgBinsUsed=(this.splitStats.avgBinsUsed*(this.splitStats.totalSplitAttempts-1)+l)/this.splitStats.totalSplitAttempts;let u=this.indices,d=this.centroids,f=this.bMin,p=this.bMax,m=this.binBoundsMin,h=this.binBoundsMax,g=this.binCounts,_=this.leftPrefixMin,v=this.leftPrefixMax,y=this.leftPrefixCount,b=this.rightPrefixMin,x=this.rightPrefixMax,S=this.rightPrefixCount,C=1/0,w=-1/0,T=1/0,E=-1/0,D=1/0,O=-1/0;for(let n=e;n<t;n++){let e=u[n]*3,t=d[e],r=d[e+1],i=d[e+2];t<C&&(C=t),t>w&&(w=t),r<T&&(T=r),r>E&&(E=r),i<D&&(D=i),i>O&&(O=i)}let k=[C,T,D],A=[w,E,O];for(let n=0;n<3;n++){let s=k[n],C=A[n];if(C-s<1e-6)continue;for(let e=0;e<l;e++){g[e]=0;let t=e*3;m[t]=1/0,m[t+1]=1/0,m[t+2]=1/0,h[t]=-1/0,h[t+1]=-1/0,h[t+2]=-1/0}let w=l/(C-s);for(let r=e;r<t;r++){let e=u[r],t=d[e*3+n],i=Math.floor((t-s)*w);i>=l&&(i=l-1),g[i]++;let a=i*3,o=e*3;f[o]<m[a]&&(m[a]=f[o]),f[o+1]<m[a+1]&&(m[a+1]=f[o+1]),f[o+2]<m[a+2]&&(m[a+2]=f[o+2]),p[o]>h[a]&&(h[a]=p[o]),p[o+1]>h[a+1]&&(h[a+1]=p[o+1]),p[o+2]>h[a+2]&&(h[a+2]=p[o+2])}y[0]=g[0],_[0]=m[0],_[1]=m[1],_[2]=m[2],v[0]=h[0],v[1]=h[1],v[2]=h[2];for(let e=1;e<l;e++){let t=e*3,n=(e-1)*3;y[e]=y[e-1]+g[e];let r=_[n],i=m[t],a=_[n+1],o=m[t+1],s=_[n+2],c=m[t+2];_[t]=r<i?r:i,_[t+1]=a<o?a:o,_[t+2]=s<c?s:c;let l=v[n],u=h[t],d=v[n+1],f=h[t+1],p=v[n+2],b=h[t+2];v[t]=l>u?l:u,v[t+1]=d>f?d:f,v[t+2]=p>b?p:b}let T=l-1,E=T*3;S[T]=g[T],b[E]=m[E],b[E+1]=m[E+1],b[E+2]=m[E+2],x[E]=h[E],x[E+1]=h[E+1],x[E+2]=h[E+2];for(let e=T-1;e>=0;e--){let t=e*3,n=(e+1)*3;S[e]=S[e+1]+g[e];let r=b[n],i=m[t],a=b[n+1],o=m[t+1],s=b[n+2],c=m[t+2];b[t]=r<i?r:i,b[t+1]=a<o?a:o,b[t+2]=s<c?s:c;let l=x[n],u=h[t],d=x[n+1],f=h[t+1],p=x[n+2],_=h[t+2];x[t]=l>u?l:u,x[t+1]=d>f?d:f,x[t+2]=p>_?p:_}for(let e=1;e<l;e++){let t=(e-1)*3,u=e*3,d=y[e-1],f=S[e];if(d===0||f===0)continue;let p=v[t]-_[t],m=v[t+1]-_[t+1],h=v[t+2]-_[t+2],g=2*(p*m+m*h+h*p),w=x[u]-b[u],T=x[u+1]-b[u+1],E=x[u+2]-b[u+2],D=2*(w*T+T*E+E*w),O=this.traversalCost+g/o*d*this.intersectionCost+D/o*f*this.intersectionCost;O<r&&O<c&&(r=O,i=n,a=s+(C-s)*e/l)}}return i===-1?this.enableObjectMedianFallback?this.findObjectMedianSplit(e,t):this.enableSpatialMedianFallback?this.findSpatialMedianSplit(e,t):{success:!1,method:`fallbacks_disabled`}:{success:!0,axis:i,pos:a,method:`SAH`,binsUsed:l}}findObjectMedianSplit(e,t){let n=this.indices,r=this.centroids,i=-1,a=-1;for(let o=0;o<3;o++){let s=1/0,c=-1/0;for(let i=e;i<t;i++){let e=r[n[i]*3+o];e<s&&(s=e),e>c&&(c=e)}let l=c-s;l>a&&(a=l,i=o)}if(i===-1||a<1e-10)return this.enableSpatialMedianFallback?this.findSpatialMedianSplit(e,t):{success:!1,method:`object_median_failed`};let o=t-e,s=e+Math.floor(o/2);this.quickselect(e,t,s,i);let c=r[n[s]*3+i],l=!0;for(let e=s+1;e<t;e++)if(r[n[e]*3+i]>c){l=!1;break}if(l){let a=-1/0;for(let t=e;t<s;t++){let e=r[n[t]*3+i];e>a&&(a=e)}if(a<c)c=(a+c)*.5;else return this.enableSpatialMedianFallback?this.findSpatialMedianSplit(e,t):{success:!1,method:`object_median_degenerate`}}return{success:!0,axis:i,pos:c,method:`object_median`}}findSpatialMedianSplit(e,t){let n=this.indices,r=this.centroids,i=this.bMin,a=this.bMax,o=-1,s=-1,c=0,l=0;for(let r=0;r<3;r++){let u=1/0,d=-1/0;for(let o=e;o<t;o++){let e=n[o]*3+r;i[e]<u&&(u=i[e]),a[e]>d&&(d=a[e])}let f=d-u;f>s&&(s=f,o=r,c=u,l=d)}if(o===-1||s<1e-12)return{success:!1,method:`spatial_median_failed`};let u=(c+l)*.5,d=t-e,f=0;for(let i=e;i<t;i++)r[n[i]*3+o]<=u&&f++;if(f===0||f===d){let i=e+Math.floor(d/2);this.quickselect(e,t,i,o);let a=r[n[i]*3+o],s=!0;for(let i=e;i<t;i++)if(r[n[i]*3+o]!==a){s=!1;break}if(s)return{success:!1,method:`spatial_median_degenerate`};let c=-1/0;for(let t=e;t<i;t++){let e=r[n[t]*3+o];e>c&&(c=e)}if(c<a)u=(c+a)*.5;else{let e=1/0;for(let a=i+1;a<t;a++){let t=r[n[a]*3+o];t<e&&(e=t)}u=(a+e)*.5}}return{success:!0,axis:o,pos:u,method:`spatial_median`}}quickselect(e,t,n,r){let i=this.indices,a=this.centroids,o=e,s=t-1;for(;o<s;){let e=o+s>>>1,t=a[i[o]*3+r],c=a[i[e]*3+r],l=a[i[s]*3+r];if(t>c){let t=i[o];i[o]=i[e],i[e]=t}if(t>l){let e=i[o];i[o]=i[s],i[s]=e}if(c>l){let t=i[e];i[e]=i[s],i[s]=t}let u=a[i[e]*3+r],d=o,f=s;for(;d<=f;){for(;a[i[d]*3+r]<u;)d++;for(;a[i[f]*3+r]>u;)f--;if(d<=f){let e=i[d];i[d]=i[f],i[f]=e,d++,f--}}f<n&&(o=d),d>n&&(s=f)}}applySAOrdering(e){if(!e||!e.leftChild)return;let t=[e],n=[];for(;t.length>0;){let e=t.pop();!e.leftChild||!e.rightChild||(n.push(e),t.push(e.leftChild),t.push(e.rightChild))}for(let e=n.length-1;e>=0;e--){let t=n[e],r=t.leftChild,i=t.rightChild,a=r.maxX-r.minX,o=r.maxY-r.minY,s=r.maxZ-r.minZ,c=i.maxX-i.minX,l=i.maxY-i.minY,u=i.maxZ-i.minZ;c*l+l*u+u*c>a*o+o*s+s*a&&(t.leftChild=i,t.rightChild=r)}}flattenBVH(e){let t=[],n=[e];for(;n.length>0;){let e=n.pop();e._flatIndex=t.length,t.push(e),e.rightChild&&n.push(e.rightChild),e.leftChild&&n.push(e.leftChild)}let r=new Float32Array(t.length*16);for(let e=0;e<t.length;e++){let n=t[e],i=e*16;if(n.leftChild){let e=n.leftChild,t=n.rightChild;r[i]=e.minX,r[i+1]=e.minY,r[i+2]=e.minZ,r[i+3]=e._flatIndex,r[i+4]=e.maxX,r[i+5]=e.maxY,r[i+6]=e.maxZ,r[i+7]=t._flatIndex,r[i+8]=t.minX,r[i+9]=t.minY,r[i+10]=t.minZ,r[i+12]=t.maxX,r[i+13]=t.maxY,r[i+14]=t.maxZ}else r[i]=n.triangleOffset,r[i+1]=n.triangleCount,r[i+3]=-1}return r}flattenBVHWithFrontier(e){let t=[],n=[e];for(;n.length>0;){let e=n.pop();e._flatIndex=t.length,t.push(e),e.rightChild&&n.push(e.rightChild),e.leftChild&&n.push(e.leftChild)}let r=new Float32Array(t.length*16),i=[];for(let e=0;e<t.length;e++){let n=t[e],a=e*16;if(n.leftChild){let e=n.leftChild,t=n.rightChild;r[a]=e.minX,r[a+1]=e.minY,r[a+2]=e.minZ,r[a+3]=e._flatIndex,r[a+4]=e.maxX,r[a+5]=e.maxY,r[a+6]=e.maxZ,r[a+7]=t._flatIndex,r[a+8]=t.minX,r[a+9]=t.minY,r[a+10]=t.minZ,r[a+12]=t.maxX,r[a+13]=t.maxY,r[a+14]=t.maxZ}else if(n.isFrontier){let t=n.frontierTaskId;r[a]=n.triangleOffset,r[a+1]=n.triangleCount,r[a+2]=t,r[a+3]=-2,i.push({taskId:t,flatIndex:e})}else r[a]=n.triangleOffset,r[a+1]=n.triangleCount,r[a+3]=-1}return{flatData:r,frontierMap:i,nodeCount:t.length}}assembleParallelBVH(e,t,n,r){let i=[...r].sort((e,t)=>e.taskId-t.taskId),a=t;for(let e=0;e<i.length;e++)a+=i[e].nodeCount;let o=new Float32Array(a*16);o.set(e);let s=new Map;for(let e of n)s.set(e.taskId,e.flatIndex);let c=t;for(let e=0;e<i.length;e++){let t=i[e],n=t.flatData,r=t.nodeCount,a=c*16;o.set(n,a);for(let e=0;e<r;e++){let t=a+e*16;o[t+3]!==-1&&(o[t+3]+=c,o[t+7]+=c)}let l=s.get(t.taskId);if(l!==void 0){let e=l*16,t=a;for(let n=0;n<16;n++)o[e+n]=o[t+n]}c+=r}return o}computeSurfaceAreaFlat(e,t,n,r,i,a){let o=r-e,s=i-t,c=a-n;return 2*(o*s+s*c+c*o)}};self.onmessage=function(t){let{tasks:r,sharedTriangleData:i,sharedCentroids:a,sharedBMin:o,sharedBMax:c,sharedIndices:l,triangleCount:u,maxLeafSize:d,numBins:f,maxBins:p,minBins:m,treeletConfig:h,reinsertionConfig:g,reportProgress:_}=t.data;for(let t=0;t<r.length;t++){let v=r[t];try{let t=new s;t.maxLeafSize=d,t.numBins=f,t.maxBins=p,t.minBins=m,t.triangles=new Float32Array(i),t.centroids=new Float32Array(a),t.bMin=new Float32Array(o),t.bMax=new Float32Array(c),t.indices=new Uint32Array(l),t.totalTriangles=u,t.totalNodes=0,t.processedTriangles=0,t.lastProgressUpdate=performance.now(),t.splitStats={sahSplits:0,objectMedianSplits:0,spatialMedianSplits:0,failedSplits:0,avgBinsUsed:0,totalSplitAttempts:0,mortonSortTime:0,totalBuildTime:0,treeletOptimizationTime:0,treeletsProcessed:0,treeletsImproved:0,averageSAHImprovement:0,initTime:0,sahBuildTime:0,reorderTime:0};let r=_?e=>{self.postMessage({type:`progress`,taskId:v.taskId,progress:e})}:null,y=performance.now(),b=t.buildNodeRecursive(v.start,v.end,v.depth,r,v.preMinX,v.preMinY,v.preMinZ,v.preMaxX,v.preMaxY,v.preMaxZ);if(h&&h.enabled&&v.end-v.start>1e3){let n=v.end-v.start>5e4,r=n?3:h.size||5,i=n?10:20,a=new e(t.traversalCost,t.intersectionCost);a.setTreeletSize(r),a.setMinImprovement(h.minImprovement||.02),a.setMaxTreelets(i);let o=h.passes||1;for(let e=0;e<o;e++)try{a.optimizeBVH(b,null)}catch(t){console.error(`[BVHSubtreeWorker] Treelet pass ${e+1} error:`,t);break}}if(g&&g.enabled&&v.end-v.start>1e3){let e=new n(t.traversalCost,t.intersectionCost);g.batchSizeRatio&&e.setBatchSizeRatio(g.batchSizeRatio),g.maxIterations&&e.setMaxIterations(g.maxIterations);try{e.optimizeBVH(b,null)}catch(e){console.error(`[BVHSubtreeWorker] Reinsertion error:`,e)}}t.applySAOrdering(b);let x=t.flattenBVH(b),S=x.length/16,C=performance.now()-y;console.log(`[BVHSubtreeWorker] Task ${v.taskId}: ${(v.end-v.start).toLocaleString()} triangles, ${S} nodes, ${Math.round(C)}ms`),self.postMessage({type:`subtreeResult`,taskId:v.taskId,flatData:x,nodeCount:S},[x.buffer])}catch(e){console.error(`[BVHSubtreeWorker] Task ${v.taskId} error:`,e),self.postMessage({type:`error`,taskId:v.taskId,error:e.message})}}}})();
|
|
2
|
-
//# sourceMappingURL=BVHSubtreeWorker-CTHfS54a.js.map
|