rayzee 5.3.5 → 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 +948 -909
- 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 +12 -4
- package/src/Processor/BVHBuilder.js +24 -8
- package/src/Processor/EquirectHDRInfo.js +12 -4
- package/src/Processor/ParallelBVHBuilder.js +45 -20
- package/src/Processor/SceneProcessor.js +69 -31
- package/src/Processor/TextureCreator.js +13 -4
- package/src/Processor/Workers/fetchAsWorker.js +35 -0
- package/dist/assets/BVHSubtreeWorker-BoG4D6dP.js +0 -2
- package/dist/assets/BVHSubtreeWorker-BoG4D6dP.js.map +0 -1
- package/dist/assets/BVHWorker-BqQTDljT.js +0 -2
- package/dist/assets/BVHWorker-BqQTDljT.js.map +0 -1
package/package.json
CHANGED
package/src/Passes/AIUpscaler.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { EventDispatcher, ACESFilmicToneMapping } from 'three';
|
|
2
2
|
import { TONE_MAP_FNS, SRGB_GAMMA, applySaturation } from '../Processor/ToneMapCPU.js';
|
|
3
|
+
import { fetchAsWorker } from '../Processor/Workers/fetchAsWorker.js';
|
|
4
|
+
import AI_UPSCALER_WORKER_URL from '../Processor/Workers/AIUpscalerWorker.js?worker&url';
|
|
3
5
|
|
|
4
6
|
|
|
5
7
|
// ─── Model Configuration ───────────────────────────────────────────────────────
|
|
@@ -137,10 +139,16 @@ export class AIUpscaler extends EventDispatcher {
|
|
|
137
139
|
// Create worker on first use
|
|
138
140
|
if ( ! this._worker ) {
|
|
139
141
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
{ type: 'module' }
|
|
143
|
-
|
|
142
|
+
try {
|
|
143
|
+
|
|
144
|
+
this._worker = new Worker( AI_UPSCALER_WORKER_URL, { type: 'module' } );
|
|
145
|
+
|
|
146
|
+
} catch ( e ) {
|
|
147
|
+
|
|
148
|
+
if ( e.name !== 'SecurityError' ) throw e;
|
|
149
|
+
this._worker = await fetchAsWorker( AI_UPSCALER_WORKER_URL );
|
|
150
|
+
|
|
151
|
+
}
|
|
144
152
|
|
|
145
153
|
}
|
|
146
154
|
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { TreeletOptimizer } from './TreeletOptimizer.js';
|
|
2
2
|
import { ReinsertionOptimizer } from './ReinsertionOptimizer.js';
|
|
3
|
+
import { fetchAsWorker } from './Workers/fetchAsWorker.js';
|
|
4
|
+
import BVH_WORKER_URL from './Workers/BVHWorker.js?worker&url';
|
|
3
5
|
|
|
4
6
|
// Inline copy of TRIANGLE_DATA_LAYOUT (mirrors Constants.js).
|
|
5
7
|
// Cannot import Constants.js because BVHBuilder runs inside BVHWorker
|
|
@@ -397,12 +399,7 @@ export class BVHBuilder {
|
|
|
397
399
|
|
|
398
400
|
return new Promise( ( resolve, reject ) => {
|
|
399
401
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
const worker = new Worker(
|
|
403
|
-
new URL( './Workers/BVHWorker.js', import.meta.url ),
|
|
404
|
-
{ type: 'module' }
|
|
405
|
-
);
|
|
402
|
+
const setupWorker = ( worker ) => {
|
|
406
403
|
|
|
407
404
|
const triangleCount = this.totalTriangles;
|
|
408
405
|
const useShared = typeof SharedArrayBuffer !== 'undefined';
|
|
@@ -482,10 +479,29 @@ export class BVHBuilder {
|
|
|
482
479
|
|
|
483
480
|
worker.postMessage( workerData, [ transferBuffer ] );
|
|
484
481
|
|
|
482
|
+
};
|
|
483
|
+
|
|
484
|
+
try {
|
|
485
|
+
|
|
486
|
+
setupWorker( new Worker( BVH_WORKER_URL, { type: 'module' } ) );
|
|
487
|
+
|
|
485
488
|
} catch ( error ) {
|
|
486
489
|
|
|
487
|
-
|
|
488
|
-
|
|
490
|
+
if ( error.name === 'SecurityError' ) {
|
|
491
|
+
|
|
492
|
+
fetchAsWorker( BVH_WORKER_URL ).then( setupWorker ).catch( () => {
|
|
493
|
+
|
|
494
|
+
console.warn( 'Worker fetch fallback failed, using synchronous build' );
|
|
495
|
+
resolve( this._buildSyncAndFlatten( triangles, depth, progressCallback ) );
|
|
496
|
+
|
|
497
|
+
} );
|
|
498
|
+
|
|
499
|
+
} else {
|
|
500
|
+
|
|
501
|
+
console.warn( 'Worker creation failed, falling back to synchronous build:', error );
|
|
502
|
+
resolve( this._buildSyncAndFlatten( triangles, depth, progressCallback ) );
|
|
503
|
+
|
|
504
|
+
}
|
|
489
505
|
|
|
490
506
|
}
|
|
491
507
|
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { DataUtils, HalfFloatType, FloatType, SRGBColorSpace } from 'three';
|
|
2
|
+
import { fetchAsWorker } from './Workers/fetchAsWorker.js';
|
|
3
|
+
import CDF_WORKER_URL from './Workers/CDFWorker.js?worker&url';
|
|
2
4
|
|
|
3
5
|
/**
|
|
4
6
|
* Binary search to find the closest index
|
|
@@ -221,10 +223,16 @@ export class EquirectHDRInfo {
|
|
|
221
223
|
// Reuse worker across calls; create on first use
|
|
222
224
|
if ( ! this._worker ) {
|
|
223
225
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
{ type: 'module' }
|
|
227
|
-
|
|
226
|
+
try {
|
|
227
|
+
|
|
228
|
+
this._worker = new Worker( CDF_WORKER_URL, { type: 'module' } );
|
|
229
|
+
|
|
230
|
+
} catch ( e ) {
|
|
231
|
+
|
|
232
|
+
if ( e.name !== 'SecurityError' ) throw e;
|
|
233
|
+
this._worker = await fetchAsWorker( CDF_WORKER_URL );
|
|
234
|
+
|
|
235
|
+
}
|
|
228
236
|
|
|
229
237
|
}
|
|
230
238
|
|
|
@@ -7,6 +7,10 @@
|
|
|
7
7
|
* a cycle that Vite cannot resolve.
|
|
8
8
|
*/
|
|
9
9
|
|
|
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';
|
|
13
|
+
|
|
10
14
|
const FPT = 32; // FLOATS_PER_TRIANGLE
|
|
11
15
|
const PARALLEL_THRESHOLD = 50000;
|
|
12
16
|
const MAX_PARALLEL_WORKERS = 8;
|
|
@@ -34,7 +38,7 @@ export function buildBVHParallel( triangles, depth, progressCallback, config ) {
|
|
|
34
38
|
|
|
35
39
|
return new Promise( ( resolve, reject ) => {
|
|
36
40
|
|
|
37
|
-
|
|
41
|
+
( async () => {
|
|
38
42
|
|
|
39
43
|
// Allocate SharedArrayBuffers
|
|
40
44
|
const sharedTriangleData = new SharedArrayBuffer( triangles.byteLength );
|
|
@@ -48,10 +52,17 @@ export function buildBVHParallel( triangles, depth, progressCallback, config ) {
|
|
|
48
52
|
const sharedReorderBuffer = new SharedArrayBuffer( triangleCount * FPT * 4 );
|
|
49
53
|
|
|
50
54
|
// Phase 1: Coordinator worker
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
+
let coordinatorWorker;
|
|
56
|
+
try {
|
|
57
|
+
|
|
58
|
+
coordinatorWorker = new Worker( BVH_WORKER_URL, { type: 'module' } );
|
|
59
|
+
|
|
60
|
+
} catch ( e ) {
|
|
61
|
+
|
|
62
|
+
if ( e.name !== 'SecurityError' ) throw e;
|
|
63
|
+
coordinatorWorker = await fetchAsWorker( BVH_WORKER_URL );
|
|
64
|
+
|
|
65
|
+
}
|
|
55
66
|
|
|
56
67
|
let phase1Stats = null;
|
|
57
68
|
const allWorkers = [ coordinatorWorker ];
|
|
@@ -133,7 +144,7 @@ export function buildBVHParallel( triangles, depth, progressCallback, config ) {
|
|
|
133
144
|
triangleCount, progressCallback, coordinatorWorker,
|
|
134
145
|
allWorkers, cleanup, fallbackToSingle, resolve, timerRef,
|
|
135
146
|
config
|
|
136
|
-
);
|
|
147
|
+
).catch( err => fallbackToSingle( err.message ) );
|
|
137
148
|
return;
|
|
138
149
|
|
|
139
150
|
}
|
|
@@ -169,12 +180,12 @@ export function buildBVHParallel( triangles, depth, progressCallback, config ) {
|
|
|
169
180
|
treeletOptimization: config.treeletOptimization
|
|
170
181
|
} );
|
|
171
182
|
|
|
172
|
-
} catch ( error ) {
|
|
183
|
+
} )().catch( ( error ) => {
|
|
173
184
|
|
|
174
185
|
console.warn( '[ParallelBVH] Parallel build setup failed:', error );
|
|
175
186
|
reject( error );
|
|
176
187
|
|
|
177
|
-
}
|
|
188
|
+
} );
|
|
178
189
|
|
|
179
190
|
} );
|
|
180
191
|
|
|
@@ -184,7 +195,7 @@ export function buildBVHParallel( triangles, depth, progressCallback, config ) {
|
|
|
184
195
|
* Handle Phase 2: distribute subtree tasks to worker pool and collect results.
|
|
185
196
|
* @private
|
|
186
197
|
*/
|
|
187
|
-
function handlePhase2(
|
|
198
|
+
async function handlePhase2(
|
|
188
199
|
phase1Result, numWorkers, sharedTriangleData, sharedCentroids,
|
|
189
200
|
sharedBMin, sharedBMax, sharedIndices, sharedReorderBuffer,
|
|
190
201
|
triangleCount, progressCallback, coordinatorWorker,
|
|
@@ -299,10 +310,17 @@ function handlePhase2(
|
|
|
299
310
|
const bucket = workerTaskBuckets[ w ];
|
|
300
311
|
if ( bucket.length === 0 ) continue;
|
|
301
312
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
313
|
+
let subtreeWorker;
|
|
314
|
+
try {
|
|
315
|
+
|
|
316
|
+
subtreeWorker = new Worker( BVH_SUBTREE_WORKER_URL, { type: 'module' } );
|
|
317
|
+
|
|
318
|
+
} catch ( e ) {
|
|
319
|
+
|
|
320
|
+
if ( e.name !== 'SecurityError' ) throw e;
|
|
321
|
+
subtreeWorker = await fetchAsWorker( BVH_SUBTREE_WORKER_URL );
|
|
322
|
+
|
|
323
|
+
}
|
|
306
324
|
|
|
307
325
|
allWorkers.push( subtreeWorker );
|
|
308
326
|
|
|
@@ -405,12 +423,19 @@ function buildSingleWorker( triangles, depth, progressCallback, config ) {
|
|
|
405
423
|
|
|
406
424
|
return new Promise( ( resolve, reject ) => {
|
|
407
425
|
|
|
408
|
-
|
|
426
|
+
( async () => {
|
|
427
|
+
|
|
428
|
+
let worker;
|
|
429
|
+
try {
|
|
430
|
+
|
|
431
|
+
worker = new Worker( BVH_WORKER_URL, { type: 'module' } );
|
|
409
432
|
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
433
|
+
} catch ( e ) {
|
|
434
|
+
|
|
435
|
+
if ( e.name !== 'SecurityError' ) throw e;
|
|
436
|
+
worker = await fetchAsWorker( BVH_WORKER_URL );
|
|
437
|
+
|
|
438
|
+
}
|
|
414
439
|
|
|
415
440
|
const triangleCount = triangles.byteLength / ( FPT * 4 );
|
|
416
441
|
const useShared = typeof SharedArrayBuffer !== 'undefined';
|
|
@@ -466,12 +491,12 @@ function buildSingleWorker( triangles, depth, progressCallback, config ) {
|
|
|
466
491
|
reinsertionOptimization: config.reinsertionOptimization
|
|
467
492
|
}, [ transferBuffer ] );
|
|
468
493
|
|
|
469
|
-
} catch ( error ) {
|
|
494
|
+
} )().catch( ( error ) => {
|
|
470
495
|
|
|
471
496
|
console.warn( '[ParallelBVH] Single worker fallback failed:', error );
|
|
472
497
|
reject( error );
|
|
473
498
|
|
|
474
|
-
}
|
|
499
|
+
} );
|
|
475
500
|
|
|
476
501
|
} );
|
|
477
502
|
|
|
@@ -11,6 +11,9 @@ import { EmissiveTriangleBuilder } from './EmissiveTriangleBuilder.js';
|
|
|
11
11
|
import { updateLoading } from '../Processor/utils.js';
|
|
12
12
|
import { BuildTimer } from './BuildTimer.js';
|
|
13
13
|
import { TRIANGLE_DATA_LAYOUT } from '../EngineDefaults.js';
|
|
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';
|
|
14
17
|
|
|
15
18
|
/**
|
|
16
19
|
* SceneProcessor - Processes scene geometry into GPU-ready data:
|
|
@@ -663,24 +666,42 @@ export class SceneProcessor {
|
|
|
663
666
|
};
|
|
664
667
|
|
|
665
668
|
// Spin up the pool
|
|
666
|
-
|
|
669
|
+
( async () => {
|
|
667
670
|
|
|
668
|
-
|
|
669
|
-
new URL( './Workers/BVHWorker.js', import.meta.url ),
|
|
670
|
-
{ type: 'module' }
|
|
671
|
-
);
|
|
672
|
-
worker.onmessage = ( e ) => onWorkerMessage( worker, e );
|
|
673
|
-
worker.onerror = ( err ) => {
|
|
671
|
+
for ( let i = 0; i < poolSize; i ++ ) {
|
|
674
672
|
|
|
675
|
-
|
|
676
|
-
|
|
673
|
+
let worker;
|
|
674
|
+
try {
|
|
677
675
|
|
|
678
|
-
|
|
676
|
+
worker = new Worker( BVH_WORKER_URL, { type: 'module' } );
|
|
679
677
|
|
|
680
|
-
|
|
681
|
-
dispatchNext( worker );
|
|
678
|
+
} catch ( e ) {
|
|
682
679
|
|
|
683
|
-
|
|
680
|
+
if ( e.name !== 'SecurityError' ) {
|
|
681
|
+
|
|
682
|
+
reject( e );
|
|
683
|
+
return;
|
|
684
|
+
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
worker = await fetchAsWorker( BVH_WORKER_URL );
|
|
688
|
+
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
worker.onmessage = ( e ) => onWorkerMessage( worker, e );
|
|
692
|
+
worker.onerror = ( err ) => {
|
|
693
|
+
|
|
694
|
+
workers.forEach( w => w.terminate() );
|
|
695
|
+
reject( err );
|
|
696
|
+
|
|
697
|
+
};
|
|
698
|
+
|
|
699
|
+
workers.push( worker );
|
|
700
|
+
dispatchNext( worker );
|
|
701
|
+
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
} )().catch( reject );
|
|
684
705
|
|
|
685
706
|
} );
|
|
686
707
|
|
|
@@ -1136,10 +1157,16 @@ export class SceneProcessor {
|
|
|
1136
1157
|
// Lazy-create worker
|
|
1137
1158
|
if ( ! this._refitWorker ) {
|
|
1138
1159
|
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
{ type: 'module' }
|
|
1142
|
-
|
|
1160
|
+
try {
|
|
1161
|
+
|
|
1162
|
+
this._refitWorker = new Worker( BVH_REFIT_WORKER_URL, { type: 'module' } );
|
|
1163
|
+
|
|
1164
|
+
} catch ( e ) {
|
|
1165
|
+
|
|
1166
|
+
if ( e.name !== 'SecurityError' ) throw e;
|
|
1167
|
+
this._refitWorker = await fetchAsWorker( BVH_REFIT_WORKER_URL );
|
|
1168
|
+
|
|
1169
|
+
}
|
|
1143
1170
|
|
|
1144
1171
|
}
|
|
1145
1172
|
|
|
@@ -1655,26 +1682,13 @@ export class SceneProcessor {
|
|
|
1655
1682
|
this._rebuildGeneration ++;
|
|
1656
1683
|
const generation = this._rebuildGeneration;
|
|
1657
1684
|
|
|
1658
|
-
|
|
1685
|
+
const dispatchRebuild = ( meshIdx, entry, worker ) => {
|
|
1659
1686
|
|
|
1660
|
-
const entry = this.instanceTable.entries[ meshIdx ];
|
|
1661
|
-
if ( ! entry ) continue;
|
|
1662
|
-
|
|
1663
|
-
// Cancel any in-flight rebuild for this mesh
|
|
1664
|
-
const existing = this._pendingRebuilds.get( meshIdx );
|
|
1665
|
-
if ( existing ) existing.terminate();
|
|
1666
|
-
|
|
1667
|
-
// Copy current world-space triangle data for this mesh
|
|
1668
1687
|
const meshTriData = this.triangleData.slice(
|
|
1669
1688
|
entry.triOffset * FPT,
|
|
1670
1689
|
( entry.triOffset + entry.triCount ) * FPT
|
|
1671
1690
|
);
|
|
1672
1691
|
|
|
1673
|
-
const worker = new Worker(
|
|
1674
|
-
new URL( './Workers/BVHWorker.js', import.meta.url ),
|
|
1675
|
-
{ type: 'module' }
|
|
1676
|
-
);
|
|
1677
|
-
|
|
1678
1692
|
this._pendingRebuilds.set( meshIdx, worker );
|
|
1679
1693
|
|
|
1680
1694
|
worker.onmessage = ( e ) => {
|
|
@@ -1729,6 +1743,30 @@ export class SceneProcessor {
|
|
|
1729
1743
|
},
|
|
1730
1744
|
}, [ meshTriData.buffer ] );
|
|
1731
1745
|
|
|
1746
|
+
};
|
|
1747
|
+
|
|
1748
|
+
for ( const meshIdx of meshIndices ) {
|
|
1749
|
+
|
|
1750
|
+
const entry = this.instanceTable.entries[ meshIdx ];
|
|
1751
|
+
if ( ! entry ) continue;
|
|
1752
|
+
|
|
1753
|
+
// Cancel any in-flight rebuild for this mesh
|
|
1754
|
+
const existing = this._pendingRebuilds.get( meshIdx );
|
|
1755
|
+
if ( existing ) existing.terminate();
|
|
1756
|
+
|
|
1757
|
+
let worker;
|
|
1758
|
+
try {
|
|
1759
|
+
|
|
1760
|
+
worker = new Worker( BVH_WORKER_URL, { type: 'module' } );
|
|
1761
|
+
dispatchRebuild( meshIdx, entry, worker );
|
|
1762
|
+
|
|
1763
|
+
} catch ( e ) {
|
|
1764
|
+
|
|
1765
|
+
if ( e.name !== 'SecurityError' ) throw e;
|
|
1766
|
+
fetchAsWorker( BVH_WORKER_URL ).then( w => dispatchRebuild( meshIdx, entry, w ) );
|
|
1767
|
+
|
|
1768
|
+
}
|
|
1769
|
+
|
|
1732
1770
|
}
|
|
1733
1771
|
|
|
1734
1772
|
}
|
|
@@ -1,5 +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
|
+
import { fetchAsWorker } from './Workers/fetchAsWorker.js';
|
|
4
|
+
import TEXTURES_WORKER_URL from './Workers/TexturesWorker.js?worker&url';
|
|
3
5
|
|
|
4
6
|
// Canvas pooling for efficient reuse of canvas elements
|
|
5
7
|
class CanvasPool {
|
|
@@ -602,10 +604,17 @@ export class TextureCreator {
|
|
|
602
604
|
|
|
603
605
|
try {
|
|
604
606
|
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
607
|
+
let worker;
|
|
608
|
+
try {
|
|
609
|
+
|
|
610
|
+
worker = new Worker( TEXTURES_WORKER_URL, { type: 'module' } );
|
|
611
|
+
|
|
612
|
+
} catch ( e ) {
|
|
613
|
+
|
|
614
|
+
if ( e.name !== 'SecurityError' ) throw e;
|
|
615
|
+
worker = await fetchAsWorker( TEXTURES_WORKER_URL );
|
|
616
|
+
|
|
617
|
+
}
|
|
609
618
|
|
|
610
619
|
// Prepare textures for worker with direct transfer
|
|
611
620
|
const texturesData = await this.prepareTexturesForWorkerDirect( textures );
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-origin Worker fallback.
|
|
3
|
+
*
|
|
4
|
+
* Browsers enforce same-origin policy on `new Worker(url)`. When the
|
|
5
|
+
* library's JS is served from a CDN (different origin to the page),
|
|
6
|
+
* the standard constructor throws `SecurityError`.
|
|
7
|
+
*
|
|
8
|
+
* This helper fetches the script over the network (CORS-allowed) and
|
|
9
|
+
* re-hosts it as a same-origin Blob URL.
|
|
10
|
+
*
|
|
11
|
+
* The Blob URL is intentionally **not** revoked — workers may reference
|
|
12
|
+
* `self.location.href` to spawn sub-workers (e.g. BVHWorker).
|
|
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
|
+
*
|
|
19
|
+
* @param {URL|string} url Worker script URL (may be cross-origin)
|
|
20
|
+
* @returns {Promise<Worker>}
|
|
21
|
+
*/
|
|
22
|
+
export async function fetchAsWorker( url ) {
|
|
23
|
+
|
|
24
|
+
const href = url instanceof URL ? url.href : url;
|
|
25
|
+
const response = await fetch( href );
|
|
26
|
+
if ( ! response.ok ) {
|
|
27
|
+
|
|
28
|
+
throw new Error( `Failed to fetch worker script: ${response.status}` );
|
|
29
|
+
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const blob = new Blob( [ await response.text() ], { type: 'application/javascript' } );
|
|
33
|
+
return new Worker( URL.createObjectURL( blob ), { type: 'module' } );
|
|
34
|
+
|
|
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}};let r={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},i=r.FLOATS_PER_TRIANGLE;var a=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}},o=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=r.POSITION_A_OFFSET,a=r.POSITION_B_OFFSET,o=r.POSITION_C_OFFSET;for(let r=0;r<e;r++){let e=r*i,s=t[e+n],c=t[e+n+1],l=t[e+n+2],u=t[e+a],d=t[e+a+1],f=t[e+a+2],p=t[e+o],m=t[e+o+1],h=t[e+o+2],g=r*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[r]=r}}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/(i*4),this.processedTriangles=0,this.lastProgressUpdate=performance.now(),this.useWorker&&typeof Worker<`u`?new Promise((r,a)=>{try{let o=new Worker(new URL(``+new URL(`BVHWorker-BqQTDljT.js`,self.location.href).href,``+self.location.href),{type:`module`}),s=this.totalTriangles,c=typeof SharedArrayBuffer<`u`;console.log(`[BVHBuilder] SharedArrayBuffer: ${c?`enabled`:`unavailable (using transfer fallback)`}`);let l=c?new SharedArrayBuffer(s*i*4):null;o.onmessage=e=>{let{bvhData:t,triangles:i,originalToBvh:s,error:c,progress:u,treeletStats:d}=e.data;if(c){o.terminate(),a(Error(c));return}if(u!==void 0&&n){n(u);return}d&&(this.splitStats=d),o.terminate(),r({bvhData:t,bvhRoot:!0,reorderedTriangles:l?new Float32Array(l):i,originalToBvh:s||null})},o.onerror=e=>{o.terminate(),a(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}};o.postMessage(d,[u])}catch(i){console.warn(`Worker creation failed, falling back to synchronous build:`,i),r(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,a=null,o=null){let s=performance.now();this.totalNodes=0,this.processedTriangles=0,this.triangles=t,this.totalTriangles=t.byteLength/(i*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,a);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,i=new e(this.traversalCost,this.intersectionCost);i.setTreeletSize(n),i.setMinImprovement(this.treeletMinImprovement),i.setMaxTreelets(r);let o=performance.now();for(let e=0;e<this.treeletOptimizationPasses;e++){let t=a?t=>{a(`Treelet optimization pass ${e+1}/${this.treeletOptimizationPasses}: ${t}`)}:null;try{i.optimizeBVH(d,t)}catch(t){console.error(`TreeletOptimizer: Error in pass ${e+1}:`,t);break}let n=i.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=i.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=a?e=>{a(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*i);for(let e=0;e<c;e++){let t=this.indices[e]*i,n=e*i;h.set(m.subarray(t,t+i),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}`:``)),a&&a(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,o,s,c,l,u,d){let f=new a;this.totalNodes++;let p=t-e;if(o===void 0?this.updateNodeBounds(f,e,t):(f.minX=o,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,o,s,c,l,u){let d=new a;this.totalNodes++;let f=t-e;if(i===void 0?this.updateNodeBounds(d,e,t):(d.minX=i,d.minY=o,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:s,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 o;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(s),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-BoG4D6dP.js.map
|