rayzee 7.20.0 → 7.20.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rayzee",
3
- "version": "7.20.0",
3
+ "version": "7.20.2",
4
4
  "type": "module",
5
5
  "description": "Real-time WebGPU path tracing engine built on Three.js",
6
6
  "main": "dist/rayzee.umd.js",
@@ -13,7 +13,8 @@
13
13
  },
14
14
  "files": [
15
15
  "dist",
16
- "src"
16
+ "src",
17
+ "!dist/**/*.map"
17
18
  ],
18
19
  "repository": {
19
20
  "type": "git",
@@ -30,7 +31,7 @@
30
31
  "author": "Atul Mourya",
31
32
  "license": "MIT",
32
33
  "scripts": {
33
- "build": "vite build && node scripts/update-size-badge.mjs",
34
+ "build": "vite build",
34
35
  "prepublishOnly": "npm run build"
35
36
  },
36
37
  "peerDependencies": {
@@ -1,8 +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
3
  import { getAssetConfig } from '../AssetConfig.js';
5
- import AI_UPSCALER_WORKER_URL from '../Processor/Workers/AIUpscalerWorker.js?worker&url';
4
+ import AIUpscalerWorker from '../Processor/Workers/AIUpscalerWorker.js?worker&inline';
6
5
 
7
6
 
8
7
  // ─── Model Configuration ───────────────────────────────────────────────────────
@@ -166,16 +165,7 @@ export class AIUpscaler extends EventDispatcher {
166
165
  // Create worker on first use
167
166
  if ( ! this._worker ) {
168
167
 
169
- try {
170
-
171
- this._worker = new Worker( AI_UPSCALER_WORKER_URL, { type: 'module' } );
172
-
173
- } catch ( e ) {
174
-
175
- if ( e.name !== 'SecurityError' ) throw e;
176
- this._worker = await fetchAsWorker( AI_UPSCALER_WORKER_URL );
177
-
178
- }
168
+ this._worker = new AIUpscalerWorker();
179
169
 
180
170
  }
181
171
 
@@ -1,12 +1,20 @@
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';
5
3
  // Logger is worker-safe (globalThis only, storage access guarded), unlike Constants.js below.
6
4
  import { createLogger, fmt } from '../utils/Logger.js';
7
5
 
8
6
  const log = createLogger( 'bvh' );
9
7
 
8
+ // Injected, not imported: this module also runs inside the worker, and `?worker&inline`
9
+ // would embed a second copy of BVHWorker's source there.
10
+ let createBVHWorker = null;
11
+
12
+ export function setBVHWorkerFactory( factory ) {
13
+
14
+ createBVHWorker = factory;
15
+
16
+ }
17
+
10
18
  // Inline copy of TRIANGLE_DATA_LAYOUT (mirrors Constants.js).
11
19
  // Cannot import Constants.js because BVHBuilder runs inside BVHWorker
12
20
  // where `window` (used elsewhere in Constants.js) is not defined.
@@ -399,7 +407,7 @@ export class BVHBuilder {
399
407
  this.processedTriangles = 0;
400
408
  this.lastProgressUpdate = performance.now();
401
409
 
402
- if ( this.useWorker && typeof Worker !== 'undefined' ) {
410
+ if ( this.useWorker && createBVHWorker && typeof Worker !== 'undefined' ) {
403
411
 
404
412
  return new Promise( ( resolve, reject ) => {
405
413
 
@@ -487,25 +495,12 @@ export class BVHBuilder {
487
495
 
488
496
  try {
489
497
 
490
- setupWorker( new Worker( BVH_WORKER_URL, { type: 'module' } ) );
498
+ setupWorker( createBVHWorker() );
491
499
 
492
500
  } catch ( error ) {
493
501
 
494
- if ( error.name === 'SecurityError' ) {
495
-
496
- fetchAsWorker( BVH_WORKER_URL ).then( setupWorker ).catch( () => {
497
-
498
- log.warn( 'worker fetch fallback failed, using synchronous build' );
499
- resolve( this._buildSyncAndFlatten( triangles, depth, progressCallback ) );
500
-
501
- } );
502
-
503
- } else {
504
-
505
- log.warn( 'worker creation failed, falling back to synchronous build:', error );
506
- resolve( this._buildSyncAndFlatten( triangles, depth, progressCallback ) );
507
-
508
- }
502
+ log.warn( 'worker creation failed, falling back to synchronous build:', error );
503
+ resolve( this._buildSyncAndFlatten( triangles, depth, progressCallback ) );
509
504
 
510
505
  }
511
506
 
@@ -1,6 +1,5 @@
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
+ import CDFWorker from './Workers/CDFWorker.js?worker&inline';
4
3
 
5
4
  /**
6
5
  * Binary search to find the closest index
@@ -223,16 +222,7 @@ export class EquirectHDRInfo {
223
222
  const { floatData, width, height } = extractFloatData( hdr );
224
223
 
225
224
  // Fresh worker per call — terminated in finally to avoid ~30 MB residency.
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
- }
225
+ this._worker = new CDFWorker();
236
226
 
237
227
  try {
238
228
 
@@ -7,11 +7,13 @@
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';
10
+ import BVHWorker from './Workers/BVHWorker.js?worker&inline';
11
+ import BVHSubtreeWorker from './Workers/BVHSubtreeWorker.js?worker&inline';
12
+ import { setBVHWorkerFactory } from './BVHBuilder.js';
13
13
  import { createLogger, fmt } from '../utils/Logger.js';
14
14
 
15
+ setBVHWorkerFactory( () => new BVHWorker() );
16
+
15
17
  const log = createLogger( 'bvh' );
16
18
 
17
19
  const FPT = 32; // FLOATS_PER_TRIANGLE
@@ -55,17 +57,7 @@ export function buildBVHParallel( triangles, depth, progressCallback, config ) {
55
57
  const sharedReorderBuffer = new SharedArrayBuffer( triangleCount * FPT * 4 );
56
58
 
57
59
  // Phase 1: Coordinator worker
58
- let coordinatorWorker;
59
- try {
60
-
61
- coordinatorWorker = new Worker( BVH_WORKER_URL, { type: 'module' } );
62
-
63
- } catch ( e ) {
64
-
65
- if ( e.name !== 'SecurityError' ) throw e;
66
- coordinatorWorker = await fetchAsWorker( BVH_WORKER_URL );
67
-
68
- }
60
+ const coordinatorWorker = new BVHWorker();
69
61
 
70
62
  let phase1Stats = null;
71
63
  const allWorkers = [ coordinatorWorker ];
@@ -313,17 +305,7 @@ async function handlePhase2(
313
305
  const bucket = workerTaskBuckets[ w ];
314
306
  if ( bucket.length === 0 ) continue;
315
307
 
316
- let subtreeWorker;
317
- try {
318
-
319
- subtreeWorker = new Worker( BVH_SUBTREE_WORKER_URL, { type: 'module' } );
320
-
321
- } catch ( e ) {
322
-
323
- if ( e.name !== 'SecurityError' ) throw e;
324
- subtreeWorker = await fetchAsWorker( BVH_SUBTREE_WORKER_URL );
325
-
326
- }
308
+ const subtreeWorker = new BVHSubtreeWorker();
327
309
 
328
310
  allWorkers.push( subtreeWorker );
329
311
 
@@ -428,17 +410,7 @@ function buildSingleWorker( triangles, depth, progressCallback, config ) {
428
410
 
429
411
  ( async () => {
430
412
 
431
- let worker;
432
- try {
433
-
434
- worker = new Worker( BVH_WORKER_URL, { type: 'module' } );
435
-
436
- } catch ( e ) {
437
-
438
- if ( e.name !== 'SecurityError' ) throw e;
439
- worker = await fetchAsWorker( BVH_WORKER_URL );
440
-
441
- }
413
+ const worker = new BVHWorker();
442
414
 
443
415
  const triangleCount = triangles.byteLength / ( FPT * 4 );
444
416
  const useShared = typeof SharedArrayBuffer !== 'undefined';
@@ -12,9 +12,8 @@ import { BuildTimer } from './BuildTimer.js';
12
12
  import { createLogger, fmt, workerLogLevel } from '../utils/Logger.js';
13
13
  import { SRGBColorSpace } from 'three';
14
14
  import { TRIANGLE_DATA_LAYOUT, TEXTURE_CONSTANTS, getTextureBucketId, packTextureIndex } from '../EngineDefaults.js';
15
- import { fetchAsWorker } from './Workers/fetchAsWorker.js';
16
- import BVH_WORKER_URL from './Workers/BVHWorker.js?worker&url';
17
- import BVH_REFIT_WORKER_URL from './Workers/BVHRefitWorker.js?worker&url';
15
+ import BVHWorker from './Workers/BVHWorker.js?worker&inline';
16
+ import BVHRefitWorker from './Workers/BVHRefitWorker.js?worker&inline';
18
17
 
19
18
  const log = createLogger( 'scene' );
20
19
 
@@ -730,18 +729,12 @@ export class SceneProcessor {
730
729
  let worker;
731
730
  try {
732
731
 
733
- worker = new Worker( BVH_WORKER_URL, { type: 'module' } );
732
+ worker = new BVHWorker();
734
733
 
735
734
  } catch ( e ) {
736
735
 
737
- if ( e.name !== 'SecurityError' ) {
738
-
739
- reject( e );
740
- return;
741
-
742
- }
743
-
744
- worker = await fetchAsWorker( BVH_WORKER_URL );
736
+ reject( e );
737
+ return;
745
738
 
746
739
  }
747
740
 
@@ -1331,16 +1324,7 @@ export class SceneProcessor {
1331
1324
  // Lazy-create worker
1332
1325
  if ( ! this._refitWorker ) {
1333
1326
 
1334
- try {
1335
-
1336
- this._refitWorker = new Worker( BVH_REFIT_WORKER_URL, { type: 'module' } );
1337
-
1338
- } catch ( e ) {
1339
-
1340
- if ( e.name !== 'SecurityError' ) throw e;
1341
- this._refitWorker = await fetchAsWorker( BVH_REFIT_WORKER_URL );
1342
-
1343
- }
1327
+ this._refitWorker = new BVHRefitWorker();
1344
1328
 
1345
1329
  }
1346
1330
 
@@ -1989,18 +1973,7 @@ export class SceneProcessor {
1989
1973
  const existing = this._pendingRebuilds.get( meshIdx );
1990
1974
  if ( existing ) existing.terminate();
1991
1975
 
1992
- let worker;
1993
- try {
1994
-
1995
- worker = new Worker( BVH_WORKER_URL, { type: 'module' } );
1996
- dispatchRebuild( meshIdx, entry, worker );
1997
-
1998
- } catch ( e ) {
1999
-
2000
- if ( e.name !== 'SecurityError' ) throw e;
2001
- fetchAsWorker( BVH_WORKER_URL ).then( w => dispatchRebuild( meshIdx, entry, w ) );
2002
-
2003
- }
1976
+ dispatchRebuild( meshIdx, entry, new BVHWorker() );
2004
1977
 
2005
1978
  }
2006
1979
 
@@ -1,7 +1,6 @@
1
1
  import { DataArrayTexture, RGBAFormat, LinearFilter, UnsignedByteType, SRGBColorSpace, RepeatWrapping } 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
+ import TexturesWorker from './Workers/TexturesWorker.js?worker&inline';
5
4
 
6
5
  // Canvas pooling for efficient reuse of canvas elements
7
6
  class CanvasPool {
@@ -641,17 +640,7 @@ export class TextureCreator {
641
640
 
642
641
  try {
643
642
 
644
- let worker;
645
- try {
646
-
647
- worker = new Worker( TEXTURES_WORKER_URL, { type: 'module' } );
648
-
649
- } catch ( e ) {
650
-
651
- if ( e.name !== 'SecurityError' ) throw e;
652
- worker = await fetchAsWorker( TEXTURES_WORKER_URL );
653
-
654
- }
643
+ const worker = new TexturesWorker();
655
644
 
656
645
  // Prepare textures for worker with direct transfer
657
646
  const texturesData = await this.prepareTexturesForWorkerDirect( textures );
@@ -1,2 +0,0 @@
1
- (function(){let e={ortRuntimeUrl:`https://cdn.jsdelivr.net/npm/onnxruntime-web@1.24.3/dist/ort.webgpu.bundle.min.mjs`,ortWasmPaths:`https://cdn.jsdelivr.net/npm/onnxruntime-web@1.24.3/dist/`,cacheNamespace:`rayzee`},t=null;async function n(){return t||(t=await import(e.ortRuntimeUrl),t.env.wasm.wasmPaths=e.ortWasmPaths,t.env.logLevel=`error`,t)}function r(){return`${e.cacheNamespace}:ai-upscaler-models`}let i=`models`,a=null,o=null;function s(){return new Promise((e,t)=>{let n=indexedDB.open(r(),1);n.onupgradeneeded=()=>n.result.createObjectStore(i),n.onsuccess=()=>e(n.result),n.onerror=()=>t(n.error)})}async function c(e){try{let t=await s();return await new Promise((n,r)=>{let a=t.transaction(i,`readonly`).objectStore(i).get(e);a.onsuccess=()=>n(a.result||null),a.onerror=()=>r(a.error)})}catch{return null}}async function l(e,t){try{let n=await s();await new Promise((r,a)=>{let o=n.transaction(i,`readwrite`);o.objectStore(i).put(t,e),o.oncomplete=()=>r(),o.onerror=()=>a(o.error)})}catch{}}async function u(e){let t=await c(e);if(t)return console.log(`AI Upscaler Worker: model loaded from cache (${(t.byteLength/1024/1024).toFixed(1)}MB)`),t;let n=await fetch(e);if(!n.ok)throw Error(`Failed to fetch model: ${n.status}`);let r=await n.arrayBuffer();return l(e,r.slice(0)),r}async function d(e,t){if(a&&o===e){self.postMessage({type:`loaded`,backend:`webgpu`});return}a&&=(await a.release(),null);let[r,i]=await Promise.all([u(e),n()]);a=await i.InferenceSession.create(r,t),o=e;let s=512;try{let e=await navigator.gpu?.requestAdapter(),t=await e?.requestAdapterInfo?.()||e?.info,n=/apple|swiftshader|llvmpipe/i.test(t?.vendor||``)||/apple|swiftshader/i.test(t?.architecture||``),r=t?.device?.toLowerCase?.()?.includes(`integrated`)||/intel.*iris|intel.*uhd|intel.*hd|amd.*vega|radeon.*graphics/i.test(t?.description||``);s=n?128:r?256:512,console.log(`AI Upscaler Worker: GPU="${t?.description||t?.device||`unknown`}", tileSize=${s}`)}catch{}let c=(r.byteLength/1024/1024).toFixed(1);console.log(`AI Upscaler Worker: model loaded (${c}MB), backend: webgpu`),self.postMessage({type:`loaded`,backend:`webgpu`,tileSize:s})}async function f(e,t,r,i){let o=await n(),s=a.inputNames[0],c=a.outputNames[0],l=new o.Tensor(`float32`,e,[1,3,r,t]),u=(await a.run({[s]:l}))[c].data;self.postMessage({type:`inferred`,outputData:u,id:i},[u.buffer])}self.onmessage=async t=>{let{type:n}=t.data;try{n===`load`?(t.data.ortRuntimeUrl&&(e.ortRuntimeUrl=t.data.ortRuntimeUrl),t.data.ortWasmPaths&&(e.ortWasmPaths=t.data.ortWasmPaths),t.data.cacheNamespace&&(e.cacheNamespace=t.data.cacheNamespace),await d(t.data.url,t.data.sessionOptions)):n===`infer`?await f(t.data.tileData,t.data.width,t.data.height,t.data.id):n===`dispose`&&a&&(await a.release(),a=null,o=null)}catch(e){self.postMessage({type:`error`,message:e.message,id:t.data?.id})}}})();
2
- //# sourceMappingURL=AIUpscalerWorker-AXN-lKWN.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"AIUpscalerWorker-AXN-lKWN.js","names":[],"sources":["../../src/Processor/Workers/AIUpscalerWorker.js"],"sourcesContent":["/**\n * Web Worker for AI Upscaler inference.\n * Handles ONNX model loading and tile-based inference off the main thread.\n *\n * Messages:\n * Main → Worker:\n * { type: 'load', url, sessionOptions } — load/switch model\n * { type: 'infer', tileData, width, height, id } — run inference on a tile\n * { type: 'dispose' } — release session\n *\n * Worker → Main:\n * { type: 'loaded', backend }\n * { type: 'inferred', outputData, id }\n * { type: 'error', message, id? }\n */\n\n// Asset config supplied via 'load' message from the main thread.\n// Defaults match the upstream Rayzee deployment; override via configureAssets().\nlet _assetConfig = {\n\tortRuntimeUrl: 'https://cdn.jsdelivr.net/npm/onnxruntime-web@1.24.3/dist/ort.webgpu.bundle.min.mjs',\n\tortWasmPaths: 'https://cdn.jsdelivr.net/npm/onnxruntime-web@1.24.3/dist/',\n\tcacheNamespace: 'rayzee',\n};\n\nlet ort = null;\n\nasync function getOrt() {\n\n\tif ( ort ) return ort;\n\n\tort = await import( /* @vite-ignore */ _assetConfig.ortRuntimeUrl );\n\n\t// WASM paths for CDN delivery — WebGPU EP still uses WASM for lightweight shape ops\n\tort.env.wasm.wasmPaths = _assetConfig.ortWasmPaths;\n\tort.env.logLevel = 'error';\n\n\treturn ort;\n\n}\n\n// IndexedDB names are namespaced so multiple consumers on the same origin don't collide.\nfunction getIdbName() {\n\n\treturn `${_assetConfig.cacheNamespace}:ai-upscaler-models`;\n\n}\n\nconst IDB_STORE = 'models';\n\nlet session = null;\nlet currentModelUrl = null;\n\n// ─── IndexedDB Model Cache ───────────────────────────────────────────────────\n\nfunction openDB() {\n\n\treturn new Promise( ( resolve, reject ) => {\n\n\t\tconst req = indexedDB.open( getIdbName(), 1 );\n\t\treq.onupgradeneeded = () => req.result.createObjectStore( IDB_STORE );\n\t\treq.onsuccess = () => resolve( req.result );\n\t\treq.onerror = () => reject( req.error );\n\n\t} );\n\n}\n\nasync function getCachedModel( url ) {\n\n\ttry {\n\n\t\tconst db = await openDB();\n\t\treturn await new Promise( ( resolve, reject ) => {\n\n\t\t\tconst tx = db.transaction( IDB_STORE, 'readonly' );\n\t\t\tconst req = tx.objectStore( IDB_STORE ).get( url );\n\t\t\treq.onsuccess = () => resolve( req.result || null );\n\t\t\treq.onerror = () => reject( req.error );\n\n\t\t} );\n\n\t} catch {\n\n\t\treturn null;\n\n\t}\n\n}\n\nasync function cacheModel( url, buffer ) {\n\n\ttry {\n\n\t\tconst db = await openDB();\n\t\tawait new Promise( ( resolve, reject ) => {\n\n\t\t\tconst tx = db.transaction( IDB_STORE, 'readwrite' );\n\t\t\ttx.objectStore( IDB_STORE ).put( buffer, url );\n\t\t\ttx.oncomplete = () => resolve();\n\t\t\ttx.onerror = () => reject( tx.error );\n\n\t\t} );\n\n\t} catch {\n\n\t\t// Cache write failure is non-fatal\n\t}\n\n}\n\n// ─── Model Loading ───────────────────────────────────────────────────────────\n\nasync function fetchModel( url ) {\n\n\t// Try IndexedDB cache first\n\tconst cached = await getCachedModel( url );\n\tif ( cached ) {\n\n\t\tconsole.log( `AI Upscaler Worker: model loaded from cache (${( cached.byteLength / 1024 / 1024 ).toFixed( 1 )}MB)` );\n\t\treturn cached;\n\n\t}\n\n\t// Network fetch + cache\n\tconst response = await fetch( url );\n\tif ( ! response.ok ) throw new Error( `Failed to fetch model: ${response.status}` );\n\tconst buffer = await response.arrayBuffer();\n\n\t// Cache in background (don't block session creation)\n\tcacheModel( url, buffer.slice( 0 ) );\n\n\treturn buffer;\n\n}\n\nasync function loadModel( url, sessionOptions ) {\n\n\tif ( session && currentModelUrl === url ) {\n\n\t\tconst backend = 'webgpu';\n\t\tself.postMessage( { type: 'loaded', backend } );\n\t\treturn;\n\n\t}\n\n\t// Dispose previous session\n\tif ( session ) {\n\n\t\tawait session.release();\n\t\tsession = null;\n\n\t}\n\n\tconst [ modelBuffer, ortLib ] = await Promise.all( [ fetchModel( url ), getOrt() ] );\n\n\tsession = await ortLib.InferenceSession.create( modelBuffer, sessionOptions );\n\tcurrentModelUrl = url;\n\n\t// Detect GPU and recommend tile size based on device type\n\tlet tileSize = 512; // default\n\ttry {\n\n\t\tconst adapter = await navigator.gpu?.requestAdapter();\n\t\tconst info = await adapter?.requestAdapterInfo?.() || adapter?.info;\n\t\tconst isMobile = /apple|swiftshader|llvmpipe/i.test( info?.vendor || '' )\n\t\t\t|| /apple|swiftshader/i.test( info?.architecture || '' );\n\t\tconst isIntegrated = info?.device?.toLowerCase?.()?.includes( 'integrated' )\n\t\t\t|| /intel.*iris|intel.*uhd|intel.*hd|amd.*vega|radeon.*graphics/i.test( info?.description || '' );\n\n\t\tif ( isMobile ) {\n\n\t\t\ttileSize = 128;\n\n\t\t} else if ( isIntegrated ) {\n\n\t\t\ttileSize = 256;\n\n\t\t} else {\n\n\t\t\ttileSize = 512;\n\n\t\t}\n\n\t\tconsole.log( `AI Upscaler Worker: GPU=\"${info?.description || info?.device || 'unknown'}\", tileSize=${tileSize}` );\n\n\t} catch { /* fallback to default */ }\n\n\tconst sizeMB = ( modelBuffer.byteLength / 1024 / 1024 ).toFixed( 1 );\n\tconsole.log( `AI Upscaler Worker: model loaded (${sizeMB}MB), backend: webgpu` );\n\n\tself.postMessage( { type: 'loaded', backend: 'webgpu', tileSize } );\n\n}\n\nasync function inferTile( tileData, width, height, id ) {\n\n\tconst ortLib = await getOrt();\n\tconst inputName = session.inputNames[ 0 ];\n\tconst outputName = session.outputNames[ 0 ];\n\tconst inputTensor = new ortLib.Tensor( 'float32', tileData, [ 1, 3, height, width ] );\n\n\tconst results = await session.run( { [ inputName ]: inputTensor } );\n\tconst outputData = results[ outputName ].data;\n\n\t// Transfer the output buffer (zero-copy)\n\tself.postMessage( { type: 'inferred', outputData, id }, [ outputData.buffer ] );\n\n}\n\nself.onmessage = async ( e ) => {\n\n\tconst { type } = e.data;\n\n\ttry {\n\n\t\tif ( type === 'load' ) {\n\n\t\t\t// Apply asset overrides from main thread before any network or cache access\n\t\t\tif ( e.data.ortRuntimeUrl ) _assetConfig.ortRuntimeUrl = e.data.ortRuntimeUrl;\n\t\t\tif ( e.data.ortWasmPaths ) _assetConfig.ortWasmPaths = e.data.ortWasmPaths;\n\t\t\tif ( e.data.cacheNamespace ) _assetConfig.cacheNamespace = e.data.cacheNamespace;\n\t\t\tawait loadModel( e.data.url, e.data.sessionOptions );\n\n\t\t} else if ( type === 'infer' ) {\n\n\t\t\tawait inferTile( e.data.tileData, e.data.width, e.data.height, e.data.id );\n\n\t\t} else if ( type === 'dispose' ) {\n\n\t\t\tif ( session ) {\n\n\t\t\t\tawait session.release();\n\t\t\t\tsession = null;\n\t\t\t\tcurrentModelUrl = null;\n\n\t\t\t}\n\n\t\t}\n\n\t} catch ( error ) {\n\n\t\tself.postMessage( { type: 'error', message: error.message, id: e.data?.id } );\n\n\t}\n\n};\n"],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- (function(){let e={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},t=e.FLOATS_PER_TRIANGLE,n=new class{constructor(){this._bounds=null,this._boundsNodeCount=0}updateTrianglePositions(n,r,i){let a=i.length;for(let o=0;o<a;o++){let a=i[o],s=o*t,c=a*9,l=r[c],u=r[c+1],d=r[c+2],f=r[c+3],p=r[c+4],m=r[c+5],h=r[c+6],g=r[c+7],_=r[c+8];n[s+e.POSITION_A_OFFSET]=l,n[s+e.POSITION_A_OFFSET+1]=u,n[s+e.POSITION_A_OFFSET+2]=d,n[s+e.POSITION_B_OFFSET]=f,n[s+e.POSITION_B_OFFSET+1]=p,n[s+e.POSITION_B_OFFSET+2]=m,n[s+e.POSITION_C_OFFSET]=h,n[s+e.POSITION_C_OFFSET+1]=g,n[s+e.POSITION_C_OFFSET+2]=_;let v=f-l,y=p-u,b=m-d,x=h-l,S=g-u,C=_-d,w=y*C-b*S,T=b*x-v*C,E=v*S-y*x;n[s+e.NORMAL_A_OFFSET]=w,n[s+e.NORMAL_A_OFFSET+1]=T,n[s+e.NORMAL_A_OFFSET+2]=E,n[s+e.NORMAL_B_OFFSET]=w,n[s+e.NORMAL_B_OFFSET+1]=T,n[s+e.NORMAL_B_OFFSET+2]=E,n[s+e.NORMAL_C_OFFSET]=w,n[s+e.NORMAL_C_OFFSET+1]=T,n[s+e.NORMAL_C_OFFSET+2]=E}}refitRange(n,r,i,a){a>this._boundsNodeCount&&(this._bounds=new Float32Array(a*6),this._boundsNodeCount=a);let o=this._bounds,s=i+a;for(let a=s-1;a>=i;a--){let s=a*16,c=(a-i)*6;if(n[s+3]===-1){let i=n[s],a=n[s+1],l=1/0,u=1/0,d=1/0,f=-1/0,p=-1/0,m=-1/0;for(let n=0;n<a;n++){let a=(i+n)*t,o=r[a+e.POSITION_A_OFFSET],s=r[a+e.POSITION_A_OFFSET+1],c=r[a+e.POSITION_A_OFFSET+2],h=r[a+e.POSITION_B_OFFSET],g=r[a+e.POSITION_B_OFFSET+1],_=r[a+e.POSITION_B_OFFSET+2],v=r[a+e.POSITION_C_OFFSET],y=r[a+e.POSITION_C_OFFSET+1],b=r[a+e.POSITION_C_OFFSET+2];l=Math.min(l,o,h,v),u=Math.min(u,s,g,y),d=Math.min(d,c,_,b),f=Math.max(f,o,h,v),p=Math.max(p,s,g,y),m=Math.max(m,c,_,b)}o[c]=l,o[c+1]=u,o[c+2]=d,o[c+3]=f,o[c+4]=p,o[c+5]=m}else{let e=n[s+3],t=n[s+7],r=(e-i)*6,a=(t-i)*6,l=o[r],u=o[r+1],d=o[r+2],f=o[r+3],p=o[r+4],m=o[r+5],h=o[a],g=o[a+1],_=o[a+2],v=o[a+3],y=o[a+4],b=o[a+5];n[s]=l,n[s+1]=u,n[s+2]=d,n[s+4]=f,n[s+5]=p,n[s+6]=m,n[s+8]=h,n[s+9]=g,n[s+10]=_,n[s+12]=v,n[s+13]=y,n[s+14]=b,o[c]=Math.min(l,h),o[c+1]=Math.min(u,g),o[c+2]=Math.min(d,_),o[c+3]=Math.max(f,v),o[c+4]=Math.max(p,y),o[c+5]=Math.max(m,b)}}}refit(n,r,i){i!==this._boundsNodeCount&&(this._bounds=new Float32Array(i*6),this._boundsNodeCount=i);let a=this._bounds;for(let o=i-1;o>=0;o--){let i=o*16,s=o*6,c=n[i+3];if(c===-1){let o=n[i],c=n[i+1],l=1/0,u=1/0,d=1/0,f=-1/0,p=-1/0,m=-1/0;for(let n=0;n<c;n++){let i=(o+n)*t,a=r[i+e.POSITION_A_OFFSET],s=r[i+e.POSITION_A_OFFSET+1],c=r[i+e.POSITION_A_OFFSET+2],h=r[i+e.POSITION_B_OFFSET],g=r[i+e.POSITION_B_OFFSET+1],_=r[i+e.POSITION_B_OFFSET+2],v=r[i+e.POSITION_C_OFFSET],y=r[i+e.POSITION_C_OFFSET+1],b=r[i+e.POSITION_C_OFFSET+2];l=Math.min(l,a,h,v),u=Math.min(u,s,g,y),d=Math.min(d,c,_,b),f=Math.max(f,a,h,v),p=Math.max(p,s,g,y),m=Math.max(m,c,_,b)}a[s]=l,a[s+1]=u,a[s+2]=d,a[s+3]=f,a[s+4]=p,a[s+5]=m}else if(c===-2){let e=n[i]*6;a[s]=a[e],a[s+1]=a[e+1],a[s+2]=a[e+2],a[s+3]=a[e+3],a[s+4]=a[e+4],a[s+5]=a[e+5]}else{let e=n[i+3],t=n[i+7],r=e*6,o=t*6,c=a[r],l=a[r+1],u=a[r+2],d=a[r+3],f=a[r+4],p=a[r+5],m=a[o],h=a[o+1],g=a[o+2],_=a[o+3],v=a[o+4],y=a[o+5];n[i]=c,n[i+1]=l,n[i+2]=u,n[i+4]=d,n[i+5]=f,n[i+6]=p,n[i+8]=m,n[i+9]=h,n[i+10]=g,n[i+12]=_,n[i+13]=v,n[i+14]=y,a[s]=Math.min(c,m),a[s+1]=Math.min(l,h),a[s+2]=Math.min(u,g),a[s+3]=Math.max(d,_),a[s+4]=Math.max(f,v),a[s+5]=Math.max(p,y)}}}},r=null,i=null,a=null,o=null,s=0;self.onmessage=function(e){let{type:t}=e.data;if(t===`init`){r=new Float32Array(e.data.sharedBvhBuf),i=new Float32Array(e.data.sharedTriBuf),a=new Float32Array(e.data.sharedPosBuf),o=e.data.bvhToOriginal,s=r.length/16;return}if(t===`refit`)try{let e=performance.now();n.updateTrianglePositions(i,a,o),n.refit(r,i,s),self.postMessage({type:`refitComplete`,refitTimeMs:performance.now()-e})}catch(e){console.error(`[BVHRefitWorker] Refit error:`,e),self.postMessage({type:`error`,error:e.message})}}})();
2
- //# sourceMappingURL=BVHRefitWorker-GkmNJYvb.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"BVHRefitWorker-GkmNJYvb.js","names":["FLOATS_PER_NODE"],"sources":["../../src/Processor/BVHRefitter.js","../../src/Processor/Workers/BVHRefitWorker.js"],"sourcesContent":["/**\n * BVHRefitter — Fast O(N) bottom-up BVH AABB refit for animated geometry.\n *\n * When mesh topology stays the same but vertex positions change (skeletal animation,\n * morph targets), this avoids the full O(N log N) SAH rebuild by recomputing only\n * the bounding boxes in the existing tree structure.\n *\n * Designed to run in both main thread and Web Worker contexts.\n */\n\n// Inline copy of layout constants (source of truth: EngineDefaults.js).\n// Cannot import because this runs inside Web Workers where window is not defined.\nconst TRIANGLE_DATA_LAYOUT = {\n\tFLOATS_PER_TRIANGLE: 32,\n\tPOSITION_A_OFFSET: 0,\n\tPOSITION_B_OFFSET: 4,\n\tPOSITION_C_OFFSET: 8,\n\tNORMAL_A_OFFSET: 12,\n\tNORMAL_B_OFFSET: 16,\n\tNORMAL_C_OFFSET: 20,\n};\n\nconst FPT = TRIANGLE_DATA_LAYOUT.FLOATS_PER_TRIANGLE;\nconst FLOATS_PER_NODE = 16; // 4 vec4s per BVH node\nconst LEAF_MARKER = - 1;\nconst BLAS_POINTER_MARKER = - 2;\n\nexport class BVHRefitter {\n\n\tconstructor() {\n\n\t\t// Reusable bounds buffer — cached across refit calls to avoid allocation per frame.\n\t\t// Resized only when nodeCount changes (i.e., new scene loaded).\n\t\tthis._bounds = null;\n\t\tthis._boundsNodeCount = 0;\n\n\t}\n\n\t/**\n\t * Update triangle positions in the BVH-reordered triangle array.\n\t * Iterates in BVH order (sequential writes, random reads) for cache efficiency.\n\t *\n\t * @param {Float32Array} triangleData - BVH-reordered triangle array (mutated in place)\n\t * @param {Float32Array} newPositions - 9 floats per triangle in ORIGINAL mesh order\n\t * @param {Uint32Array} bvhToOriginal - Map from BVH-order index to original tri index\n\t */\n\tupdateTrianglePositions( triangleData, newPositions, bvhToOriginal ) {\n\n\t\tconst triCount = bvhToOriginal.length;\n\n\t\tfor ( let bvhIdx = 0; bvhIdx < triCount; bvhIdx ++ ) {\n\n\t\t\tconst orig = bvhToOriginal[ bvhIdx ];\n\t\t\tconst dstOff = bvhIdx * FPT; // sequential writes\n\t\t\tconst srcOff = orig * 9;\n\n\t\t\tconst ax = newPositions[ srcOff ];\n\t\t\tconst ay = newPositions[ srcOff + 1 ];\n\t\t\tconst az = newPositions[ srcOff + 2 ];\n\t\t\tconst bx = newPositions[ srcOff + 3 ];\n\t\t\tconst by = newPositions[ srcOff + 4 ];\n\t\t\tconst bz = newPositions[ srcOff + 5 ];\n\t\t\tconst cx = newPositions[ srcOff + 6 ];\n\t\t\tconst cy = newPositions[ srcOff + 7 ];\n\t\t\tconst cz = newPositions[ srcOff + 8 ];\n\n\t\t\ttriangleData[ dstOff + TRIANGLE_DATA_LAYOUT.POSITION_A_OFFSET ] = ax;\n\t\t\ttriangleData[ dstOff + TRIANGLE_DATA_LAYOUT.POSITION_A_OFFSET + 1 ] = ay;\n\t\t\ttriangleData[ dstOff + TRIANGLE_DATA_LAYOUT.POSITION_A_OFFSET + 2 ] = az;\n\n\t\t\ttriangleData[ dstOff + TRIANGLE_DATA_LAYOUT.POSITION_B_OFFSET ] = bx;\n\t\t\ttriangleData[ dstOff + TRIANGLE_DATA_LAYOUT.POSITION_B_OFFSET + 1 ] = by;\n\t\t\ttriangleData[ dstOff + TRIANGLE_DATA_LAYOUT.POSITION_B_OFFSET + 2 ] = bz;\n\n\t\t\ttriangleData[ dstOff + TRIANGLE_DATA_LAYOUT.POSITION_C_OFFSET ] = cx;\n\t\t\ttriangleData[ dstOff + TRIANGLE_DATA_LAYOUT.POSITION_C_OFFSET + 1 ] = cy;\n\t\t\ttriangleData[ dstOff + TRIANGLE_DATA_LAYOUT.POSITION_C_OFFSET + 2 ] = cz;\n\n\t\t\t// Compute unnormalized face normal from cross product.\n\t\t\t// Skip sqrt normalization — the path tracer shader normalizes during shading.\n\t\t\tconst abx = bx - ax, aby = by - ay, abz = bz - az;\n\t\t\tconst acx = cx - ax, acy = cy - ay, acz = cz - az;\n\t\t\tconst nx = aby * acz - abz * acy;\n\t\t\tconst ny = abz * acx - abx * acz;\n\t\t\tconst nz = abx * acy - aby * acx;\n\n\t\t\ttriangleData[ dstOff + TRIANGLE_DATA_LAYOUT.NORMAL_A_OFFSET ] = nx;\n\t\t\ttriangleData[ dstOff + TRIANGLE_DATA_LAYOUT.NORMAL_A_OFFSET + 1 ] = ny;\n\t\t\ttriangleData[ dstOff + TRIANGLE_DATA_LAYOUT.NORMAL_A_OFFSET + 2 ] = nz;\n\n\t\t\ttriangleData[ dstOff + TRIANGLE_DATA_LAYOUT.NORMAL_B_OFFSET ] = nx;\n\t\t\ttriangleData[ dstOff + TRIANGLE_DATA_LAYOUT.NORMAL_B_OFFSET + 1 ] = ny;\n\t\t\ttriangleData[ dstOff + TRIANGLE_DATA_LAYOUT.NORMAL_B_OFFSET + 2 ] = nz;\n\n\t\t\ttriangleData[ dstOff + TRIANGLE_DATA_LAYOUT.NORMAL_C_OFFSET ] = nx;\n\t\t\ttriangleData[ dstOff + TRIANGLE_DATA_LAYOUT.NORMAL_C_OFFSET + 1 ] = ny;\n\t\t\ttriangleData[ dstOff + TRIANGLE_DATA_LAYOUT.NORMAL_C_OFFSET + 2 ] = nz;\n\n\t\t}\n\n\t}\n\n\t/**\n\t * Refit a BLAS sub-range within the combined BVH buffer.\n\t * Same algorithm as refit() but scoped to nodes [startNode, startNode + count).\n\t *\n\t * @param {Float32Array} bvhData - Combined BVH array (TLAS + all BLASes)\n\t * @param {Float32Array} triangleData - Global triangle data\n\t * @param {number} startNode - First node index of this BLAS in bvhData\n\t * @param {number} nodeCount - Number of nodes in this BLAS\n\t */\n\trefitRange( bvhData, triangleData, startNode, nodeCount ) {\n\n\t\t// Grow-only bounds buffer to avoid reallocation on mixed-size BLASes\n\t\tif ( nodeCount > this._boundsNodeCount ) {\n\n\t\t\tthis._bounds = new Float32Array( nodeCount * 6 );\n\t\t\tthis._boundsNodeCount = nodeCount;\n\n\t\t}\n\n\t\tconst bounds = this._bounds;\n\t\tconst endNode = startNode + nodeCount;\n\n\t\tfor ( let i = endNode - 1; i >= startNode; i -- ) {\n\n\t\t\tconst o = i * FLOATS_PER_NODE;\n\t\t\tconst b = ( i - startNode ) * 6; // bounds indexed relative to BLAS start\n\n\t\t\tif ( bvhData[ o + 3 ] === LEAF_MARKER ) {\n\n\t\t\t\tconst triOffset = bvhData[ o ];\n\t\t\t\tconst triCount = bvhData[ o + 1 ];\n\n\t\t\t\tlet minX = Infinity, minY = Infinity, minZ = Infinity;\n\t\t\t\tlet maxX = - Infinity, maxY = - Infinity, maxZ = - Infinity;\n\n\t\t\t\tfor ( let t = 0; t < triCount; t ++ ) {\n\n\t\t\t\t\tconst tOff = ( triOffset + t ) * FPT;\n\t\t\t\t\tconst ax = triangleData[ tOff + TRIANGLE_DATA_LAYOUT.POSITION_A_OFFSET ];\n\t\t\t\t\tconst ay = triangleData[ tOff + TRIANGLE_DATA_LAYOUT.POSITION_A_OFFSET + 1 ];\n\t\t\t\t\tconst az = triangleData[ tOff + TRIANGLE_DATA_LAYOUT.POSITION_A_OFFSET + 2 ];\n\t\t\t\t\tconst bx = triangleData[ tOff + TRIANGLE_DATA_LAYOUT.POSITION_B_OFFSET ];\n\t\t\t\t\tconst by = triangleData[ tOff + TRIANGLE_DATA_LAYOUT.POSITION_B_OFFSET + 1 ];\n\t\t\t\t\tconst bz = triangleData[ tOff + TRIANGLE_DATA_LAYOUT.POSITION_B_OFFSET + 2 ];\n\t\t\t\t\tconst cx = triangleData[ tOff + TRIANGLE_DATA_LAYOUT.POSITION_C_OFFSET ];\n\t\t\t\t\tconst cy = triangleData[ tOff + TRIANGLE_DATA_LAYOUT.POSITION_C_OFFSET + 1 ];\n\t\t\t\t\tconst cz = triangleData[ tOff + TRIANGLE_DATA_LAYOUT.POSITION_C_OFFSET + 2 ];\n\n\t\t\t\t\tminX = Math.min( minX, ax, bx, cx );\n\t\t\t\t\tminY = Math.min( minY, ay, by, cy );\n\t\t\t\t\tminZ = Math.min( minZ, az, bz, cz );\n\t\t\t\t\tmaxX = Math.max( maxX, ax, bx, cx );\n\t\t\t\t\tmaxY = Math.max( maxY, ay, by, cy );\n\t\t\t\t\tmaxZ = Math.max( maxZ, az, bz, cz );\n\n\t\t\t\t}\n\n\t\t\t\tbounds[ b ] = minX;\n\t\t\t\tbounds[ b + 1 ] = minY;\n\t\t\t\tbounds[ b + 2 ] = minZ;\n\t\t\t\tbounds[ b + 3 ] = maxX;\n\t\t\t\tbounds[ b + 4 ] = maxY;\n\t\t\t\tbounds[ b + 5 ] = maxZ;\n\n\t\t\t} else {\n\n\t\t\t\t// Inner node — child indices are absolute, but bounds index relative to startNode\n\t\t\t\tconst leftIdx = bvhData[ o + 3 ];\n\t\t\t\tconst rightIdx = bvhData[ o + 7 ];\n\t\t\t\tconst lb = ( leftIdx - startNode ) * 6;\n\t\t\t\tconst rb = ( rightIdx - startNode ) * 6;\n\n\t\t\t\tconst lMinX = bounds[ lb ];\n\t\t\t\tconst lMinY = bounds[ lb + 1 ];\n\t\t\t\tconst lMinZ = bounds[ lb + 2 ];\n\t\t\t\tconst lMaxX = bounds[ lb + 3 ];\n\t\t\t\tconst lMaxY = bounds[ lb + 4 ];\n\t\t\t\tconst lMaxZ = bounds[ lb + 5 ];\n\n\t\t\t\tconst rMinX = bounds[ rb ];\n\t\t\t\tconst rMinY = bounds[ rb + 1 ];\n\t\t\t\tconst rMinZ = bounds[ rb + 2 ];\n\t\t\t\tconst rMaxX = bounds[ rb + 3 ];\n\t\t\t\tconst rMaxY = bounds[ rb + 4 ];\n\t\t\t\tconst rMaxZ = bounds[ rb + 5 ];\n\n\t\t\t\tbvhData[ o ] = lMinX;\n\t\t\t\tbvhData[ o + 1 ] = lMinY;\n\t\t\t\tbvhData[ o + 2 ] = lMinZ;\n\t\t\t\tbvhData[ o + 4 ] = lMaxX;\n\t\t\t\tbvhData[ o + 5 ] = lMaxY;\n\t\t\t\tbvhData[ o + 6 ] = lMaxZ;\n\n\t\t\t\tbvhData[ o + 8 ] = rMinX;\n\t\t\t\tbvhData[ o + 9 ] = rMinY;\n\t\t\t\tbvhData[ o + 10 ] = rMinZ;\n\t\t\t\tbvhData[ o + 12 ] = rMaxX;\n\t\t\t\tbvhData[ o + 13 ] = rMaxY;\n\t\t\t\tbvhData[ o + 14 ] = rMaxZ;\n\n\t\t\t\tbounds[ b ] = Math.min( lMinX, rMinX );\n\t\t\t\tbounds[ b + 1 ] = Math.min( lMinY, rMinY );\n\t\t\t\tbounds[ b + 2 ] = Math.min( lMinZ, rMinZ );\n\t\t\t\tbounds[ b + 3 ] = Math.max( lMaxX, rMaxX );\n\t\t\t\tbounds[ b + 4 ] = Math.max( lMaxY, rMaxY );\n\t\t\t\tbounds[ b + 5 ] = Math.max( lMaxZ, rMaxZ );\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t/**\n\t * Bottom-up refit of all BVH node AABBs.\n\t * Reverse pre-order iteration gives valid bottom-up order (children have higher\n\t * indices than parents in pre-order, so reversing processes children first).\n\t *\n\t * @param {Float32Array} bvhData - Flat BVH array (mutated in place)\n\t * @param {Float32Array} triangleData - Updated triangle data\n\t * @param {number} nodeCount - Total number of BVH nodes\n\t */\n\trefit( bvhData, triangleData, nodeCount ) {\n\n\t\t// Reuse bounds buffer across frames (reallocate only on scene change)\n\t\tif ( nodeCount !== this._boundsNodeCount ) {\n\n\t\t\tthis._bounds = new Float32Array( nodeCount * 6 );\n\t\t\tthis._boundsNodeCount = nodeCount;\n\n\t\t}\n\n\t\tconst bounds = this._bounds;\n\n\t\t// Reverse iteration: bottom-up in pre-order layout\n\t\tfor ( let i = nodeCount - 1; i >= 0; i -- ) {\n\n\t\t\tconst o = i * FLOATS_PER_NODE;\n\t\t\tconst b = i * 6;\n\n\t\t\tconst marker = bvhData[ o + 3 ];\n\n\t\t\tif ( marker === LEAF_MARKER ) {\n\n\t\t\t\t// Triangle leaf: compute AABB from triangles\n\t\t\t\tconst triOffset = bvhData[ o ];\n\t\t\t\tconst triCount = bvhData[ o + 1 ];\n\n\t\t\t\tlet minX = Infinity, minY = Infinity, minZ = Infinity;\n\t\t\t\tlet maxX = - Infinity, maxY = - Infinity, maxZ = - Infinity;\n\n\t\t\t\tfor ( let t = 0; t < triCount; t ++ ) {\n\n\t\t\t\t\tconst tOff = ( triOffset + t ) * FPT;\n\n\t\t\t\t\t// Position A\n\t\t\t\t\tconst ax = triangleData[ tOff + TRIANGLE_DATA_LAYOUT.POSITION_A_OFFSET ];\n\t\t\t\t\tconst ay = triangleData[ tOff + TRIANGLE_DATA_LAYOUT.POSITION_A_OFFSET + 1 ];\n\t\t\t\t\tconst az = triangleData[ tOff + TRIANGLE_DATA_LAYOUT.POSITION_A_OFFSET + 2 ];\n\t\t\t\t\t// Position B\n\t\t\t\t\tconst bx = triangleData[ tOff + TRIANGLE_DATA_LAYOUT.POSITION_B_OFFSET ];\n\t\t\t\t\tconst by = triangleData[ tOff + TRIANGLE_DATA_LAYOUT.POSITION_B_OFFSET + 1 ];\n\t\t\t\t\tconst bz = triangleData[ tOff + TRIANGLE_DATA_LAYOUT.POSITION_B_OFFSET + 2 ];\n\t\t\t\t\t// Position C\n\t\t\t\t\tconst cx = triangleData[ tOff + TRIANGLE_DATA_LAYOUT.POSITION_C_OFFSET ];\n\t\t\t\t\tconst cy = triangleData[ tOff + TRIANGLE_DATA_LAYOUT.POSITION_C_OFFSET + 1 ];\n\t\t\t\t\tconst cz = triangleData[ tOff + TRIANGLE_DATA_LAYOUT.POSITION_C_OFFSET + 2 ];\n\n\t\t\t\t\tminX = Math.min( minX, ax, bx, cx );\n\t\t\t\t\tminY = Math.min( minY, ay, by, cy );\n\t\t\t\t\tminZ = Math.min( minZ, az, bz, cz );\n\t\t\t\t\tmaxX = Math.max( maxX, ax, bx, cx );\n\t\t\t\t\tmaxY = Math.max( maxY, ay, by, cy );\n\t\t\t\t\tmaxZ = Math.max( maxZ, az, bz, cz );\n\n\t\t\t\t}\n\n\t\t\t\tbounds[ b ] = minX;\n\t\t\t\tbounds[ b + 1 ] = minY;\n\t\t\t\tbounds[ b + 2 ] = minZ;\n\t\t\t\tbounds[ b + 3 ] = maxX;\n\t\t\t\tbounds[ b + 4 ] = maxY;\n\t\t\t\tbounds[ b + 5 ] = maxZ;\n\n\t\t\t} else if ( marker === BLAS_POINTER_MARKER ) {\n\n\t\t\t\t// BLAS-pointer leaf (TLAS): read BLAS root node's bounds (already computed)\n\t\t\t\tconst blasRoot = bvhData[ o ];\n\t\t\t\tconst br = blasRoot * 6;\n\t\t\t\tbounds[ b ] = bounds[ br ];\n\t\t\t\tbounds[ b + 1 ] = bounds[ br + 1 ];\n\t\t\t\tbounds[ b + 2 ] = bounds[ br + 2 ];\n\t\t\t\tbounds[ b + 3 ] = bounds[ br + 3 ];\n\t\t\t\tbounds[ b + 4 ] = bounds[ br + 4 ];\n\t\t\t\tbounds[ b + 5 ] = bounds[ br + 5 ];\n\n\t\t\t} else {\n\n\t\t\t\t// Inner node: union children bounds (already computed since we iterate in reverse)\n\t\t\t\tconst leftIdx = bvhData[ o + 3 ];\n\t\t\t\tconst rightIdx = bvhData[ o + 7 ];\n\t\t\t\tconst lb = leftIdx * 6;\n\t\t\t\tconst rb = rightIdx * 6;\n\n\t\t\t\tconst lMinX = bounds[ lb ];\n\t\t\t\tconst lMinY = bounds[ lb + 1 ];\n\t\t\t\tconst lMinZ = bounds[ lb + 2 ];\n\t\t\t\tconst lMaxX = bounds[ lb + 3 ];\n\t\t\t\tconst lMaxY = bounds[ lb + 4 ];\n\t\t\t\tconst lMaxZ = bounds[ lb + 5 ];\n\n\t\t\t\tconst rMinX = bounds[ rb ];\n\t\t\t\tconst rMinY = bounds[ rb + 1 ];\n\t\t\t\tconst rMinZ = bounds[ rb + 2 ];\n\t\t\t\tconst rMaxX = bounds[ rb + 3 ];\n\t\t\t\tconst rMaxY = bounds[ rb + 4 ];\n\t\t\t\tconst rMaxZ = bounds[ rb + 5 ];\n\n\t\t\t\t// Write left child AABB into bvhData\n\t\t\t\tbvhData[ o ] = lMinX;\n\t\t\t\tbvhData[ o + 1 ] = lMinY;\n\t\t\t\tbvhData[ o + 2 ] = lMinZ;\n\t\t\t\t// o+3 = leftChildIdx (preserved)\n\t\t\t\tbvhData[ o + 4 ] = lMaxX;\n\t\t\t\tbvhData[ o + 5 ] = lMaxY;\n\t\t\t\tbvhData[ o + 6 ] = lMaxZ;\n\t\t\t\t// o+7 = rightChildIdx (preserved)\n\n\t\t\t\t// Write right child AABB into bvhData\n\t\t\t\tbvhData[ o + 8 ] = rMinX;\n\t\t\t\tbvhData[ o + 9 ] = rMinY;\n\t\t\t\tbvhData[ o + 10 ] = rMinZ;\n\t\t\t\t// o+11 = 0 padding\n\t\t\t\tbvhData[ o + 12 ] = rMaxX;\n\t\t\t\tbvhData[ o + 13 ] = rMaxY;\n\t\t\t\tbvhData[ o + 14 ] = rMaxZ;\n\t\t\t\t// o+15 = 0 padding\n\n\t\t\t\t// Store this node's bounds as union of children\n\t\t\t\tbounds[ b ] = Math.min( lMinX, rMinX );\n\t\t\t\tbounds[ b + 1 ] = Math.min( lMinY, rMinY );\n\t\t\t\tbounds[ b + 2 ] = Math.min( lMinZ, rMinZ );\n\t\t\t\tbounds[ b + 3 ] = Math.max( lMaxX, rMaxX );\n\t\t\t\tbounds[ b + 4 ] = Math.max( lMaxY, rMaxY );\n\t\t\t\tbounds[ b + 5 ] = Math.max( lMaxZ, rMaxZ );\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n}\n","/**\n * BVHRefitWorker — Off-main-thread BVH refit using SharedArrayBuffer.\n *\n * Protocol:\n * 'init' → receives SharedArrayBuffers + index map (once per scene)\n * 'refit' → reads shared positions, writes shared bvh/tri data (per frame)\n */\n\nimport { BVHRefitter } from '../BVHRefitter.js';\n\nconst FLOATS_PER_NODE = 16;\nconst refitter = new BVHRefitter();\n\n// Cached shared memory views (set once on 'init', reused every frame)\nlet bvhData = null;\nlet triData = null;\nlet posData = null;\nlet bvhToOriginal = null;\nlet nodeCount = 0;\n\nself.onmessage = function ( e ) {\n\n\tconst { type } = e.data;\n\n\tif ( type === 'init' ) {\n\n\t\tbvhData = new Float32Array( e.data.sharedBvhBuf );\n\t\ttriData = new Float32Array( e.data.sharedTriBuf );\n\t\tposData = new Float32Array( e.data.sharedPosBuf );\n\t\tbvhToOriginal = e.data.bvhToOriginal; // transferred Uint32Array\n\t\tnodeCount = bvhData.length / FLOATS_PER_NODE;\n\t\treturn;\n\n\t}\n\n\tif ( type === 'refit' ) {\n\n\t\ttry {\n\n\t\t\tconst startTime = performance.now();\n\n\t\t\trefitter.updateTrianglePositions( triData, posData, bvhToOriginal );\n\t\t\trefitter.refit( bvhData, triData, nodeCount );\n\n\t\t\tself.postMessage( {\n\t\t\t\ttype: 'refitComplete',\n\t\t\t\trefitTimeMs: performance.now() - startTime\n\t\t\t} );\n\n\t\t} catch ( error ) {\n\n\t\t\tconsole.error( '[BVHRefitWorker] Refit error:', error );\n\t\t\tself.postMessage( { type: 'error', error: error.message } );\n\n\t\t}\n\n\t}\n\n};\n"],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- (function(){var e=class{constructor(e,t){this.traversalCost=e,this.intersectionCost=t,this.maxTreeletLeaves=7,this.minImprovement=.02,this.topologyCache=new Map;for(let e=3;e<=this.maxTreeletLeaves;e++)this.topologyCache.set(e,this.generateTopologies(e));this.stats={treeletsProcessed:0,treeletsImproved:0,totalSAHImprovement:0,averageSAHImprovement:0,optimizationTime:0}}generateTopologies(e){if(e===1)return[0];if(e===2)return[[0,1]];let t=[];for(let n=1;n<e;n++){let r=this.generateTopologies(n),i=this.generateTopologies(e-n);for(let e of r)for(let r of i)t.push([e,this.offsetTopology(r,n)])}return t}offsetTopology(e,t){return typeof e==`number`?e+t:[this.offsetTopology(e[0],t),this.offsetTopology(e[1],t)]}optimizeBVH(e){let t=performance.now();this.stats={treeletsProcessed:0,treeletsImproved:0,totalSAHImprovement:0,averageSAHImprovement:0,optimizationTime:0};let n=this.identifyTreeletRoots(e);for(let e=0;e<n.length;e++){if(performance.now()-t>3e4){console.warn(`TreeletOptimizer: timeout after ${e}/${n.length} treelets`);break}this.optimizeTreelet(n[e])}return this.stats.optimizationTime=performance.now()-t,this.stats.averageSAHImprovement=this.stats.treeletsProcessed>0?this.stats.totalSAHImprovement/this.stats.treeletsProcessed:0,e}identifyTreeletRoots(e){let t=[],n=new Set,r=[{node:e,visited:!1}];for(;r.length>0;){let e=r[r.length-1];if(e.visited){r.pop();let i=e.node;if(i.triangleCount>0||n.has(i))continue;let a=this.countLeaves(i);a>=3&&a<=this.maxTreeletLeaves&&(t.push(i),this.markSubtree(i,n))}else{e.visited=!0;let t=e.node;if(t.triangleCount>0)continue;t.rightChild&&r.push({node:t.rightChild,visited:!1}),t.leftChild&&r.push({node:t.leftChild,visited:!1})}}return t}countLeaves(e){return e?e.triangleCount>0?1:this.countLeaves(e.leftChild)+this.countLeaves(e.rightChild):0}markSubtree(e,t){e&&(t.add(e),!(e.triangleCount>0)&&(this.markSubtree(e.leftChild,t),this.markSubtree(e.rightChild,t)))}optimizeTreelet(e){let t=[];this.extractLeaves(e,t);let n=t.length;if(n<3||n>this.maxTreeletLeaves)return;this.stats.treeletsProcessed++;let r=this.evaluateSubtreeSAH(e),i=this.topologyCache.get(n);if(!i||i.length===0)return;let a=r,o=null,s=null;if(n<=5){let e=this.generatePermutations(n);for(let n of i)for(let r of e){let e=this.evaluateTopology(n,t,r);e<a&&(a=e,o=n,s=r)}}else{let e=Array.from({length:n},(e,t)=>t);for(let n of i){let r=this.evaluateTopology(n,t,e);r<a&&(a=r,o=n,s=e);let i=this.greedySwapOptimize(n,t,e,r);i.cost<a&&(a=i.cost,o=n,s=i.perm)}}let c=(r-a)/r;o&&c>this.minImprovement&&(this.reconstructTreelet(e,o,t,s),this.stats.treeletsImproved++,this.stats.totalSAHImprovement+=c)}extractLeaves(e,t){if(e){if(e.triangleCount>0){t.push({minX:e.minX,minY:e.minY,minZ:e.minZ,maxX:e.maxX,maxY:e.maxY,maxZ:e.maxZ,triangleOffset:e.triangleOffset,triangleCount:e.triangleCount});return}this.extractLeaves(e.leftChild,t),this.extractLeaves(e.rightChild,t)}}evaluateSubtreeSAH(e){if(!e)return 0;if(e.triangleCount>0)return this.surfaceAreaFlat(e.minX,e.minY,e.minZ,e.maxX,e.maxY,e.maxZ)*e.triangleCount*this.intersectionCost;let t=this.evaluateSubtreeSAH(e.leftChild),n=this.evaluateSubtreeSAH(e.rightChild);return this.surfaceAreaFlat(e.minX,e.minY,e.minZ,e.maxX,e.maxY,e.maxZ)*this.traversalCost+t+n}evaluateTopology(e,t,n){return this.evalTopoRecursive(e,t,n).cost}evalTopoRecursive(e,t,n){if(typeof e==`number`){let r=t[n[e]];return{cost:this.surfaceAreaFlat(r.minX,r.minY,r.minZ,r.maxX,r.maxY,r.maxZ)*r.triangleCount*this.intersectionCost,minX:r.minX,minY:r.minY,minZ:r.minZ,maxX:r.maxX,maxY:r.maxY,maxZ:r.maxZ}}let r=this.evalTopoRecursive(e[0],t,n),i=this.evalTopoRecursive(e[1],t,n),a=Math.min(r.minX,i.minX),o=Math.min(r.minY,i.minY),s=Math.min(r.minZ,i.minZ),c=Math.max(r.maxX,i.maxX),l=Math.max(r.maxY,i.maxY),u=Math.max(r.maxZ,i.maxZ);return{cost:this.surfaceAreaFlat(a,o,s,c,l,u)*this.traversalCost+r.cost+i.cost,minX:a,minY:o,minZ:s,maxX:c,maxY:l,maxZ:u}}surfaceAreaFlat(e,t,n,r,i,a){let o=r-e,s=i-t,c=a-n;return 2*(o*s+s*c+c*o)}generatePermutations(e){let t=[],n=Array.from({length:e},(e,t)=>t),r=i=>{if(i===e){t.push([...n]);return}for(let t=i;t<e;t++)[n[i],n[t]]=[n[t],n[i]],r(i+1),[n[i],n[t]]=[n[t],n[i]]};return r(0),t}greedySwapOptimize(e,t,n,r){let i=[...n],a=r,o=!0;for(;o;){o=!1;for(let n=0;n<i.length-1;n++)for(let r=n+1;r<i.length;r++){[i[n],i[r]]=[i[r],i[n]];let s=this.evaluateTopology(e,t,i);s<a?(a=s,o=!0):[i[n],i[r]]=[i[r],i[n]]}}return{perm:i,cost:a}}reconstructTreelet(e,t,n,r){let i=this.buildSubtree(t,n,r);e.minX=i.minX,e.minY=i.minY,e.minZ=i.minZ,e.maxX=i.maxX,e.maxY=i.maxY,e.maxZ=i.maxZ,e.leftChild=i.leftChild,e.rightChild=i.rightChild,e.triangleOffset=i.triangleOffset,e.triangleCount=i.triangleCount}buildSubtree(e,n,r){if(typeof e==`number`){let i=n[r[e]],a=new t;return a.minX=i.minX,a.minY=i.minY,a.minZ=i.minZ,a.maxX=i.maxX,a.maxY=i.maxY,a.maxZ=i.maxZ,a.triangleOffset=i.triangleOffset,a.triangleCount=i.triangleCount,a}let i=this.buildSubtree(e[0],n,r),a=this.buildSubtree(e[1],n,r),o=new t;return o.leftChild=i,o.rightChild=a,o.minX=Math.min(i.minX,a.minX),o.minY=Math.min(i.minY,a.minY),o.minZ=Math.min(i.minZ,a.minZ),o.maxX=Math.max(i.maxX,a.maxX),o.maxY=Math.max(i.maxY,a.maxY),o.maxZ=Math.max(i.maxZ,a.maxZ),o}setTreeletSize(e){this.maxTreeletLeaves=Math.max(3,Math.min(7,e));for(let e=3;e<=this.maxTreeletLeaves;e++)this.topologyCache.has(e)||this.topologyCache.set(e,this.generateTopologies(e))}setMinImprovement(e){this.minImprovement=Math.max(.001,e)}setMaxTreelets(){}getStatistics(){return{...this.stats}}},t=class{constructor(){this.minX=0,this.minY=0,this.minZ=0,this.maxX=0,this.maxY=0,this.maxZ=0,this.leftChild=null,this.rightChild=null,this.triangleOffset=0,this.triangleCount=0}},n=class{constructor(e,t){this.traversalCost=e,this.intersectionCost=t,this.batchSizeRatio=.02,this.maxIterations=2,this.timeBudgetMs=15e3,this.stats={reinsertionsApplied:0,iterations:0,timeMs:0}}setBatchSizeRatio(e){this.batchSizeRatio=Math.max(.005,Math.min(.1,e))}setMaxIterations(e){this.maxIterations=Math.max(1,Math.min(5,e))}getStatistics(){return{...this.stats}}surfaceArea(e){let t=e.maxX-e.minX,n=e.maxY-e.minY,r=e.maxZ-e.minZ;return t*n+n*r+r*t}buildParentMap(e){let t=new Map;t.set(e,{parent:null,isLeft:!1});let n=[e];for(;n.length>0;){let e=n.pop();e.triangleCount>0||(e.leftChild&&(t.set(e.leftChild,{parent:e,isLeft:!0}),n.push(e.leftChild)),e.rightChild&&(t.set(e.rightChild,{parent:e,isLeft:!1}),n.push(e.rightChild)))}return t}findCandidates(e,t,n){let r=[],i=[e];for(;i.length>0;){let a=i.pop();if(a!==e&&n.get(a).parent!==e){let e=this.surfaceArea(a);r.length<t?(r.push({node:a,cost:e}),r.length===t&&this._heapify(r)):e>r[0].cost&&(r[0]={node:a,cost:e},this._siftDown(r,0))}a.triangleCount===0&&(a.leftChild&&i.push(a.leftChild),a.rightChild&&i.push(a.rightChild))}return r}_heapify(e){for(let t=(e.length>>1)-1;t>=0;t--)this._siftDown(e,t)}_siftDown(e,t){let n=e.length;for(;;){let r=t,i=2*t+1,a=2*t+2;if(i<n&&e[i].cost<e[r].cost&&(r=i),a<n&&e[a].cost<e[r].cost&&(r=a),r===t)break;let o=e[t];e[t]=e[r],e[r]=o,t=r}}findReinsertion(e,t,n){let r=n.get(e),i=r.parent;if(!i)return null;let a=r.isLeft?i.rightChild:i.leftChild,o=this.surfaceArea(e),s=this.surfaceArea(i),c=null,l=0,u=s,d=a.minX,f=a.minY,p=a.minZ,m=a.maxX,h=a.maxY,g=a.maxZ,_=a,v=i,y=[];do{for(y.length=0,y.push(u,_);y.length>0;){let t=y.pop(),n=y.pop();if(n-o<=l)continue;let r=Math.min(t.minX,e.minX),i=Math.min(t.minY,e.minY),a=Math.min(t.minZ,e.minZ),s=Math.max(t.maxX,e.maxX),u=Math.max(t.maxY,e.maxY),d=Math.max(t.maxZ,e.maxZ),f=s-r,p=u-i,m=d-a,h=n-(f*p+p*m+m*f);if(h>l&&(c=t,l=h),t.triangleCount===0&&t.leftChild&&t.rightChild){let e=h+this.surfaceArea(t);y.push(e,t.leftChild),y.push(e,t.rightChild)}}let t=n.get(v);if(!t||t.parent===null)break;if(v!==i){d=Math.min(d,_.minX),f=Math.min(f,_.minY),p=Math.min(p,_.minZ),m=Math.max(m,_.maxX),h=Math.max(h,_.maxY),g=Math.max(g,_.maxZ);let e=m-d,t=h-f,n=g-p,r=e*t+t*n+n*e;u+=this.surfaceArea(v)-r}let r=t.parent;_=t.isLeft?r.rightChild:r.leftChild,v=r}while(n.get(v).parent!==null);return c===a||c===i?null:c?{from:e,to:c,areaDiff:l}:null}getConflicts(e,t,n){let r=n.get(e);return[t,e,r.isLeft?r.parent.rightChild:r.parent.leftChild,n.get(t).parent,r.parent]}reinsertNode(e,t,n){let r=n.get(e),i=r.parent,a=r.isLeft?i.rightChild:i.leftChild,o=n.get(i),s=o.parent,c=n.get(t),l=c.parent;o.isLeft?s.leftChild=a:s.rightChild=a,i.leftChild=e,i.rightChild=t,i.triangleOffset=0,i.triangleCount=0,i.minX=Math.min(e.minX,t.minX),i.minY=Math.min(e.minY,t.minY),i.minZ=Math.min(e.minZ,t.minZ),i.maxX=Math.max(e.maxX,t.maxX),i.maxY=Math.max(e.maxY,t.maxY),i.maxZ=Math.max(e.maxZ,t.maxZ),c.isLeft?l.leftChild=i:l.rightChild=i,n.set(a,{parent:s,isLeft:o.isLeft}),n.set(i,{parent:l,isLeft:c.isLeft}),n.set(e,{parent:i,isLeft:!0}),n.set(t,{parent:i,isLeft:!1}),this.refitFrom(s,n),this.refitFrom(l,n)}refitFrom(e,t){let n=e;for(;n;){if(n.triangleCount===0&&n.leftChild&&n.rightChild){let e=n.leftChild,t=n.rightChild;n.minX=Math.min(e.minX,t.minX),n.minY=Math.min(e.minY,t.minY),n.minZ=Math.min(e.minZ,t.minZ),n.maxX=Math.max(e.maxX,t.maxX),n.maxY=Math.max(e.maxY,t.maxY),n.maxZ=Math.max(e.maxZ,t.maxZ)}let e=t.get(n);n=e?e.parent:null}}optimizeBVH(e,t){let n=performance.now();this.stats={reinsertionsApplied:0,iterations:0,timeMs:0};for(let r=0;r<this.maxIterations&&!(performance.now()-n>this.timeBudgetMs);r++){let i=this.buildParentMap(e),a=i.size,o=Math.max(1,Math.floor(a*this.batchSizeRatio));t&&t(`Reinsertion iter ${r+1}/${this.maxIterations}: selecting ${o} candidates`);let s=this.findCandidates(e,o,i),c=[];for(let t=0;t<s.length&&!(performance.now()-n>this.timeBudgetMs);t++){let n=this.findReinsertion(s[t].node,e,i);n&&n.areaDiff>0&&c.push(n)}c.sort((e,t)=>t.areaDiff-e.areaDiff);let l=new Set,u=0;for(let e of c){let t=this.getConflicts(e.from,e.to,i);if(!t.some(e=>l.has(e))){for(let e of t)l.add(e);this.reinsertNode(e.from,e.to,i),u++}}if(this.stats.reinsertionsApplied+=u,this.stats.iterations=r+1,t&&t(`Reinsertion iter ${r+1}: applied ${u} reinsertions`),u===0)break}return this.stats.timeMs=performance.now()-n,this.stats}};async function r(e){let t=e instanceof URL?e.href:e,n=await fetch(t);if(!n.ok)throw Error(`Failed to fetch worker script: ${n.status}`);let r=new Blob([await n.text()],{type:`application/javascript`});return new Worker(URL.createObjectURL(r),{type:`module`})}var i=``+new URL(`BVHWorker-DOaGn6uN.js`,self.location.href).href;let a={silent:0,error:1,warn:2,info:3,debug:4},o=`info`,s=`rayzeeLogLevel`,c=`rayzeeLogNamespaces`,l=`color:#7c8494`,u=`color:inherit`,d=null,f=null;function p(e){try{return globalThis.localStorage?.getItem(e)??null}catch{return null}}function m(e,t){try{t===null?globalThis.localStorage?.removeItem(e):globalThis.localStorage?.setItem(e,t)}catch{}}function h(){if(d!==null)return d;let e=globalThis.__RAYZEE_LOG_LEVEL__;return e!==void 0&&e in a?(d=a[e],d):(d=a[p(s)]??a[o],d)}function g(){if(f!==null)return f;let e=globalThis.__RAYZEE_LOG_NS__??p(c);return f=e?String(e).split(`,`).map(e=>e.trim()).filter(Boolean):[],f}function _(e,t){if(t<a.debug)return!0;let n=g();return n.length===0||n.some(t=>e===t||e.startsWith(`${t}:`))}function v(e,t,n){if(e>h()||!_(t,e))return;let r=e===a.error?console.error:e===a.warn?console.warn:console.log,i=`[${t}]`;typeof n[0]==`string`?r(`%c${i}%c ${n[0]}`,l,u,...n.slice(1)):r(`%c${i}`,l,...n)}let y={n:e=>Number(e).toLocaleString(`en-US`),ms:e=>e<10?`${Number(e).toFixed(1)} ms`:`${Math.round(e)} ms`,mb:e=>{let t=e/(1024*1024);return t<10?`${t.toFixed(1)} MB`:`${Math.round(t)} MB`},px:(e,t)=>`${e}×${t}`,count:(e,t,n=`${t}s`)=>`${y.n(e)} ${Number(e)===1?t:n}`,list:e=>e.filter(Boolean).join(` · `)},b={levels:a,setLevel(e){return e in a?(globalThis.__RAYZEE_LOG_LEVEL__=e,d=a[e],m(s,e===o?null:e),e):(console.warn(`[log] unknown level '${e}' — expected one of ${Object.keys(a).join(`, `)}`),this.getLevel())},getLevel(){let e=h();return Object.keys(a).find(t=>a[t]===e)},isEnabled(e){return(a[e]??a.debug)<=h()},only(...e){let t=e.flat().filter(Boolean);return f=t,globalThis.__RAYZEE_LOG_NS__=t.join(`,`),m(c,t.length?t.join(`,`):null),t.length&&this.setLevel(`debug`),t},refresh(){return d=null,f=null,this.getLevel()}};function x(e){return{namespace:e,error:(...t)=>v(a.error,e,t),warn:(...t)=>v(a.warn,e,t),info:(...t)=>v(a.info,e,t),debug:(...t)=>v(a.debug,e,t),isEnabled:e=>b.isEnabled(e),summary(t,n=[]){let r=n.filter(Boolean);if(r.length===0||!console.groupCollapsed){v(a.info,e,[t]),r.forEach(t=>v(a.debug,e,[t]));return}a.info>h()||((h()>=a.debug&&console.group?console.group:console.groupCollapsed)(`%c[${e}]%c ${t}`,l,u),r.forEach(e=>console.log(e)),console.groupEnd())}}}let S=x(`bvh`),C={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},w=C.FLOATS_PER_TRIANGLE;var 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}},E=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=C.POSITION_A_OFFSET,r=C.POSITION_B_OFFSET,i=C.POSITION_C_OFFSET;for(let a=0;a<e;a++){let e=a*w,o=t[e+n],s=t[e+n+1],c=t[e+n+2],l=t[e+r],u=t[e+r+1],d=t[e+r+2],f=t[e+i],p=t[e+i+1],m=t[e+i+2],h=a*3;this.centroids[h]=(o+l+f)/3,this.centroids[h+1]=(s+u+p)/3,this.centroids[h+2]=(c+d+m)/3,this.bMin[h]=o<l?o<f?o:f:l<f?l:f,this.bMin[h+1]=s<u?s<p?s:p:u<p?u:p,this.bMin[h+2]=c<d?c<m?c:m:d<m?d:m,this.bMax[h]=o>l?o>f?o:f:l>f?l:f,this.bMax[h+1]=s>u?s>p?s:p:u>p?u:p,this.bMax[h+2]=c>d?c>m?c:m:d>m?d:m,this.indices[a]=a}}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/(w*4),this.processedTriangles=0,this.lastProgressUpdate=performance.now(),this.useWorker&&typeof Worker<`u`?new Promise((a,o)=>{let s=r=>{let i=this.totalTriangles,s=typeof SharedArrayBuffer<`u`;S.debug(`SharedArrayBuffer ${s?`enabled`:`unavailable (using transfer fallback)`}`);let c=s?new SharedArrayBuffer(i*w*4):null;r.onmessage=e=>{let{bvhData:t,triangles:i,originalToBvh:s,error:l,progress:u,treeletStats:d}=e.data;if(l){r.terminate(),o(Error(l));return}if(u!==void 0&&n){n(u);return}d&&(this.splitStats=d),r.terminate(),a({bvhData:t,bvhRoot:!0,reorderedTriangles:c?new Float32Array(c):i,originalToBvh:s||null})},r.onerror=e=>{r.terminate(),o(e)};let l=e.buffer,u={triangleData:l,triangleByteOffset:e.byteOffset,triangleByteLength:e.byteLength,triangleCount:i,depth:t,reportProgress:!!n,sharedReorderBuffer:c,treeletOptimization:{enabled:this.enableTreeletOptimization,size:this.treeletSize,passes:this.treeletOptimizationPasses,minImprovement:this.treeletMinImprovement},reinsertionOptimization:{enabled:this.enableReinsertionOptimization,batchSizeRatio:this.reinsertionBatchSizeRatio,maxIterations:this.reinsertionMaxIterations}};r.postMessage(u,[l])};try{s(new Worker(i,{type:`module`}))}catch(o){o.name===`SecurityError`?r(i).then(s).catch(()=>{S.warn(`worker fetch fallback failed, using synchronous build`),a(this._buildSyncAndFlatten(e,t,n))}):(S.warn(`worker creation failed, falling back to synchronous build:`,o),a(this._buildSyncAndFlatten(e,t,n)))}}):new Promise(r=>{r(this._buildSyncAndFlatten(e,t,n))})}_buildSyncAndFlatten(e,t,n){let r=this.buildSync(e,t,n);return{bvhData:this.flattenBVH(r),bvhRoot:!0,reorderedTriangles:this.reorderedTriangleData||null,originalToBvh:this.originalToBvhMap||null}}buildSync(t,r=30,i=null,a=null){let o=performance.now();this.totalNodes=0,this.processedTriangles=0,this.triangles=t,this.totalTriangles=t.byteLength/(w*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 s=this.totalTriangles,c=performance.now();this.centroids=new Float32Array(s*3),this.bMin=new Float32Array(s*3),this.bMax=new Float32Array(s*3),this.indices=new Uint32Array(s),this.mortonCodes=new Uint32Array(s),this.initializeTriangleArrays(),this.splitStats.initTime=performance.now()-c,this.sortTrianglesByMortonCode();let l=performance.now(),u=this.buildNodeRecursive(0,s,r,i);if(this.splitStats.sahBuildTime=performance.now()-l,this.enableTreeletOptimization&&this.totalTriangles>1e3){let t=this.totalTriangles>this.treeletComplexityThreshold,n=t?3:this.treeletSize,r=t?10:this.maxTreeletsPerScene,a=new e(this.traversalCost,this.intersectionCost);a.setTreeletSize(n),a.setMinImprovement(this.treeletMinImprovement),a.setMaxTreelets(r);let o=performance.now();for(let e=0;e<this.treeletOptimizationPasses;e++){let t=i?t=>{i(`Treelet optimization pass ${e+1}/${this.treeletOptimizationPasses}: ${t}`)}:null;try{a.optimizeBVH(u,t)}catch(t){S.error(`treelet optimizer failed in pass ${e+1}:`,t);break}let n=a.getStatistics(),r=performance.now()-o;if(n.treeletsImproved===0&&e>0||r>15e3)break}let s=performance.now()-o;this.splitStats.treeletOptimizationTime=s;let c=a.getStatistics();this.splitStats.treeletsProcessed=c.treeletsProcessed,this.splitStats.treeletsImproved=c.treeletsImproved,this.splitStats.averageSAHImprovement=c.averageSAHImprovement}if(this.enableReinsertionOptimization&&this.totalTriangles>1e3){let e=new n(this.traversalCost,this.intersectionCost);e.setBatchSizeRatio(this.reinsertionBatchSizeRatio),e.setMaxIterations(this.reinsertionMaxIterations);let t=i?e=>{i(e)}:null;try{e.optimizeBVH(u,t)}catch(e){S.error(`reinsertion optimizer failed:`,e)}let r=e.getStatistics();this.splitStats.reinsertionOptimizationTime=r.timeMs,this.splitStats.reinsertionsApplied=r.reinsertionsApplied,this.splitStats.reinsertionIterations=r.iterations}let d=performance.now();this.applySAOrdering(u),this.splitStats.saOrderTime=performance.now()-d;let f=performance.now(),p=this.triangles,m=a||new Float32Array(s*w);for(let e=0;e<s;e++){let t=this.indices[e]*w,n=e*w;m.set(p.subarray(t,t+w),n)}this.reorderedTriangleData=m;let h=new Uint32Array(s);for(let e=0;e<s;e++)h[this.indices[e]]=e;this.originalToBvhMap=h,this.splitStats.reorderTime=performance.now()-f,this.splitStats.totalBuildTime=performance.now()-o;let g=this.splitStats.totalBuildTime,_=this.splitStats;return S.debug(`${y.n(s)} tris → ${y.n(this.totalNodes)} nodes in ${y.ms(g)} | SAH ${_.sahSplits} objMed ${_.objectMedianSplits} spatMed ${_.spatialMedianSplits} failed ${_.failedSplits}`+(_.treeletsProcessed?` | treelets ${_.treeletsImproved}/${_.treeletsProcessed} improved`:``)+(_.reinsertionsApplied?` | reinsertions ${_.reinsertionsApplied}`:``)),i&&i(100),this.centroids=null,this.bMin=null,this.bMax=null,this.mortonCodes=null,u}updateProgress(e,t){if(!t)return;this.processedTriangles+=e;let n=performance.now();n-this.lastProgressUpdate<this.progressUpdateInterval||(this.lastProgressUpdate=n,t(Math.min(Math.floor(this.processedTriangles/this.totalTriangles*100),99)))}buildNodeRecursiveToDepth(e,t,n,r,i,a,o,s,c,l,u){let d=new T;this.totalNodes++;let f=t-e;if(a===void 0?this.updateNodeBounds(d,e,t):(d.minX=a,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,i),d;if(r<=0&&f>this.maxLeafSize*16){let r=this.frontierTasks.length;return d.triangleOffset=e,d.triangleCount=f,d.isFrontier=!0,d.frontierTaskId=r,this.frontierTasks.push({taskId:r,start:e,end:t,depth:n,preMinX:d.minX,preMinY:d.minY,preMinZ:d.minZ,preMaxX:d.maxX,preMaxY:d.maxY,preMaxZ:d.maxZ}),d}let p=this.findBestSplitPositionSAH(e,t,d);if(!p.success){if(this.splitStats.failedSplits++,r>0||f<=this.maxLeafSize*16)return d.triangleOffset=e,d.triangleCount=f,this.updateProgress(f,i),d;let a=this.frontierTasks.length;return d.triangleOffset=e,d.triangleCount=f,d.isFrontier=!0,d.frontierTaskId=a,this.frontierTasks.push({taskId:a,start:e,end:t,depth:n,preMinX:d.minX,preMinY:d.minY,preMinZ:d.minZ,preMaxX:d.maxX,preMaxY:d.maxY,preMaxZ:d.maxZ}),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,E=m.rMaxX,D=m.rMaxY,O=m.rMaxZ;return h===e||h===t?(d.triangleOffset=e,d.triangleCount=f,this.updateProgress(f,i),d):(d.leftChild=this.buildNodeRecursiveToDepth(e,h,n-1,r-1,i,g,_,v,y,b,x),d.rightChild=this.buildNodeRecursiveToDepth(h,t,n-1,r-1,i,S,C,w,E,D,O),d)}buildNodeRecursive(e,t,n,r,i,a,o,s,c,l){let u=new T;this.totalNodes++;let d=t-e;if(i===void 0?this.updateNodeBounds(u,e,t):(u.minX=i,u.minY=a,u.minZ=o,u.maxX=s,u.maxY=c,u.maxZ=l),d<=this.maxLeafSize||n<=0)return u.triangleOffset=e,u.triangleCount=d,this.updateProgress(d,r),u;let f=this.findBestSplitPositionSAH(e,t,u);if(!f.success)return this.splitStats.failedSplits++,u.triangleOffset=e,u.triangleCount=d,this.updateProgress(d,r),u;f.method===`SAH`?this.splitStats.sahSplits++:f.method===`object_median`?this.splitStats.objectMedianSplits++:f.method===`spatial_median`&&this.splitStats.spatialMedianSplits++,this.partitionWithBounds(e,t,f.axis,f.pos);let p=this._partResult,m=p.mid,h=p.lMinX,g=p.lMinY,_=p.lMinZ,v=p.lMaxX,y=p.lMaxY,b=p.lMaxZ,x=p.rMinX,S=p.rMinY,C=p.rMinZ,w=p.rMaxX,E=p.rMaxY,D=p.rMaxZ;return m===e||m===t?(u.triangleOffset=e,u.triangleCount=d,this.updateProgress(d,r),u):(u.leftChild=this.buildNodeRecursive(e,m,n-1,r,h,g,_,v,y,b),u.rightChild=this.buildNodeRecursive(m,t,n-1,r,x,S,C,w,E,D),u)}partitionWithBounds(e,t,n,r){let i=this.indices,a=this.centroids,o=this.bMin,s=this.bMax,c=e,l=t-1,u=1/0,d=1/0,f=1/0,p=-1/0,m=-1/0,h=-1/0,g=1/0,_=1/0,v=1/0,y=-1/0,b=-1/0,x=-1/0;for(;c<=l;){let e=i[c],t=e*3;a[t+n]<=r?(o[t]<u&&(u=o[t]),o[t+1]<d&&(d=o[t+1]),o[t+2]<f&&(f=o[t+2]),s[t]>p&&(p=s[t]),s[t+1]>m&&(m=s[t+1]),s[t+2]>h&&(h=s[t+2]),c++):(o[t]<g&&(g=o[t]),o[t+1]<_&&(_=o[t+1]),o[t+2]<v&&(v=o[t+2]),s[t]>y&&(y=s[t]),s[t+1]>b&&(b=s[t+1]),s[t+2]>x&&(x=s[t+2]),i[c]=i[l],i[l]=e,l--)}let S=this._partResult;return S.mid=c,S.lMinX=u,S.lMinY=d,S.lMinZ=f,S.lMaxX=p,S.lMaxY=m,S.lMaxZ=h,S.rMinX=g,S.rMinY=_,S.rMinZ=v,S.rMaxX=y,S.rMaxY=b,S.rMaxZ=x,S}updateNodeBounds(e,t,n){let r=1/0,i=1/0,a=1/0,o=-1/0,s=-1/0,c=-1/0,l=this.indices,u=this.bMin,d=this.bMax;for(let e=t;e<n;e++){let t=l[e]*3;u[t]<r&&(r=u[t]),u[t+1]<i&&(i=u[t+1]),u[t+2]<a&&(a=u[t+2]),d[t]>o&&(o=d[t]),d[t+1]>s&&(s=d[t+1]),d[t+2]>c&&(c=d[t+2])}e.minX=r,e.minY=i,e.minZ=a,e.maxX=o,e.maxY=s,e.maxZ=c}findBestSplitPositionSAH(e,t,n){let r=1/0,i=-1,a=0,o=this.computeSurfaceAreaFlat(n.minX,n.minY,n.minZ,n.maxX,n.maxY,n.maxZ),s=t-e,c=this.intersectionCost*s,l=this.getOptimalBinCount(s);this.splitStats.totalSplitAttempts++,this.splitStats.avgBinsUsed=(this.splitStats.avgBinsUsed*(this.splitStats.totalSplitAttempts-1)+l)/this.splitStats.totalSplitAttempts;let u=this.indices,d=this.centroids,f=this.bMin,p=this.bMax,m=this.binBoundsMin,h=this.binBoundsMax,g=this.binCounts,_=this.leftPrefixMin,v=this.leftPrefixMax,y=this.leftPrefixCount,b=this.rightPrefixMin,x=this.rightPrefixMax,S=this.rightPrefixCount,C=1/0,w=-1/0,T=1/0,E=-1/0,D=1/0,O=-1/0;for(let n=e;n<t;n++){let e=u[n]*3,t=d[e],r=d[e+1],i=d[e+2];t<C&&(C=t),t>w&&(w=t),r<T&&(T=r),r>E&&(E=r),i<D&&(D=i),i>O&&(O=i)}let k=[C,T,D],A=[w,E,O];for(let n=0;n<3;n++){let s=k[n],C=A[n];if(C-s<1e-6)continue;for(let e=0;e<l;e++){g[e]=0;let t=e*3;m[t]=1/0,m[t+1]=1/0,m[t+2]=1/0,h[t]=-1/0,h[t+1]=-1/0,h[t+2]=-1/0}let w=l/(C-s);for(let r=e;r<t;r++){let e=u[r],t=d[e*3+n],i=Math.floor((t-s)*w);i>=l&&(i=l-1),g[i]++;let a=i*3,o=e*3;f[o]<m[a]&&(m[a]=f[o]),f[o+1]<m[a+1]&&(m[a+1]=f[o+1]),f[o+2]<m[a+2]&&(m[a+2]=f[o+2]),p[o]>h[a]&&(h[a]=p[o]),p[o+1]>h[a+1]&&(h[a+1]=p[o+1]),p[o+2]>h[a+2]&&(h[a+2]=p[o+2])}y[0]=g[0],_[0]=m[0],_[1]=m[1],_[2]=m[2],v[0]=h[0],v[1]=h[1],v[2]=h[2];for(let e=1;e<l;e++){let t=e*3,n=(e-1)*3;y[e]=y[e-1]+g[e];let r=_[n],i=m[t],a=_[n+1],o=m[t+1],s=_[n+2],c=m[t+2];_[t]=r<i?r:i,_[t+1]=a<o?a:o,_[t+2]=s<c?s:c;let l=v[n],u=h[t],d=v[n+1],f=h[t+1],p=v[n+2],b=h[t+2];v[t]=l>u?l:u,v[t+1]=d>f?d:f,v[t+2]=p>b?p:b}let T=l-1,E=T*3;S[T]=g[T],b[E]=m[E],b[E+1]=m[E+1],b[E+2]=m[E+2],x[E]=h[E],x[E+1]=h[E+1],x[E+2]=h[E+2];for(let e=T-1;e>=0;e--){let t=e*3,n=(e+1)*3;S[e]=S[e+1]+g[e];let r=b[n],i=m[t],a=b[n+1],o=m[t+1],s=b[n+2],c=m[t+2];b[t]=r<i?r:i,b[t+1]=a<o?a:o,b[t+2]=s<c?s:c;let l=x[n],u=h[t],d=x[n+1],f=h[t+1],p=x[n+2],_=h[t+2];x[t]=l>u?l:u,x[t+1]=d>f?d:f,x[t+2]=p>_?p:_}for(let e=1;e<l;e++){let t=(e-1)*3,u=e*3,d=y[e-1],f=S[e];if(d===0||f===0)continue;let p=v[t]-_[t],m=v[t+1]-_[t+1],h=v[t+2]-_[t+2],g=2*(p*m+m*h+h*p),w=x[u]-b[u],T=x[u+1]-b[u+1],E=x[u+2]-b[u+2],D=2*(w*T+T*E+E*w),O=this.traversalCost+g/o*d*this.intersectionCost+D/o*f*this.intersectionCost;O<r&&O<c&&(r=O,i=n,a=s+(C-s)*e/l)}}return i===-1?this.enableObjectMedianFallback?this.findObjectMedianSplit(e,t):this.enableSpatialMedianFallback?this.findSpatialMedianSplit(e,t):{success:!1,method:`fallbacks_disabled`}:{success:!0,axis:i,pos:a,method:`SAH`,binsUsed:l}}findObjectMedianSplit(e,t){let n=this.indices,r=this.centroids,i=-1,a=-1;for(let o=0;o<3;o++){let s=1/0,c=-1/0;for(let i=e;i<t;i++){let e=r[n[i]*3+o];e<s&&(s=e),e>c&&(c=e)}let l=c-s;l>a&&(a=l,i=o)}if(i===-1||a<1e-10)return this.enableSpatialMedianFallback?this.findSpatialMedianSplit(e,t):{success:!1,method:`object_median_failed`};let o=t-e,s=e+Math.floor(o/2);this.quickselect(e,t,s,i);let c=r[n[s]*3+i],l=!0;for(let e=s+1;e<t;e++)if(r[n[e]*3+i]>c){l=!1;break}if(l){let a=-1/0;for(let t=e;t<s;t++){let e=r[n[t]*3+i];e>a&&(a=e)}if(a<c)c=(a+c)*.5;else return this.enableSpatialMedianFallback?this.findSpatialMedianSplit(e,t):{success:!1,method:`object_median_degenerate`}}return{success:!0,axis:i,pos:c,method:`object_median`}}findSpatialMedianSplit(e,t){let n=this.indices,r=this.centroids,i=this.bMin,a=this.bMax,o=-1,s=-1,c=0,l=0;for(let r=0;r<3;r++){let u=1/0,d=-1/0;for(let o=e;o<t;o++){let e=n[o]*3+r;i[e]<u&&(u=i[e]),a[e]>d&&(d=a[e])}let f=d-u;f>s&&(s=f,o=r,c=u,l=d)}if(o===-1||s<1e-12)return{success:!1,method:`spatial_median_failed`};let u=(c+l)*.5,d=t-e,f=0;for(let i=e;i<t;i++)r[n[i]*3+o]<=u&&f++;if(f===0||f===d){let i=e+Math.floor(d/2);this.quickselect(e,t,i,o);let a=r[n[i]*3+o],s=!0;for(let i=e;i<t;i++)if(r[n[i]*3+o]!==a){s=!1;break}if(s)return{success:!1,method:`spatial_median_degenerate`};let c=-1/0;for(let t=e;t<i;t++){let e=r[n[t]*3+o];e>c&&(c=e)}if(c<a)u=(c+a)*.5;else{let e=1/0;for(let a=i+1;a<t;a++){let t=r[n[a]*3+o];t<e&&(e=t)}u=(a+e)*.5}}return{success:!0,axis:o,pos:u,method:`spatial_median`}}quickselect(e,t,n,r){let i=this.indices,a=this.centroids,o=e,s=t-1;for(;o<s;){let e=o+s>>>1,t=a[i[o]*3+r],c=a[i[e]*3+r],l=a[i[s]*3+r];if(t>c){let t=i[o];i[o]=i[e],i[e]=t}if(t>l){let e=i[o];i[o]=i[s],i[s]=e}if(c>l){let t=i[e];i[e]=i[s],i[s]=t}let u=a[i[e]*3+r],d=o,f=s;for(;d<=f;){for(;a[i[d]*3+r]<u;)d++;for(;a[i[f]*3+r]>u;)f--;if(d<=f){let e=i[d];i[d]=i[f],i[f]=e,d++,f--}}f<n&&(o=d),d>n&&(s=f)}}applySAOrdering(e){if(!e||!e.leftChild)return;let t=[e],n=[];for(;t.length>0;){let e=t.pop();!e.leftChild||!e.rightChild||(n.push(e),t.push(e.leftChild),t.push(e.rightChild))}for(let e=n.length-1;e>=0;e--){let t=n[e],r=t.leftChild,i=t.rightChild,a=r.maxX-r.minX,o=r.maxY-r.minY,s=r.maxZ-r.minZ,c=i.maxX-i.minX,l=i.maxY-i.minY,u=i.maxZ-i.minZ;c*l+l*u+u*c>a*o+o*s+s*a&&(t.leftChild=i,t.rightChild=r)}}flattenBVH(e){let t=[],n=[e];for(;n.length>0;){let e=n.pop();e._flatIndex=t.length,t.push(e),e.rightChild&&n.push(e.rightChild),e.leftChild&&n.push(e.leftChild)}let r=new Float32Array(t.length*16);for(let e=0;e<t.length;e++){let n=t[e],i=e*16;if(n.leftChild){let e=n.leftChild,t=n.rightChild;r[i]=e.minX,r[i+1]=e.minY,r[i+2]=e.minZ,r[i+3]=e._flatIndex,r[i+4]=e.maxX,r[i+5]=e.maxY,r[i+6]=e.maxZ,r[i+7]=t._flatIndex,r[i+8]=t.minX,r[i+9]=t.minY,r[i+10]=t.minZ,r[i+12]=t.maxX,r[i+13]=t.maxY,r[i+14]=t.maxZ}else r[i]=n.triangleOffset,r[i+1]=n.triangleCount,r[i+3]=-1}return r}flattenBVHWithFrontier(e){let t=[],n=[e];for(;n.length>0;){let e=n.pop();e._flatIndex=t.length,t.push(e),e.rightChild&&n.push(e.rightChild),e.leftChild&&n.push(e.leftChild)}let r=new Float32Array(t.length*16),i=[];for(let e=0;e<t.length;e++){let n=t[e],a=e*16;if(n.leftChild){let e=n.leftChild,t=n.rightChild;r[a]=e.minX,r[a+1]=e.minY,r[a+2]=e.minZ,r[a+3]=e._flatIndex,r[a+4]=e.maxX,r[a+5]=e.maxY,r[a+6]=e.maxZ,r[a+7]=t._flatIndex,r[a+8]=t.minX,r[a+9]=t.minY,r[a+10]=t.minZ,r[a+12]=t.maxX,r[a+13]=t.maxY,r[a+14]=t.maxZ}else if(n.isFrontier){let t=n.frontierTaskId;r[a]=n.triangleOffset,r[a+1]=n.triangleCount,r[a+2]=t,r[a+3]=-2,i.push({taskId:t,flatIndex:e})}else r[a]=n.triangleOffset,r[a+1]=n.triangleCount,r[a+3]=-1}return{flatData:r,frontierMap:i,nodeCount:t.length}}assembleParallelBVH(e,t,n,r){let i=[...r].sort((e,t)=>e.taskId-t.taskId),a=t;for(let e=0;e<i.length;e++)a+=i[e].nodeCount;let o=new Float32Array(a*16);o.set(e);let s=new Map;for(let e of n)s.set(e.taskId,e.flatIndex);let c=t;for(let e=0;e<i.length;e++){let t=i[e],n=t.flatData,r=t.nodeCount,a=c*16;o.set(n,a);for(let e=0;e<r;e++){let t=a+e*16;o[t+3]!==-1&&(o[t+3]+=c,o[t+7]+=c)}let l=s.get(t.taskId);if(l!==void 0){let e=l*16,t=a;for(let n=0;n<16;n++)o[e+n]=o[t+n]}c+=r}return o}computeSurfaceAreaFlat(e,t,n,r,i,a){let o=r-e,s=i-t,c=a-n;return 2*(o*s+s*c+c*o)}};self.onmessage=function(t){let{tasks:r,sharedTriangleData:i,sharedCentroids:a,sharedBMin:o,sharedBMax:s,sharedIndices:c,triangleCount:l,maxLeafSize:u,numBins:d,maxBins:f,minBins:p,treeletConfig:m,reinsertionConfig:h,reportProgress:g}=t.data;for(let t=0;t<r.length;t++){let _=r[t];try{let t=new E;t.maxLeafSize=u,t.numBins=d,t.maxBins=f,t.minBins=p,t.triangles=new Float32Array(i),t.centroids=new Float32Array(a),t.bMin=new Float32Array(o),t.bMax=new Float32Array(s),t.indices=new Uint32Array(c),t.totalTriangles=l,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=g?e=>{self.postMessage({type:`progress`,taskId:_.taskId,progress:e})}:null,v=performance.now(),y=t.buildNodeRecursive(_.start,_.end,_.depth,r,_.preMinX,_.preMinY,_.preMinZ,_.preMaxX,_.preMaxY,_.preMaxZ);if(m&&m.enabled&&_.end-_.start>1e3){let n=_.end-_.start>5e4,r=n?3:m.size||5,i=n?10:20,a=new e(t.traversalCost,t.intersectionCost);a.setTreeletSize(r),a.setMinImprovement(m.minImprovement||.02),a.setMaxTreelets(i);let o=m.passes||1;for(let e=0;e<o;e++)try{a.optimizeBVH(y,null)}catch(t){console.error(`[BVHSubtreeWorker] Treelet pass ${e+1} error:`,t);break}}if(h&&h.enabled&&_.end-_.start>1e3){let e=new n(t.traversalCost,t.intersectionCost);h.batchSizeRatio&&e.setBatchSizeRatio(h.batchSizeRatio),h.maxIterations&&e.setMaxIterations(h.maxIterations);try{e.optimizeBVH(y,null)}catch(e){console.error(`[BVHSubtreeWorker] Reinsertion error:`,e)}}t.applySAOrdering(y);let b=t.flattenBVH(y),x=b.length/16,S=performance.now()-v;console.log(`[BVHSubtreeWorker] Task ${_.taskId}: ${(_.end-_.start).toLocaleString()} triangles, ${x} nodes, ${Math.round(S)}ms`),self.postMessage({type:`subtreeResult`,taskId:_.taskId,flatData:b,nodeCount:x},[b.buffer])}catch(e){console.error(`[BVHSubtreeWorker] Task ${_.taskId} error:`,e),self.postMessage({type:`error`,taskId:_.taskId,error:e.message})}}}})();
2
- //# sourceMappingURL=BVHSubtreeWorker-D7KMbrQ-.js.map