rayzee 5.3.6 → 5.3.8

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rayzee",
3
- "version": "5.3.6",
3
+ "version": "5.3.8",
4
4
  "type": "module",
5
5
  "description": "Real-time WebGPU path tracing engine built on Three.js",
6
6
  "main": "dist/rayzee.umd.js",
@@ -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' ) { reject( e ); return; }
682
- worker = await fetchAsWorker(
683
- new URL( './Workers/BVHWorker.js', import.meta.url )
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