rayzee 4.8.4 → 4.8.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/README.md +141 -82
  2. package/dist/assets/AIUpscalerWorker-D58dcMrY.js +2 -0
  3. package/dist/assets/AIUpscalerWorker-D58dcMrY.js.map +1 -0
  4. package/dist/assets/BVHRefitWorker-GkmNJYvb.js +2 -0
  5. package/dist/assets/BVHRefitWorker-GkmNJYvb.js.map +1 -0
  6. package/dist/assets/BVHSubtreeWorker-C02ZWVeG.js +2 -0
  7. package/dist/assets/BVHSubtreeWorker-C02ZWVeG.js.map +1 -0
  8. package/dist/assets/BVHWorker-DobVXMda.js +2 -0
  9. package/dist/assets/BVHWorker-DobVXMda.js.map +1 -0
  10. package/dist/assets/CDFWorker-2MoynL4F.js +2 -0
  11. package/dist/assets/CDFWorker-2MoynL4F.js.map +1 -0
  12. package/dist/assets/TexturesWorker-DBqGmVdR.js +2 -0
  13. package/dist/assets/TexturesWorker-DBqGmVdR.js.map +1 -0
  14. package/dist/rayzee.es.js +922 -871
  15. package/dist/rayzee.es.js.map +1 -1
  16. package/dist/rayzee.umd.js +43 -43
  17. package/dist/rayzee.umd.js.map +1 -1
  18. package/package.json +1 -1
  19. package/src/Passes/AIUpscaler.js +1 -2
  20. package/src/PathTracerApp.js +36 -3
  21. package/src/Processor/AssetLoader.js +2 -2
  22. package/src/Processor/BVHBuilder.js +1 -2
  23. package/src/Processor/EquirectHDRInfo.js +1 -2
  24. package/src/Processor/LightSerializer.js +26 -7
  25. package/src/Processor/ParallelBVHBuilder.js +8 -9
  26. package/src/Processor/SceneProcessor.js +3 -4
  27. package/src/Processor/TextureCreator.js +1 -2
  28. package/src/Processor/Workers/AIUpscalerWorker.js +21 -7
  29. package/src/README.md +1 -2
  30. package/src/Stages/PathTracer.js +7 -6
  31. package/src/TSL/LightsCore.js +12 -2
  32. package/src/TSL/LightsSampling.js +8 -6
  33. package/src/managers/LightManager.js +1 -1
  34. package/src/managers/UniformManager.js +2 -2
  35. package/src/Processor/createWorker.js +0 -38
package/README.md CHANGED
@@ -5,22 +5,10 @@ A real-time WebGPU path tracing engine built on Three.js. Framework-agnostic —
5
5
  ## Installation
6
6
 
7
7
  ```bash
8
- npm install rayzee@github:atul-mourya/RayTracing three stats-gl
8
+ npm install rayzee three
9
9
  ```
10
10
 
11
- Or add to `package.json`:
12
-
13
- ```json
14
- {
15
- "dependencies": {
16
- "rayzee": "github:atul-mourya/RayTracing",
17
- "three": "^0.183.0",
18
- "stats-gl": "^4.0.2"
19
- }
20
- }
21
- ```
22
-
23
- npm publishing is coming soon. `three` and `stats-gl` are required peer dependencies.
11
+ `three` (>=0.170.0) is a required peer dependency. `stats-gl` is installed automatically as a transitive dependency.
24
12
 
25
13
  ## Getting Started
26
14
 
@@ -31,26 +19,10 @@ npm publishing is coming soon. `three` and `stats-gl` are required peer dependen
31
19
  ```bash
32
20
  npm create vite@latest my-raytracer -- --template vanilla
33
21
  cd my-raytracer
34
- npm install rayzee@github:atul-mourya/RayTracing three stats-gl
22
+ npm install rayzee three
35
23
  ```
36
24
 
37
- 2. **Configure Vite** WebGPU needs cross-origin isolation headers and `.hdr` asset support. Create or update `vite.config.js`:
38
-
39
- ```js
40
- import { defineConfig } from 'vite';
41
-
42
- export default defineConfig({
43
- server: {
44
- headers: {
45
- 'Cross-Origin-Opener-Policy': 'same-origin',
46
- 'Cross-Origin-Embedder-Policy': 'credentialless',
47
- },
48
- },
49
- assetsInclude: ['**/*.hdr'],
50
- });
51
- ```
52
-
53
- 3. **Set up the HTML**
25
+ 2. **Set up the HTML**
54
26
 
55
27
  ```html
56
28
  <!-- index.html -->
@@ -60,7 +32,7 @@ npm publishing is coming soon. `three` and `stats-gl` are required peer dependen
60
32
  </body>
61
33
  ```
62
34
 
63
- 4. **Write the code**
35
+ 3. **Write the code**
64
36
 
65
37
  ```js
66
38
  // main.js
@@ -88,11 +60,11 @@ npm publishing is coming soon. `three` and `stats-gl` are required peer dependen
88
60
  });
89
61
 
90
62
  // Tweak settings
91
- engine.set('maxBounces', 8);
63
+ engine.set('bounces', 8);
92
64
  engine.set('exposure', 1.2);
93
65
  ```
94
66
 
95
- 5. **Run**
67
+ 4. **Run**
96
68
 
97
69
  ```bash
98
70
  npm run dev
@@ -132,7 +104,8 @@ A single HTML file — no Node.js, no build step. Uses [ES module import maps](h
132
104
 
133
105
  const engine = new PathTracerApp(canvas);
134
106
  await engine.init();
135
- await engine.loadModel('https://example.com/scene.glb');
107
+ // Replace with your own model URL
108
+ await engine.loadModel('https://your-cdn.com/scene.glb');
136
109
  engine.animate();
137
110
 
138
111
  window.addEventListener('resize', () => {
@@ -164,25 +137,27 @@ export default function Viewport({ modelUrl }) {
164
137
  const engineRef = useRef(null);
165
138
 
166
139
  useEffect(() => {
167
- let engine;
140
+ const canvas = canvasRef.current;
141
+ canvas.width = canvas.clientWidth;
142
+ canvas.height = canvas.clientHeight;
168
143
 
169
- async function start() {
170
- engine = new PathTracerApp(canvasRef.current);
144
+ const engine = new PathTracerApp(canvas);
145
+ engineRef.current = engine;
146
+
147
+ (async () => {
171
148
  await engine.init();
172
149
  if (modelUrl) await engine.loadModel(modelUrl);
173
150
  engine.animate();
174
- engineRef.current = engine;
175
- }
151
+ })();
176
152
 
177
- start();
178
- return () => engine?.dispose();
153
+ return () => engine.dispose();
179
154
  }, [modelUrl]);
180
155
 
181
156
  return <canvas ref={canvasRef} style={{ width: '100%', height: '100vh' }} />;
182
157
  }
183
158
  ```
184
159
 
185
- The same Vite config (headers + assetsInclude) applies for React projects.
160
+ No special build config is needed models and HDRs are loaded via URL at runtime.
186
161
 
187
162
  ## API Reference
188
163
 
@@ -191,14 +166,14 @@ The same Vite config (headers + assetsInclude) applies for React projects.
191
166
  The main engine class. Extends Three.js `EventDispatcher`.
192
167
 
193
168
  ```js
194
- const engine = new PathTracerApp(canvas, denoiserCanvas?, options?)
169
+ const engine = new PathTracerApp(canvas, options?)
195
170
  ```
196
171
 
197
172
  | Parameter | Type | Description |
198
173
  |---|---|---|
199
174
  | `canvas` | `HTMLCanvasElement` | Rendering target |
200
- | `denoiserCanvas` | `HTMLCanvasElement` | Optional canvas for OIDN denoiser output |
201
175
  | `options.autoResize` | `boolean` | Auto-resize on window resize (default: `true`) |
176
+ | `options.statsContainer` | `HTMLElement` | DOM element to append the stats panel to (defaults to `document.body`) |
202
177
 
203
178
  #### Lifecycle
204
179
 
@@ -209,25 +184,29 @@ engine.pause() // Pause rendering
209
184
  engine.resume() // Resume rendering
210
185
  engine.reset() // Reset accumulation (restart from sample 0)
211
186
  engine.dispose() // Clean up all resources
187
+ engine.wake() // Resume render loop if idle (called automatically on interaction)
188
+ engine.isComplete() // Check if rendering has converged
189
+ engine.getFrameCount() // Get the current accumulated frame count
212
190
  ```
213
191
 
214
192
  #### Loading Assets
215
193
 
216
194
  ```js
217
195
  await engine.loadModel(url) // Load GLB/GLTF/FBX/OBJ/STL/PLY/DAE/3MF/USDZ
196
+ await engine.loadObject3D(object3d) // Load a Three.js Object3D directly
218
197
  await engine.loadEnvironment(url) // Load HDR/EXR environment map
219
198
  ```
220
199
 
221
200
  #### Settings
222
201
 
223
202
  ```js
224
- engine.set('maxBounces', 8) // Set a single parameter
203
+ engine.set('bounces', 8) // Set a single parameter
225
204
  engine.setMany({ // Set multiple parameters at once
226
- maxBounces: 8,
205
+ bounces: 8,
227
206
  samplesPerPixel: 1,
228
207
  exposure: 1.0
229
208
  })
230
- engine.get('maxBounces') // Read a parameter
209
+ engine.get('bounces') // Read a parameter
231
210
  engine.getAll() // Get all current settings
232
211
  ```
233
212
 
@@ -235,25 +214,33 @@ Key settings:
235
214
 
236
215
  | Setting | Type | Default | Description |
237
216
  |---|---|---|---|
238
- | `maxBounces` | `number` | 5 | Max ray bounce depth |
217
+ | `bounces` | `number` | 3 | Max ray bounce depth |
239
218
  | `samplesPerPixel` | `number` | 1 | Samples per pixel per frame |
219
+ | `maxSamples` | `number` | 60 | Max accumulated samples before stopping |
240
220
  | `exposure` | `number` | 1.0 | Exposure value |
221
+ | `saturation` | `number` | 1.2 | Color saturation |
241
222
  | `enableEnvironment` | `boolean` | true | Use environment lighting |
242
223
  | `environmentIntensity` | `number` | 1.0 | Environment light strength |
243
- | `environmentRotation` | `number` | 0 | Environment Y-rotation (radians) |
244
- | `fireflyThreshold` | `number` | 10 | Firefly clamping threshold |
224
+ | `environmentRotation` | `number` | 270 | Environment Y-rotation (degrees) |
225
+ | `fireflyThreshold` | `number` | 3.0 | Firefly clamping threshold |
245
226
  | `transmissiveBounces` | `number` | 5 | Max bounces for transmissive materials |
246
- | `visMode` | `number` | 0 | Debug visualization mode (0 = off) |
227
+ | `enableDOF` | `boolean` | false | Enable depth of field |
228
+ | `focusDistance` | `number` | 0.8 | DOF focus distance |
229
+ | `aperture` | `number` | 5.6 | DOF aperture (f-stop) |
230
+ | `focalLength` | `number` | 50 | DOF focal length (mm) |
231
+ | `adaptiveSampling` | `boolean` | false | Variance-guided sample distribution |
232
+ | `transparentBackground` | `boolean` | false | Transparent canvas background |
233
+ | `debugMode` | `number` | 0 | Debug visualization mode (0 = off) |
234
+ | `environmentMode` | `string` | 'hdri' | Sky mode: `'hdri'` \| `'procedural'` \| `'gradient'` \| `'color'` |
247
235
 
248
236
  See `ENGINE_DEFAULTS` for the full list with default values.
249
237
 
250
238
  #### Rendering Modes
251
239
 
252
240
  ```js
253
- import { FINAL_RENDER_CONFIG, PREVIEW_RENDER_CONFIG } from 'rayzee';
254
-
255
- engine.configureForMode('final') // High quality (tiled, 20 bounces)
256
- engine.configureForMode('interactive') // Real-time navigation (3 bounces)
241
+ engine.configureForMode('final-render') // High quality (tiled, 20 bounces, OIDN)
242
+ engine.configureForMode('preview') // Real-time navigation (3 bounces)
243
+ engine.configureForMode('results') // Paused rendering for image viewing
257
244
  ```
258
245
 
259
246
  #### Camera
@@ -262,6 +249,9 @@ engine.configureForMode('interactive') // Real-time navigation (3 bounces)
262
249
  engine.switchCamera(index) // Switch between scene cameras
263
250
  engine.getCameraNames() // List available cameras
264
251
  engine.toggleFocusMode() // Enable click-to-focus DOF
252
+ engine.focusOnPoint(center) // Focus orbit camera on a world-space point
253
+ engine.camera // Access the active PerspectiveCamera
254
+ engine.controls // Access OrbitControls
265
255
  ```
266
256
 
267
257
  #### Lights
@@ -269,17 +259,58 @@ engine.toggleFocusMode() // Enable click-to-focus DOF
269
259
  ```js
270
260
  engine.addLight('point') // Add a light (point, spot, directional, area)
271
261
  engine.removeLight(uuid) // Remove by UUID
262
+ engine.clearLights() // Remove all lights
272
263
  engine.getLights() // Get all lights
264
+ engine.updateLights() // Re-upload light data to GPU
273
265
  engine.setShowLightHelper(true) // Toggle visual helpers
274
266
  ```
275
267
 
268
+ #### Object Selection & Transform
269
+
270
+ ```js
271
+ engine.toggleSelectMode() // Toggle object selection mode
272
+ engine.selectObject(object) // Programmatically select an object
273
+ engine.setTransformMode('translate') // Set gizmo mode: 'translate' | 'rotate' | 'scale'
274
+ engine.setTransformSpace('world') // Set gizmo space: 'world' | 'local'
275
+ engine.transformManager // Access the underlying TransformManager
276
+ ```
277
+
278
+ #### Animation
279
+
280
+ ```js
281
+ engine.playAnimation(clipIndex) // Play a GLTF animation clip
282
+ engine.pauseAnimation() // Pause playback
283
+ engine.resumeAnimation() // Resume playback
284
+ engine.stopAnimationPlayback() // Stop and reset
285
+ engine.setAnimationSpeed(speed) // Set playback speed multiplier
286
+ engine.setAnimationLoop(loop) // Enable/disable looping
287
+ engine.animationClips // Get available animation clips
288
+ ```
289
+
276
290
  #### Denoising
277
291
 
278
292
  ```js
279
- engine.setDenoiserStrategy('asvgf') // Real-time temporal denoiser
280
- engine.setDenoiserStrategy('oidn') // Intel OIDN (higher quality, final renders)
281
- engine.setDenoiserStrategy('none') // No denoising
282
- engine.setASVGFEnabled(true, 'balanced') // ASVGF with quality preset
293
+ engine.setDenoiserStrategy('asvgf') // Real-time temporal denoiser
294
+ engine.setDenoiserStrategy('oidn') // Intel OIDN (higher quality, final renders)
295
+ engine.setDenoiserStrategy('edgeaware') // Edge-preserving temporal filter (default)
296
+ engine.setDenoiserStrategy('none') // No denoising
297
+ engine.setASVGFEnabled(true, 'medium') // ASVGF with quality preset (low/medium/high)
298
+ engine.applyASVGFPreset('high') // Apply an ASVGF quality preset
299
+ engine.setAutoExposureEnabled(true) // Toggle auto-exposure
300
+ engine.setAdaptiveSamplingEnabled(true) // Toggle adaptive sampling
301
+ ```
302
+
303
+ #### Environment
304
+
305
+ ```js
306
+ engine.getEnvParams() // Get current environment parameters
307
+ engine.getEnvironmentTexture() // Get the loaded environment texture
308
+ engine.setEnvironmentMap(texture) // Set a custom environment texture
309
+ engine.setEnvironmentMode('procedural') // Switch sky mode
310
+ engine.generateProceduralSkyTexture() // Generate Preetham-model sky
311
+ engine.generateGradientTexture() // Generate gradient sky
312
+ engine.generateSolidColorTexture() // Generate solid color sky
313
+ engine.markEnvironmentNeedsUpdate() // Flag environment for GPU re-upload
283
314
  ```
284
315
 
285
316
  #### Canvas & Resolution
@@ -287,6 +318,25 @@ engine.setASVGFEnabled(true, 'balanced') // ASVGF with quality preset
287
318
  ```js
288
319
  engine.setCanvasSize(1920, 1080) // Set explicit canvas dimensions
289
320
  engine.onResize() // Trigger manual resize recalculation
321
+ engine.getOutputCanvas() // Get the canvas with the final rendered image
322
+ engine.takeScreenshot() // Download a PNG screenshot
323
+ ```
324
+
325
+ #### Materials
326
+
327
+ ```js
328
+ engine.updateMaterialProperty(index, property, value) // Update a material property
329
+ engine.updateTextureTransform(index, textureName, transform)
330
+ engine.refreshMaterial() // Re-upload all material data to GPU
331
+ engine.updateMaterial(index, material) // Replace a material entirely
332
+ await engine.rebuildMaterials(scene) // Full material rebuild (texture changes)
333
+ ```
334
+
335
+ #### Scene Info
336
+
337
+ ```js
338
+ engine.getSceneStatistics() // Triangle count, mesh count, etc.
339
+ engine.stages // Named access to all pipeline stages
290
340
  ```
291
341
 
292
342
  ### Events
@@ -297,40 +347,47 @@ Subscribe to engine lifecycle events via `addEventListener`:
297
347
  import { EngineEvents } from 'rayzee';
298
348
 
299
349
  engine.addEventListener(EngineEvents.RENDER_COMPLETE, (e) => {
300
- console.log('Render complete', e.detail);
350
+ console.log('Render complete');
301
351
  });
302
352
 
303
353
  engine.addEventListener(EngineEvents.STATS_UPDATE, (e) => {
304
- console.log('FPS:', e.detail.fps);
354
+ console.log('Stats:', e);
305
355
  });
306
356
  ```
307
357
 
308
358
  | Event | Fired when |
309
359
  |---|---|
310
- | `RENDER_COMPLETE` | A frame finishes rendering |
360
+ | `RENDER_COMPLETE` | Rendering has converged |
311
361
  | `RENDER_RESET` | Accumulation buffer is reset |
312
362
  | `DENOISING_START` / `DENOISING_END` | Denoiser runs |
313
363
  | `UPSCALING_START` / `UPSCALING_PROGRESS` / `UPSCALING_END` | AI upscaler runs |
314
364
  | `LOADING_UPDATE` / `LOADING_RESET` | Asset loading progress |
315
365
  | `STATS_UPDATE` | Performance stats updated |
316
366
  | `OBJECT_SELECTED` / `OBJECT_DESELECTED` | Object selection changes |
367
+ | `OBJECT_DOUBLE_CLICKED` | Object double-clicked |
368
+ | `OBJECT_TRANSFORM_START` / `OBJECT_TRANSFORM_END` | Transform gizmo drag |
369
+ | `TRANSFORM_MODE_CHANGED` | Gizmo mode changed |
370
+ | `SELECT_MODE_CHANGED` | Selection mode toggled |
317
371
  | `SETTING_CHANGED` | A render setting is modified |
318
372
  | `AUTO_FOCUS_UPDATED` | Auto-focus recalculated |
319
373
  | `AUTO_EXPOSURE_UPDATED` | Auto-exposure recalculated |
374
+ | `AF_POINT_PLACED` | Focus point placed on screen |
375
+ | `ANIMATION_STARTED` / `ANIMATION_PAUSED` / `ANIMATION_STOPPED` / `ANIMATION_FINISHED` | Animation lifecycle |
376
+ | `VIDEO_RENDER_PROGRESS` / `VIDEO_RENDER_COMPLETE` | Video export progress |
320
377
 
321
378
  ### Advanced: Custom Pipeline Stages
322
379
 
323
380
  Build custom rendering stages by extending `RenderStage`:
324
381
 
325
382
  ```js
326
- import { RenderStage, PipelineContext } from 'rayzee';
383
+ import { RenderStage } from 'rayzee';
327
384
 
328
385
  class MyCustomStage extends RenderStage {
329
386
  constructor() {
330
387
  super('my-stage');
331
388
  }
332
389
 
333
- render(renderer, context) {
390
+ render(context, writeBuffer) {
334
391
  const input = context.getTexture('pathtracer:color');
335
392
  // ... process input, write output
336
393
  context.setTexture('my-stage:output', this.outputTexture);
@@ -351,6 +408,13 @@ import {
351
408
  CAMERA_PRESETS,
352
409
  CAMERA_RANGES,
353
410
  SKY_PRESETS,
411
+ AUTO_FOCUS_MODES,
412
+ AF_DEFAULTS,
413
+ TRIANGLE_DATA_LAYOUT,
414
+ BVH_LEAF_MARKERS,
415
+ TEXTURE_CONSTANTS,
416
+ DEFAULT_TEXTURE_MATRIX,
417
+ MEMORY_CONSTANTS,
354
418
  FINAL_RENDER_CONFIG,
355
419
  PREVIEW_RENDER_CONFIG,
356
420
  } from 'rayzee';
@@ -361,6 +425,10 @@ import {
361
425
  CameraManager,
362
426
  LightManager,
363
427
  DenoisingManager,
428
+ OverlayManager,
429
+ AnimationManager,
430
+ TransformManager,
431
+ VideoRenderManager,
364
432
  } from 'rayzee';
365
433
 
366
434
  // Advanced: pipeline infrastructure
@@ -374,32 +442,23 @@ import {
374
442
 
375
443
  ## Browser Requirements
376
444
 
377
- - WebGPU support (Chrome 113+, Edge 113+, Firefox Nightly)
445
+ - WebGPU support (Chrome 113+, Edge 113+, Safari 18+, Firefox 141+)
378
446
  - Secure context (HTTPS or localhost)
379
447
 
380
448
  ## Optional Dependencies
381
449
 
382
- | Package | Purpose |
383
- |---|---|
384
- | `oidn-web` | Intel Open Image Denoise for high-quality final renders |
385
- | `onnxruntime-web` | AI-powered upscaling |
386
-
387
- Install them alongside rayzee if needed:
388
-
389
- ```bash
390
- npm install oidn-web onnxruntime-web
391
- ```
450
+ | Package | Purpose | Install needed? |
451
+ |---|---|---|
452
+ | `oidn-web` | Intel Open Image Denoise for high-quality final renders | Yes — `npm install oidn-web` |
453
+ | `onnxruntime-web` | AI-powered upscaling | No — loaded from CDN at runtime |
392
454
 
393
455
  ## Troubleshooting
394
456
 
395
457
  **Black screen / "WebGPU not supported"**
396
- Your browser may not support WebGPU. Use Chrome 113+ or Edge 113+. Ensure you're on HTTPS or localhost.
397
-
398
- **CORS errors loading models/HDRs**
399
- Add cross-origin isolation headers to your dev server (see the Vite config in Getting Started).
458
+ Your browser may not support WebGPU. Use Chrome 113+, Edge 113+, Safari 18+, or Firefox 141+. Ensure you're on HTTPS or localhost.
400
459
 
401
460
  **Models not loading**
402
- Place `.glb` / `.hdr` files in your `public/` folder and reference them with absolute paths (e.g., `/scene.glb`).
461
+ If serving locally, place files in your `public/` folder and reference them with absolute paths (e.g., `/scene.glb`). For remote files, ensure the server allows CORS.
403
462
 
404
463
  ## License
405
464
 
@@ -0,0 +1,2 @@
1
+ (function(){let e=null;async function t(){return e||(e=await import(`https://cdn.jsdelivr.net/npm/onnxruntime-web@1.24.3/dist/ort.webgpu.bundle.min.mjs`),e.env.wasm.wasmPaths=`https://cdn.jsdelivr.net/npm/onnxruntime-web@1.24.3/dist/`,e.env.logLevel=`error`,e)}let n=`models`,r=null,i=null;function a(){return new Promise((e,t)=>{let r=indexedDB.open(`ai-upscaler-models`,1);r.onupgradeneeded=()=>r.result.createObjectStore(n),r.onsuccess=()=>e(r.result),r.onerror=()=>t(r.error)})}async function o(e){try{let t=await a();return await new Promise((r,i)=>{let a=t.transaction(n,`readonly`).objectStore(n).get(e);a.onsuccess=()=>r(a.result||null),a.onerror=()=>i(a.error)})}catch{return null}}async function s(e,t){try{let r=await a();await new Promise((i,a)=>{let o=r.transaction(n,`readwrite`);o.objectStore(n).put(t,e),o.oncomplete=()=>i(),o.onerror=()=>a(o.error)})}catch{}}async function c(e){let t=await o(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 s(e,r.slice(0)),r}async function l(e,n){if(r&&i===e){self.postMessage({type:`loaded`,backend:`webgpu`});return}r&&=(await r.release(),null);let[a,o]=await Promise.all([c(e),t()]);r=await o.InferenceSession.create(a,n),i=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 l=(a.byteLength/1024/1024).toFixed(1);console.log(`AI Upscaler Worker: model loaded (${l}MB), backend: webgpu`),self.postMessage({type:`loaded`,backend:`webgpu`,tileSize:s})}async function u(e,n,i,a){let o=await t(),s=r.inputNames[0],c=r.outputNames[0],l=new o.Tensor(`float32`,e,[1,3,i,n]),u=(await r.run({[s]:l}))[c].data;self.postMessage({type:`inferred`,outputData:u,id:a},[u.buffer])}self.onmessage=async e=>{let{type:t}=e.data;try{t===`load`?await l(e.data.url,e.data.sessionOptions):t===`infer`?await u(e.data.tileData,e.data.width,e.data.height,e.data.id):t===`dispose`&&r&&(await r.release(),r=null,i=null)}catch(t){self.postMessage({type:`error`,message:t.message,id:e.data?.id})}}})();
2
+ //# sourceMappingURL=AIUpscalerWorker-D58dcMrY.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AIUpscalerWorker-D58dcMrY.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// Loaded lazily via CDN to avoid bundling the 69 MB onnxruntime-web package\nconst ORT_CDN_URL = 'https://cdn.jsdelivr.net/npm/onnxruntime-web@1.24.3/dist/ort.webgpu.bundle.min.mjs';\n\nlet ort = null;\n\nasync function getOrt() {\n\n\tif ( ort ) return ort;\n\n\tort = await import( /* @vite-ignore */ ORT_CDN_URL );\n\n\t// WASM paths for CDN delivery — WebGPU EP still uses WASM for lightweight shape ops\n\tort.env.wasm.wasmPaths = 'https://cdn.jsdelivr.net/npm/onnxruntime-web@1.24.3/dist/';\n\tort.env.logLevel = 'error';\n\n\treturn ort;\n\n}\n\nconst IDB_NAME = 'ai-upscaler-models';\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( IDB_NAME, 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\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":"YAiBA,IAEI,EAAM,KAEV,eAAe,GAAS,CAUvB,OARK,IAEL,EAAM,MAAM,OAA2B,sFAGvC,EAAI,IAAI,KAAK,UAAY,4DACzB,EAAI,IAAI,SAAW,QAEZ,GAIR,IACM,EAAY,SAEd,EAAU,KACV,EAAkB,KAItB,SAAS,GAAS,CAEjB,OAAO,IAAI,SAAW,EAAS,IAAY,CAE1C,IAAM,EAAM,UAAU,KAAM,qBAAU,EAAG,CACzC,EAAI,oBAAwB,EAAI,OAAO,kBAAmB,EAAW,CACrE,EAAI,cAAkB,EAAS,EAAI,OAAQ,CAC3C,EAAI,YAAgB,EAAQ,EAAI,MAAO,EAErC,CAIJ,eAAe,EAAgB,EAAM,CAEpC,GAAI,CAEH,IAAM,EAAK,MAAM,GAAQ,CACzB,OAAO,MAAM,IAAI,SAAW,EAAS,IAAY,CAGhD,IAAM,EADK,EAAG,YAAa,EAAW,WAAY,CACnC,YAAa,EAAW,CAAC,IAAK,EAAK,CAClD,EAAI,cAAkB,EAAS,EAAI,QAAU,KAAM,CACnD,EAAI,YAAgB,EAAQ,EAAI,MAAO,EAErC,MAEI,CAEP,OAAO,MAMT,eAAe,EAAY,EAAK,EAAS,CAExC,GAAI,CAEH,IAAM,EAAK,MAAM,GAAQ,CACzB,MAAM,IAAI,SAAW,EAAS,IAAY,CAEzC,IAAM,EAAK,EAAG,YAAa,EAAW,YAAa,CACnD,EAAG,YAAa,EAAW,CAAC,IAAK,EAAQ,EAAK,CAC9C,EAAG,eAAmB,GAAS,CAC/B,EAAG,YAAgB,EAAQ,EAAG,MAAO,EAEnC,MAEI,GAST,eAAe,EAAY,EAAM,CAGhC,IAAM,EAAS,MAAM,EAAgB,EAAK,CAC1C,GAAK,EAGJ,OADA,QAAQ,IAAK,iDAAkD,EAAO,WAAa,KAAO,MAAO,QAAS,EAAG,CAAC,KAAM,CAC7G,EAKR,IAAM,EAAW,MAAM,MAAO,EAAK,CACnC,GAAK,CAAE,EAAS,GAAK,MAAU,MAAO,0BAA0B,EAAS,SAAU,CACnF,IAAM,EAAS,MAAM,EAAS,aAAa,CAK3C,OAFA,EAAY,EAAK,EAAO,MAAO,EAAG,CAAE,CAE7B,EAIR,eAAe,EAAW,EAAK,EAAiB,CAE/C,GAAK,GAAW,IAAoB,EAAM,CAGzC,KAAK,YAAa,CAAE,KAAM,SAAU,QADpB,SAC6B,CAAE,CAC/C,OAKD,AAGC,KADA,MAAM,EAAQ,SAAS,CACb,MAIX,GAAM,CAAE,EAAa,GAAW,MAAM,QAAQ,IAAK,CAAE,EAAY,EAAK,CAAE,GAAQ,CAAE,CAAE,CAEpF,EAAU,MAAM,EAAO,iBAAiB,OAAQ,EAAa,EAAgB,CAC7E,EAAkB,EAGlB,IAAI,EAAW,IACf,GAAI,CAEH,IAAM,EAAU,MAAM,UAAU,KAAK,gBAAgB,CAC/C,EAAO,MAAM,GAAS,sBAAsB,EAAI,GAAS,KACzD,EAAW,8BAA8B,KAAM,GAAM,QAAU,GAAI,EACrE,qBAAqB,KAAM,GAAM,cAAgB,GAAI,CACnD,EAAe,GAAM,QAAQ,eAAe,EAAE,SAAU,aAAc,EACxE,+DAA+D,KAAM,GAAM,aAAe,GAAI,CAElG,AAUC,EAVI,EAEO,IAEA,EAEA,IAIA,IAIZ,QAAQ,IAAK,4BAA4B,GAAM,aAAe,GAAM,QAAU,UAAU,cAAc,IAAY,MAE3G,EAER,IAAM,GAAW,EAAY,WAAa,KAAO,MAAO,QAAS,EAAG,CACpE,QAAQ,IAAK,qCAAqC,EAAO,sBAAuB,CAEhF,KAAK,YAAa,CAAE,KAAM,SAAU,QAAS,SAAU,WAAU,CAAE,CAIpE,eAAe,EAAW,EAAU,EAAO,EAAQ,EAAK,CAEvD,IAAM,EAAS,MAAM,GAAQ,CACvB,EAAY,EAAQ,WAAY,GAChC,EAAa,EAAQ,YAAa,GAClC,EAAc,IAAI,EAAO,OAAQ,UAAW,EAAU,CAAE,EAAG,EAAG,EAAQ,EAAO,CAAE,CAG/E,GADU,MAAM,EAAQ,IAAK,EAAI,GAAa,EAAa,CAAE,EACvC,GAAa,KAGzC,KAAK,YAAa,CAAE,KAAM,WAAY,aAAY,KAAI,CAAE,CAAE,EAAW,OAAQ,CAAE,CAIhF,KAAK,UAAY,KAAQ,IAAO,CAE/B,GAAM,CAAE,QAAS,EAAE,KAEnB,GAAI,CAEE,IAAS,OAEb,MAAM,EAAW,EAAE,KAAK,IAAK,EAAE,KAAK,eAAgB,CAEzC,IAAS,QAEpB,MAAM,EAAW,EAAE,KAAK,SAAU,EAAE,KAAK,MAAO,EAAE,KAAK,OAAQ,EAAE,KAAK,GAAI,CAE/D,IAAS,WAEf,IAEJ,MAAM,EAAQ,SAAS,CACvB,EAAU,KACV,EAAkB,YAMX,EAAQ,CAEjB,KAAK,YAAa,CAAE,KAAM,QAAS,QAAS,EAAM,QAAS,GAAI,EAAE,MAAM,GAAI,CAAE"}
@@ -0,0 +1,2 @@
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
@@ -0,0 +1 @@
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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"}
@@ -0,0 +1,2 @@
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-DobVXMda.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=e=>_>0?(e/_*100).toFixed(1)+`%`:`0%`,y=[{Phase:`Init + bounds`,"Time (ms)":Math.round(this.splitStats.initTime),"%":v(this.splitStats.initTime)},{Phase:`Morton sort`,"Time (ms)":Math.round(this.splitStats.mortonSortTime),"%":v(this.splitStats.mortonSortTime)},{Phase:`SAH recursive build`,"Time (ms)":Math.round(this.splitStats.sahBuildTime),"%":v(this.splitStats.sahBuildTime)},{Phase:`Treelet optimization`,"Time (ms)":Math.round(this.splitStats.treeletOptimizationTime),"%":v(this.splitStats.treeletOptimizationTime)},{Phase:`Reinsertion optimization`,"Time (ms)":Math.round(this.splitStats.reinsertionOptimizationTime),"%":v(this.splitStats.reinsertionOptimizationTime)},{Phase:`SA ordering`,"Time (ms)":Math.round(this.splitStats.saOrderTime),"%":v(this.splitStats.saOrderTime)},{Phase:`Triangle reorder`,"Time (ms)":Math.round(this.splitStats.reorderTime),"%":v(this.splitStats.reorderTime)},{Phase:`TOTAL`,"Time (ms)":Math.round(_),"%":`100%`}];return console.groupCollapsed(`⏱ BVH Build (${c.toLocaleString()} triangles, ${Math.round(_)}ms)`),console.table(y),console.table({"Total Nodes":this.totalNodes,"Max Leaf Size":this.maxLeafSize,"SAH Splits":this.splitStats.sahSplits,"Object Median Splits":this.splitStats.objectMedianSplits,"Spatial Median Splits":this.splitStats.spatialMedianSplits,"Failed Splits":this.splitStats.failedSplits,"Treelets Processed":this.splitStats.treeletsProcessed,"Treelets Improved":this.splitStats.treeletsImproved,"Avg SAH Improvement":(this.splitStats.averageSAHImprovement*100).toFixed(2)+`%`,"Reinsertions Applied":this.splitStats.reinsertionsApplied,"Reinsertion Iterations":this.splitStats.reinsertionIterations}),console.groupEnd(),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-C02ZWVeG.js.map