rayzee 5.3.4 → 5.3.6
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-CTHfS54a.js +2 -0
- package/dist/assets/BVHSubtreeWorker-CTHfS54a.js.map +1 -0
- package/dist/assets/BVHWorker-BarjE67Z.js +2 -0
- package/dist/assets/BVHWorker-BarjE67Z.js.map +1 -0
- package/dist/rayzee.es.js +983 -910
- 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 +16 -4
- package/src/PathTracerApp.js +0 -4
- package/src/Processor/BVHBuilder.js +28 -8
- package/src/Processor/EquirectHDRInfo.js +16 -4
- package/src/Processor/ParallelBVHBuilder.js +58 -20
- package/src/Processor/SceneProcessor.js +76 -31
- package/src/Processor/TextureCreator.js +17 -4
- package/src/Processor/Workers/fetchAsWorker.js +30 -0
- package/src/managers/DenoisingManager.js +4 -17
- 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,6 @@
|
|
|
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';
|
|
3
4
|
|
|
4
5
|
|
|
5
6
|
// ─── Model Configuration ───────────────────────────────────────────────────────
|
|
@@ -137,10 +138,21 @@ export class AIUpscaler extends EventDispatcher {
|
|
|
137
138
|
// Create worker on first use
|
|
138
139
|
if ( ! this._worker ) {
|
|
139
140
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
141
|
+
try {
|
|
142
|
+
|
|
143
|
+
this._worker = new Worker(
|
|
144
|
+
new URL( '../Processor/Workers/AIUpscalerWorker.js', import.meta.url ),
|
|
145
|
+
{ type: 'module' }
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
} catch ( e ) {
|
|
149
|
+
|
|
150
|
+
if ( e.name !== 'SecurityError' ) throw e;
|
|
151
|
+
this._worker = await fetchAsWorker(
|
|
152
|
+
new URL( '../Processor/Workers/AIUpscalerWorker.js', import.meta.url )
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
}
|
|
144
156
|
|
|
145
157
|
}
|
|
146
158
|
|
package/src/PathTracerApp.js
CHANGED
|
@@ -645,8 +645,6 @@ export class PathTracerApp extends EventDispatcher {
|
|
|
645
645
|
this.cameraManager.camera.aspect = width / height;
|
|
646
646
|
this.cameraManager.camera.updateProjectionMatrix();
|
|
647
647
|
|
|
648
|
-
this.denoisingManager?.syncCanvasStyle( width, height );
|
|
649
|
-
|
|
650
648
|
// Overlay helpers always render at display resolution
|
|
651
649
|
const dpr = window.devicePixelRatio || 1;
|
|
652
650
|
this.overlayManager?.setSize(
|
|
@@ -679,8 +677,6 @@ export class PathTracerApp extends EventDispatcher {
|
|
|
679
677
|
|
|
680
678
|
setCanvasSize( width, height ) {
|
|
681
679
|
|
|
682
|
-
this.denoisingManager?.syncCanvasStyle( width, height );
|
|
683
|
-
|
|
684
680
|
if ( width === 0 || height === 0 ) return;
|
|
685
681
|
|
|
686
682
|
this.renderer.setPixelRatio( 1.0 );
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { TreeletOptimizer } from './TreeletOptimizer.js';
|
|
2
2
|
import { ReinsertionOptimizer } from './ReinsertionOptimizer.js';
|
|
3
|
+
import { fetchAsWorker } from './Workers/fetchAsWorker.js';
|
|
3
4
|
|
|
4
5
|
// Inline copy of TRIANGLE_DATA_LAYOUT (mirrors Constants.js).
|
|
5
6
|
// Cannot import Constants.js because BVHBuilder runs inside BVHWorker
|
|
@@ -397,12 +398,7 @@ export class BVHBuilder {
|
|
|
397
398
|
|
|
398
399
|
return new Promise( ( resolve, reject ) => {
|
|
399
400
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
const worker = new Worker(
|
|
403
|
-
new URL( './Workers/BVHWorker.js', import.meta.url ),
|
|
404
|
-
{ type: 'module' }
|
|
405
|
-
);
|
|
401
|
+
const setupWorker = ( worker ) => {
|
|
406
402
|
|
|
407
403
|
const triangleCount = this.totalTriangles;
|
|
408
404
|
const useShared = typeof SharedArrayBuffer !== 'undefined';
|
|
@@ -482,10 +478,34 @@ export class BVHBuilder {
|
|
|
482
478
|
|
|
483
479
|
worker.postMessage( workerData, [ transferBuffer ] );
|
|
484
480
|
|
|
481
|
+
};
|
|
482
|
+
|
|
483
|
+
try {
|
|
484
|
+
|
|
485
|
+
setupWorker( new Worker(
|
|
486
|
+
new URL( './Workers/BVHWorker.js', import.meta.url ),
|
|
487
|
+
{ type: 'module' }
|
|
488
|
+
) );
|
|
489
|
+
|
|
485
490
|
} catch ( error ) {
|
|
486
491
|
|
|
487
|
-
|
|
488
|
-
|
|
492
|
+
if ( error.name === 'SecurityError' ) {
|
|
493
|
+
|
|
494
|
+
fetchAsWorker(
|
|
495
|
+
new URL( './Workers/BVHWorker.js', import.meta.url )
|
|
496
|
+
).then( setupWorker ).catch( () => {
|
|
497
|
+
|
|
498
|
+
console.warn( 'Worker fetch fallback failed, using synchronous build' );
|
|
499
|
+
resolve( this._buildSyncAndFlatten( triangles, depth, progressCallback ) );
|
|
500
|
+
|
|
501
|
+
} );
|
|
502
|
+
|
|
503
|
+
} else {
|
|
504
|
+
|
|
505
|
+
console.warn( 'Worker creation failed, falling back to synchronous build:', error );
|
|
506
|
+
resolve( this._buildSyncAndFlatten( triangles, depth, progressCallback ) );
|
|
507
|
+
|
|
508
|
+
}
|
|
489
509
|
|
|
490
510
|
}
|
|
491
511
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { DataUtils, HalfFloatType, FloatType, SRGBColorSpace } from 'three';
|
|
2
|
+
import { fetchAsWorker } from './Workers/fetchAsWorker.js';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Binary search to find the closest index
|
|
@@ -221,10 +222,21 @@ export class EquirectHDRInfo {
|
|
|
221
222
|
// Reuse worker across calls; create on first use
|
|
222
223
|
if ( ! this._worker ) {
|
|
223
224
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
225
|
+
try {
|
|
226
|
+
|
|
227
|
+
this._worker = new Worker(
|
|
228
|
+
new URL( './Workers/CDFWorker.js', import.meta.url ),
|
|
229
|
+
{ type: 'module' }
|
|
230
|
+
);
|
|
231
|
+
|
|
232
|
+
} catch ( e ) {
|
|
233
|
+
|
|
234
|
+
if ( e.name !== 'SecurityError' ) throw e;
|
|
235
|
+
this._worker = await fetchAsWorker(
|
|
236
|
+
new URL( './Workers/CDFWorker.js', import.meta.url )
|
|
237
|
+
);
|
|
238
|
+
|
|
239
|
+
}
|
|
228
240
|
|
|
229
241
|
}
|
|
230
242
|
|
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
* a cycle that Vite cannot resolve.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
+
import { fetchAsWorker } from './Workers/fetchAsWorker.js';
|
|
11
|
+
|
|
10
12
|
const FPT = 32; // FLOATS_PER_TRIANGLE
|
|
11
13
|
const PARALLEL_THRESHOLD = 50000;
|
|
12
14
|
const MAX_PARALLEL_WORKERS = 8;
|
|
@@ -34,7 +36,7 @@ export function buildBVHParallel( triangles, depth, progressCallback, config ) {
|
|
|
34
36
|
|
|
35
37
|
return new Promise( ( resolve, reject ) => {
|
|
36
38
|
|
|
37
|
-
|
|
39
|
+
( async () => {
|
|
38
40
|
|
|
39
41
|
// Allocate SharedArrayBuffers
|
|
40
42
|
const sharedTriangleData = new SharedArrayBuffer( triangles.byteLength );
|
|
@@ -48,10 +50,22 @@ export function buildBVHParallel( triangles, depth, progressCallback, config ) {
|
|
|
48
50
|
const sharedReorderBuffer = new SharedArrayBuffer( triangleCount * FPT * 4 );
|
|
49
51
|
|
|
50
52
|
// Phase 1: Coordinator worker
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
53
|
+
let coordinatorWorker;
|
|
54
|
+
try {
|
|
55
|
+
|
|
56
|
+
coordinatorWorker = new Worker(
|
|
57
|
+
new URL( './Workers/BVHWorker.js', import.meta.url ),
|
|
58
|
+
{ type: 'module' }
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
} catch ( e ) {
|
|
62
|
+
|
|
63
|
+
if ( e.name !== 'SecurityError' ) throw e;
|
|
64
|
+
coordinatorWorker = await fetchAsWorker(
|
|
65
|
+
new URL( './Workers/BVHWorker.js', import.meta.url )
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
}
|
|
55
69
|
|
|
56
70
|
let phase1Stats = null;
|
|
57
71
|
const allWorkers = [ coordinatorWorker ];
|
|
@@ -133,7 +147,7 @@ export function buildBVHParallel( triangles, depth, progressCallback, config ) {
|
|
|
133
147
|
triangleCount, progressCallback, coordinatorWorker,
|
|
134
148
|
allWorkers, cleanup, fallbackToSingle, resolve, timerRef,
|
|
135
149
|
config
|
|
136
|
-
);
|
|
150
|
+
).catch( err => fallbackToSingle( err.message ) );
|
|
137
151
|
return;
|
|
138
152
|
|
|
139
153
|
}
|
|
@@ -169,12 +183,12 @@ export function buildBVHParallel( triangles, depth, progressCallback, config ) {
|
|
|
169
183
|
treeletOptimization: config.treeletOptimization
|
|
170
184
|
} );
|
|
171
185
|
|
|
172
|
-
} catch ( error ) {
|
|
186
|
+
} )().catch( ( error ) => {
|
|
173
187
|
|
|
174
188
|
console.warn( '[ParallelBVH] Parallel build setup failed:', error );
|
|
175
189
|
reject( error );
|
|
176
190
|
|
|
177
|
-
}
|
|
191
|
+
} );
|
|
178
192
|
|
|
179
193
|
} );
|
|
180
194
|
|
|
@@ -184,7 +198,7 @@ export function buildBVHParallel( triangles, depth, progressCallback, config ) {
|
|
|
184
198
|
* Handle Phase 2: distribute subtree tasks to worker pool and collect results.
|
|
185
199
|
* @private
|
|
186
200
|
*/
|
|
187
|
-
function handlePhase2(
|
|
201
|
+
async function handlePhase2(
|
|
188
202
|
phase1Result, numWorkers, sharedTriangleData, sharedCentroids,
|
|
189
203
|
sharedBMin, sharedBMax, sharedIndices, sharedReorderBuffer,
|
|
190
204
|
triangleCount, progressCallback, coordinatorWorker,
|
|
@@ -299,10 +313,22 @@ function handlePhase2(
|
|
|
299
313
|
const bucket = workerTaskBuckets[ w ];
|
|
300
314
|
if ( bucket.length === 0 ) continue;
|
|
301
315
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
316
|
+
let subtreeWorker;
|
|
317
|
+
try {
|
|
318
|
+
|
|
319
|
+
subtreeWorker = new Worker(
|
|
320
|
+
new URL( './Workers/BVHSubtreeWorker.js', import.meta.url ),
|
|
321
|
+
{ type: 'module' }
|
|
322
|
+
);
|
|
323
|
+
|
|
324
|
+
} catch ( e ) {
|
|
325
|
+
|
|
326
|
+
if ( e.name !== 'SecurityError' ) throw e;
|
|
327
|
+
subtreeWorker = await fetchAsWorker(
|
|
328
|
+
new URL( './Workers/BVHSubtreeWorker.js', import.meta.url )
|
|
329
|
+
);
|
|
330
|
+
|
|
331
|
+
}
|
|
306
332
|
|
|
307
333
|
allWorkers.push( subtreeWorker );
|
|
308
334
|
|
|
@@ -405,12 +431,24 @@ function buildSingleWorker( triangles, depth, progressCallback, config ) {
|
|
|
405
431
|
|
|
406
432
|
return new Promise( ( resolve, reject ) => {
|
|
407
433
|
|
|
408
|
-
|
|
434
|
+
( async () => {
|
|
409
435
|
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
436
|
+
let worker;
|
|
437
|
+
try {
|
|
438
|
+
|
|
439
|
+
worker = new Worker(
|
|
440
|
+
new URL( './Workers/BVHWorker.js', import.meta.url ),
|
|
441
|
+
{ type: 'module' }
|
|
442
|
+
);
|
|
443
|
+
|
|
444
|
+
} catch ( e ) {
|
|
445
|
+
|
|
446
|
+
if ( e.name !== 'SecurityError' ) throw e;
|
|
447
|
+
worker = await fetchAsWorker(
|
|
448
|
+
new URL( './Workers/BVHWorker.js', import.meta.url )
|
|
449
|
+
);
|
|
450
|
+
|
|
451
|
+
}
|
|
414
452
|
|
|
415
453
|
const triangleCount = triangles.byteLength / ( FPT * 4 );
|
|
416
454
|
const useShared = typeof SharedArrayBuffer !== 'undefined';
|
|
@@ -466,12 +504,12 @@ function buildSingleWorker( triangles, depth, progressCallback, config ) {
|
|
|
466
504
|
reinsertionOptimization: config.reinsertionOptimization
|
|
467
505
|
}, [ transferBuffer ] );
|
|
468
506
|
|
|
469
|
-
} catch ( error ) {
|
|
507
|
+
} )().catch( ( error ) => {
|
|
470
508
|
|
|
471
509
|
console.warn( '[ParallelBVH] Single worker fallback failed:', error );
|
|
472
510
|
reject( error );
|
|
473
511
|
|
|
474
|
-
}
|
|
512
|
+
} );
|
|
475
513
|
|
|
476
514
|
} );
|
|
477
515
|
|
|
@@ -11,6 +11,7 @@ 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';
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
17
|
* SceneProcessor - Processes scene geometry into GPU-ready data:
|
|
@@ -663,24 +664,41 @@ export class SceneProcessor {
|
|
|
663
664
|
};
|
|
664
665
|
|
|
665
666
|
// Spin up the pool
|
|
666
|
-
|
|
667
|
+
( async () => {
|
|
667
668
|
|
|
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 ) => {
|
|
669
|
+
for ( let i = 0; i < poolSize; i ++ ) {
|
|
674
670
|
|
|
675
|
-
|
|
676
|
-
|
|
671
|
+
let worker;
|
|
672
|
+
try {
|
|
677
673
|
|
|
678
|
-
|
|
674
|
+
worker = new Worker(
|
|
675
|
+
new URL( './Workers/BVHWorker.js', import.meta.url ),
|
|
676
|
+
{ type: 'module' }
|
|
677
|
+
);
|
|
679
678
|
|
|
680
|
-
|
|
681
|
-
dispatchNext( worker );
|
|
679
|
+
} catch ( e ) {
|
|
682
680
|
|
|
683
|
-
|
|
681
|
+
if ( e.name !== 'SecurityError' ) { reject( e ); return; }
|
|
682
|
+
worker = await fetchAsWorker(
|
|
683
|
+
new URL( './Workers/BVHWorker.js', import.meta.url )
|
|
684
|
+
);
|
|
685
|
+
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
worker.onmessage = ( e ) => onWorkerMessage( worker, e );
|
|
689
|
+
worker.onerror = ( err ) => {
|
|
690
|
+
|
|
691
|
+
workers.forEach( w => w.terminate() );
|
|
692
|
+
reject( err );
|
|
693
|
+
|
|
694
|
+
};
|
|
695
|
+
|
|
696
|
+
workers.push( worker );
|
|
697
|
+
dispatchNext( worker );
|
|
698
|
+
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
} )().catch( reject );
|
|
684
702
|
|
|
685
703
|
} );
|
|
686
704
|
|
|
@@ -1136,10 +1154,21 @@ export class SceneProcessor {
|
|
|
1136
1154
|
// Lazy-create worker
|
|
1137
1155
|
if ( ! this._refitWorker ) {
|
|
1138
1156
|
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1157
|
+
try {
|
|
1158
|
+
|
|
1159
|
+
this._refitWorker = new Worker(
|
|
1160
|
+
new URL( './Workers/BVHRefitWorker.js', import.meta.url ),
|
|
1161
|
+
{ type: 'module' }
|
|
1162
|
+
);
|
|
1163
|
+
|
|
1164
|
+
} catch ( e ) {
|
|
1165
|
+
|
|
1166
|
+
if ( e.name !== 'SecurityError' ) throw e;
|
|
1167
|
+
this._refitWorker = await fetchAsWorker(
|
|
1168
|
+
new URL( './Workers/BVHRefitWorker.js', import.meta.url )
|
|
1169
|
+
);
|
|
1170
|
+
|
|
1171
|
+
}
|
|
1143
1172
|
|
|
1144
1173
|
}
|
|
1145
1174
|
|
|
@@ -1655,26 +1684,13 @@ export class SceneProcessor {
|
|
|
1655
1684
|
this._rebuildGeneration ++;
|
|
1656
1685
|
const generation = this._rebuildGeneration;
|
|
1657
1686
|
|
|
1658
|
-
|
|
1659
|
-
|
|
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();
|
|
1687
|
+
const dispatchRebuild = ( meshIdx, entry, worker ) => {
|
|
1666
1688
|
|
|
1667
|
-
// Copy current world-space triangle data for this mesh
|
|
1668
1689
|
const meshTriData = this.triangleData.slice(
|
|
1669
1690
|
entry.triOffset * FPT,
|
|
1670
1691
|
( entry.triOffset + entry.triCount ) * FPT
|
|
1671
1692
|
);
|
|
1672
1693
|
|
|
1673
|
-
const worker = new Worker(
|
|
1674
|
-
new URL( './Workers/BVHWorker.js', import.meta.url ),
|
|
1675
|
-
{ type: 'module' }
|
|
1676
|
-
);
|
|
1677
|
-
|
|
1678
1694
|
this._pendingRebuilds.set( meshIdx, worker );
|
|
1679
1695
|
|
|
1680
1696
|
worker.onmessage = ( e ) => {
|
|
@@ -1729,6 +1745,35 @@ export class SceneProcessor {
|
|
|
1729
1745
|
},
|
|
1730
1746
|
}, [ meshTriData.buffer ] );
|
|
1731
1747
|
|
|
1748
|
+
};
|
|
1749
|
+
|
|
1750
|
+
for ( const meshIdx of meshIndices ) {
|
|
1751
|
+
|
|
1752
|
+
const entry = this.instanceTable.entries[ meshIdx ];
|
|
1753
|
+
if ( ! entry ) continue;
|
|
1754
|
+
|
|
1755
|
+
// Cancel any in-flight rebuild for this mesh
|
|
1756
|
+
const existing = this._pendingRebuilds.get( meshIdx );
|
|
1757
|
+
if ( existing ) existing.terminate();
|
|
1758
|
+
|
|
1759
|
+
let worker;
|
|
1760
|
+
try {
|
|
1761
|
+
|
|
1762
|
+
worker = new Worker(
|
|
1763
|
+
new URL( './Workers/BVHWorker.js', import.meta.url ),
|
|
1764
|
+
{ type: 'module' }
|
|
1765
|
+
);
|
|
1766
|
+
dispatchRebuild( meshIdx, entry, worker );
|
|
1767
|
+
|
|
1768
|
+
} catch ( e ) {
|
|
1769
|
+
|
|
1770
|
+
if ( e.name !== 'SecurityError' ) throw e;
|
|
1771
|
+
fetchAsWorker(
|
|
1772
|
+
new URL( './Workers/BVHWorker.js', import.meta.url )
|
|
1773
|
+
).then( w => dispatchRebuild( meshIdx, entry, w ) );
|
|
1774
|
+
|
|
1775
|
+
}
|
|
1776
|
+
|
|
1732
1777
|
}
|
|
1733
1778
|
|
|
1734
1779
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
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';
|
|
3
4
|
|
|
4
5
|
// Canvas pooling for efficient reuse of canvas elements
|
|
5
6
|
class CanvasPool {
|
|
@@ -602,10 +603,22 @@ export class TextureCreator {
|
|
|
602
603
|
|
|
603
604
|
try {
|
|
604
605
|
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
606
|
+
let worker;
|
|
607
|
+
try {
|
|
608
|
+
|
|
609
|
+
worker = new Worker(
|
|
610
|
+
new URL( './Workers/TexturesWorker.js', import.meta.url ),
|
|
611
|
+
{ type: 'module' }
|
|
612
|
+
);
|
|
613
|
+
|
|
614
|
+
} catch ( e ) {
|
|
615
|
+
|
|
616
|
+
if ( e.name !== 'SecurityError' ) throw e;
|
|
617
|
+
worker = await fetchAsWorker(
|
|
618
|
+
new URL( './Workers/TexturesWorker.js', import.meta.url )
|
|
619
|
+
);
|
|
620
|
+
|
|
621
|
+
}
|
|
609
622
|
|
|
610
623
|
// Prepare textures for worker with direct transfer
|
|
611
624
|
const texturesData = await this.prepareTexturesForWorkerDirect( textures );
|
|
@@ -0,0 +1,30 @@
|
|
|
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
|
+
* @param {URL|string} url Worker script URL (may be cross-origin)
|
|
15
|
+
* @returns {Promise<Worker>}
|
|
16
|
+
*/
|
|
17
|
+
export async function fetchAsWorker( url ) {
|
|
18
|
+
|
|
19
|
+
const href = url instanceof URL ? url.href : url;
|
|
20
|
+
const response = await fetch( href );
|
|
21
|
+
if ( ! response.ok ) {
|
|
22
|
+
|
|
23
|
+
throw new Error( `Failed to fetch worker script: ${response.status}` );
|
|
24
|
+
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const blob = new Blob( [ await response.text() ], { type: 'application/javascript' } );
|
|
28
|
+
return new Worker( URL.createObjectURL( blob ) );
|
|
29
|
+
|
|
30
|
+
}
|
|
@@ -64,8 +64,10 @@ export class DenoisingManager extends EventDispatcher {
|
|
|
64
64
|
const dc = document.createElement( 'canvas' );
|
|
65
65
|
dc.width = mainCanvas.width;
|
|
66
66
|
dc.height = mainCanvas.height;
|
|
67
|
-
dc.style.
|
|
68
|
-
dc.style.
|
|
67
|
+
dc.style.position = 'absolute';
|
|
68
|
+
dc.style.inset = '0';
|
|
69
|
+
dc.style.width = '100%';
|
|
70
|
+
dc.style.height = '100%';
|
|
69
71
|
|
|
70
72
|
parent.insertBefore( dc, mainCanvas );
|
|
71
73
|
return dc;
|
|
@@ -86,21 +88,6 @@ export class DenoisingManager extends EventDispatcher {
|
|
|
86
88
|
|
|
87
89
|
}
|
|
88
90
|
|
|
89
|
-
/**
|
|
90
|
-
* Syncs the denoiser canvas CSS dimensions to match display size.
|
|
91
|
-
* @param {number} width
|
|
92
|
-
* @param {number} height
|
|
93
|
-
*/
|
|
94
|
-
syncCanvasStyle( width, height ) {
|
|
95
|
-
|
|
96
|
-
if ( this.denoiserCanvas ) {
|
|
97
|
-
|
|
98
|
-
this.denoiserCanvas.style.width = `${width}px`;
|
|
99
|
-
this.denoiserCanvas.style.height = `${height}px`;
|
|
100
|
-
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
}
|
|
104
91
|
|
|
105
92
|
/**
|
|
106
93
|
* Restores the denoiser canvas to base render resolution after upscaling.
|
|
@@ -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
|