rayzee 7.31.1 → 8.0.0

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 (51) hide show
  1. package/README.md +164 -0
  2. package/dist/rayzee.es.js +6366 -4361
  3. package/dist/rayzee.umd.js +122 -73
  4. package/package.json +1 -1
  5. package/src/EngineDefaults.js +168 -13
  6. package/src/EngineIssues.js +5 -0
  7. package/src/PathTracerApp.js +120 -19
  8. package/src/Processor/ArchiveReader.js +538 -0
  9. package/src/Processor/AssetLoader.js +265 -23
  10. package/src/Processor/BVHBuilder.js +48 -32
  11. package/src/Processor/BVHRefitter.js +244 -99
  12. package/src/Processor/ChunkedRecords.js +302 -0
  13. package/src/Processor/EmissiveTriangleBuilder.js +48 -21
  14. package/src/Processor/GeometryExtractor.js +577 -92
  15. package/src/Processor/HostMemory.js +222 -0
  16. package/src/Processor/InstanceTable.js +548 -97
  17. package/src/Processor/PBRT/EqualAreaOctahedral.js +32 -8
  18. package/src/Processor/PBRT/PBRTCurves.js +249 -0
  19. package/src/Processor/PBRT/PBRTMath.js +19 -0
  20. package/src/Processor/PBRT/PBRTParser.js +187 -41
  21. package/src/Processor/PBRT/PBRTSceneBuilder.js +586 -42
  22. package/src/Processor/PBRT/PBRTTokenizer.js +205 -65
  23. package/src/Processor/PBRT/index.js +257 -50
  24. package/src/Processor/PackedRayBuffer.js +29 -18
  25. package/src/Processor/ParallelBVHBuilder.js +9 -8
  26. package/src/Processor/QueueManager.js +5 -3
  27. package/src/Processor/SceneProcessor.js +1046 -365
  28. package/src/Processor/TLASBuilder.js +546 -201
  29. package/src/Processor/TextureCreator.js +11 -9
  30. package/src/Processor/VRAMTracker.js +12 -3
  31. package/src/Processor/Workers/BVHRefitWorker.js +16 -11
  32. package/src/Processor/Workers/BVHWorker.js +12 -12
  33. package/src/Processor/Workers/TLASWorker.js +37 -0
  34. package/src/Stages/NormalDepth.js +6 -2
  35. package/src/Stages/PathTracer.js +5 -5
  36. package/src/Stages/PathTracerStage.js +163 -41
  37. package/src/TSL/BVHTraversal.js +208 -90
  38. package/src/TSL/Common.js +135 -2
  39. package/src/TSL/Displacement.js +21 -11
  40. package/src/TSL/EmissiveSampling.js +49 -25
  41. package/src/TSL/ExtendKernel.js +3 -2
  42. package/src/TSL/LightBVHSampling.js +7 -4
  43. package/src/TSL/LightsDirect.js +30 -29
  44. package/src/TSL/ShadeKernel.js +8 -6
  45. package/src/TSL/Struct.js +3 -0
  46. package/src/TSL/TextureSampling.js +24 -9
  47. package/src/TSL/patches.js +128 -3
  48. package/src/index.js +3 -0
  49. package/src/managers/AnimationManager.js +86 -76
  50. package/src/managers/MaterialDataManager.js +29 -23
  51. package/src/managers/TransformManager.js +9 -192
package/README.md CHANGED
@@ -30,6 +30,8 @@ A real-time WebGPU path tracing engine built on Three.js. Framework-agnostic —
30
30
  - [engine.denoisingManager](#enginedenoisingmanager)
31
31
  - [engine.interactionManager](#engineinteractionmanager)
32
32
  - [engine.transformManager](#enginetransformmanager)
33
+ - [Moving and Deforming Objects](#moving-and-deforming-objects)
34
+ - [Degradation contract](#degradation-contract)
33
35
  - [Output Methods](#output-methods)
34
36
  - [Render Resolution Reserve](#render-resolution-reserve)
35
37
  - [Memory Monitoring](#memory-monitoring)
@@ -309,6 +311,9 @@ const engine = new PathTracerApp(canvas, options?)
309
311
  | `canvas` | `HTMLCanvasElement` | Rendering target |
310
312
  | `options.autoResize` | `boolean` | Auto-resize on window resize (default: `true`) |
311
313
  | `options.container` | `HTMLElement` | Single DOM parent the engine mounts auxiliary elements into — HUD overlay (tile borders, helpers) and denoiser canvas. Defaults to `canvas.parentNode`. |
314
+ | `options.strict` | `boolean` | Throw an `EngineIssueError` where the engine would otherwise degrade and carry on (default: `false`). See [Degradation contract](#degradation-contract). |
315
+ | `options.profile` | `string` | `'viewer'` (default) or `'physical'` — product tuning that is not a physical constant: area-light scale, environment rotation, tone mapping, saturation. An unknown name throws. |
316
+ | `options.maxSceneBytes` | `number` | Raise or lower the CPU memory ceiling a scene may need before the engine refuses it (default 9,216 MB). See [Memory monitoring](#memory-monitoring). |
312
317
 
313
318
  The engine creates and mounts everything it needs (denoiser canvas, tile/HUD overlay) into a single parent on `init()`. Performance HUDs (e.g. `stats-gl`) are not bundled — listen to `EngineEvents.FRAME` and tick your own panel.
314
319
 
@@ -349,6 +354,41 @@ engine.setSceneObjectVisibility(id, visible) // Toggle visi
349
354
 
350
355
  `id` is the appended root's `Object3D.uuid`, returned by `addModel`/`addModelFromObject3D`. For `addModelFromObject3D` the engine carries your object's uuid onto its copy, so the id matches the object you passed. The built-in ground plane is permanent and can't be removed.
351
356
 
357
+ ##### Loading part of a scene archive
358
+
359
+ A pbrt-v4 scene archive (`.tar`, `.tar.gz`, `.zip`) is usually a root `.pbrt` file that includes one
360
+ subtree per element, and the whole thing rarely fits in a browser tab — Moana is 29 GB unpacked.
361
+ The archive can be inspected without retaining any of it, then loaded one element at a time:
362
+
363
+ ```js
364
+ const { kind, root, elements, entryCount, totalBytes } = await engine.inspectArchive(file);
365
+
366
+ await engine.loadFile(file, { element: elements[0].path }); // one element
367
+ await engine.loadFile(file, { element: [ a.path, b.path ] }); // several together
368
+ ```
369
+
370
+ Everything above a chosen element comes along — the root scene file, the material library, an
371
+ ancestor's `textures` folder — and an `Include` pointing at an element you left out only warns,
372
+ which is what makes a partial load work. Selecting every element is a valid answer and loads the
373
+ whole scene.
374
+
375
+ Past 4 GB unpacked, a multi-element archive **throws** `ARCHIVE_NEEDS_ELEMENT` rather than taking
376
+ all of it. The error carries the element list, so a host can turn it into a picker:
377
+
378
+ ```js
379
+ try {
380
+ await engine.loadFile(file);
381
+ } catch (err) {
382
+ if (err.code === 'ARCHIVE_NEEDS_ELEMENT') showPicker(err.elements, err.root, err.totalBytes);
383
+ else throw err;
384
+ }
385
+ ```
386
+
387
+ Per-load options for pbrt archives: `promptBytes` moves that 4 GB line, `maxTriangles` (default
388
+ 45M) and `maxPlacements` (default 6M) cap the build — past either, placements are skipped and the
389
+ build reports itself truncated. 45M is the highest rung measured to survive; raising it is a
390
+ deliberate act on a fresh browser tab.
391
+
352
392
  #### Settings
353
393
 
354
394
  ```js
@@ -575,6 +615,88 @@ engine.transformManager.setSpace('world') // 'world' | 'local'
575
615
  engine.transformManager.controls // Access the underlying TransformControls
576
616
  ```
577
617
 
618
+ ### Moving and Deforming Objects
619
+
620
+ Three calls update a loaded scene without rebuilding it. They differ in how much work they do, and
621
+ picking the wrong one is the usual source of trouble.
622
+
623
+ ```js
624
+ engine.updateMeshTransforms(meshIndices) // an object moved, rotated or scaled
625
+ engine.refitBLASes(meshIndices, positions) // specific objects' vertices changed
626
+ await engine.refitBVH(positions) // the whole scene is posed anew, e.g. animation
627
+ ```
628
+
629
+ **`updateMeshTransforms` is the one a gizmo drag wants.** Triangles are stored in each object's own
630
+ space, so a rigid move only rewrites a matrix — no vertex pass, no geometry upload. Using
631
+ `refitBLASes` for a move instead rewrites vertices needlessly, and drags along any other object
632
+ sharing the same geometry.
633
+
634
+ All three take **indices into `engine.sceneMeshes`**, which is a depth-first walk of the rendered
635
+ scene and includes the engine's own hidden ground disk. Build your index list from that array, never
636
+ from your own model root, or the two orders silently disagree.
637
+
638
+ Positions are **world space**, 9 floats per triangle (`ax,ay,az, bx,by,bz, cx,cy,cz`), triangles in
639
+ index order. Two shapes are accepted:
640
+
641
+ ```js
642
+ // Preferred: a per-mesh callback, asked for one mesh at a time. You may hand back the same
643
+ // scratch buffer on every call.
644
+ await engine.refitBVH((meshIndex, triCount) => myPositionsFor(meshIndex));
645
+
646
+ // Also works: one array for every triangle in the scene, meshes in sceneMeshes order.
647
+ // 1,030 MB at 30M triangles, and will not allocate at that size — prefer the callback.
648
+ await engine.refitBVH(sceneWideFloat32Array);
649
+ ```
650
+
651
+ Both shapes are length-checked and throw on a mismatch. Before that check existed, a short buffer
652
+ wrote NaN through every bounding box with no error and the scene simply vanished.
653
+
654
+ An object that shares its geometry with another cannot be deformed — writing its vertices would
655
+ move every copy. `refitBLASes` skips such a mesh and records a `refit.shared_geometry` issue.
656
+ Anything skinned or morphed is given triangles of its own at load, so this only fires when the
657
+ wrong mesh was handed over.
658
+
659
+ ---
660
+
661
+ ### Degradation contract
662
+
663
+ The engine degrades rather than fails, which is right for a viewer and backwards for a batch
664
+ renderer, so one option decides which you get:
665
+
666
+ ```js
667
+ const engine = new PathTracerApp(canvas, { strict: true }); // throw at the point of degradation
668
+ ```
669
+
670
+ Lenient hosts read the log instead:
671
+
672
+ ```js
673
+ engine.issues // every recorded issue, newest last
674
+ engine.issueErrors // just the ones a strict host would have thrown on
675
+ engine.addEventListener(EngineEvents.ISSUE, ({ issue }) => report(issue));
676
+ ```
677
+
678
+ Each issue carries `{ code, message, detail, severity, at }`. `ISSUE_CODES` is **add-only API
679
+ surface** — pin a version and branch on the strings; they are never renamed or repurposed.
680
+
681
+ | Code | Raised when |
682
+ |---|---|
683
+ | `adapter.software` | the GPU is a software rasteriser (SwiftShader, llvmpipe, lavapipe, WARP) |
684
+ | `asset.unreachable` / `asset.ambiguous_entry` | the asset could not be fetched, or an archive held several candidate models |
685
+ | `asset.archive_too_large` / `asset.entry_too_large` | an archive or one of its entries exceeded the byte budget |
686
+ | `texture.build_failed` / `texture.processing_fallback` / `texture.limit_exceeded` | a texture could not be built, fell back to a slower path, or exceeded the per-map-type cap |
687
+ | `environment.load_failed` | the environment map failed to load |
688
+ | `setting.unknown_key` | a setting name reached no stage — how a typo becomes a wrong image |
689
+ | `render.size_declined` / `render.reserve_capped` | the requested render size or reserve exceeded device limits |
690
+ | `stage.render_failed` | a pipeline stage threw (recorded once per stage and phase) |
691
+ | `scene.memory_budget` | the scene needs more CPU memory than is safe, or more than is possible |
692
+ | `emissive.instances_collapsed` | an emissive instanced mesh was too large to expand, so its copies light the scene as one |
693
+ | `refit.shared_geometry` | a deform was asked for on a mesh that shares its triangles, and was skipped |
694
+
695
+ `settings.getEffective()` is the companion for the `setting.unknown_key` case: it returns every live
696
+ setting as `{ value, source, routed }`, and `routed: false` means stored but reaching no stage.
697
+
698
+ ---
699
+
578
700
  ### Output Methods
579
701
 
580
702
  Canvas output, screenshots, and scene statistics — accessed as direct methods on the engine.
@@ -649,6 +771,33 @@ engine.vram.getReport(); // formatted one-line summary string
649
771
 
650
772
  The React app surfaces this as a `Memory: … | Peak: …` readout in the on-canvas stats overlay.
651
773
 
774
+ #### CPU memory
775
+
776
+ The wall a large scene hits is not VRAM, it is contiguous `ArrayBuffer` address space on the CPU —
777
+ and how much of it a browser can still hand out falls as the tab stays up, so the same scene can
778
+ load after a restart and fail after a long session.
779
+
780
+ ```js
781
+ const { preflight, allocatedBytes, peakLiveBytes, byPhase, samples } = engine.getHostMemoryInfo();
782
+ // null until a scene has been built
783
+ ```
784
+
785
+ ⚠️ Do not use `performance.memory.usedJSHeapSize` for this. It does not count `SharedArrayBuffer`,
786
+ and the triangle and node stores are SAB-backed, so the browser's own reading under-reports a large
787
+ scene by gigabytes.
788
+
789
+ Before extraction the engine prices the scene and applies two lines, both recording
790
+ `scene.memory_budget`:
791
+
792
+ | Estimate | What happens |
793
+ |---|---|
794
+ | above ~7,040 MB | warns, and builds anyway |
795
+ | above ~9,216 MB | **throws** — past this the renderer process is killed rather than throwing an error you could catch, so refusing early is the only useful answer |
796
+
797
+ Raise or lower the hard line with `new PathTracerApp(canvas, { maxSceneBytes })`. The estimate runs
798
+ low at the very top of its range, so the per-load `maxTriangles` cap (45M) is the more reliable
799
+ guard on a scene of that size.
800
+
652
801
  ---
653
802
 
654
803
  ### Logging
@@ -828,6 +977,21 @@ import {
828
977
  // VRAM accounting (VRAMTracker is also reachable as engine.vram)
829
978
  import { VRAMTracker, bufferBytes, textureBytes } from 'rayzee';
830
979
 
980
+ // Degradation contract — see above. ISSUE_CODES is add-only; pin a version and branch on it.
981
+ import { ISSUE_CODES, ISSUE_SEVERITY, IssueLog, EngineIssueError } from 'rayzee';
982
+
983
+ // CPU memory: price a scene before loading it, or measure what can still be placed
984
+ import {
985
+ MemoryLedger,
986
+ estimateSceneBytes,
987
+ probeAddressSpace,
988
+ SAFE_SCENE_BYTES,
989
+ MAX_SCENE_BYTES,
990
+ } from 'rayzee';
991
+
992
+ // Adapter description — flags software rasterisers (SwiftShader, llvmpipe, lavapipe, WARP)
993
+ import { describeAdapter } from 'rayzee';
994
+
831
995
  // Dev-only: texture-binding aliasing guard. Two TextureNodes still holding the default
832
996
  // EmptyTexture when a kernel is first compiled can share one GPU binding — nothing throws,
833
997
  // the aliased node just reads someone else's texture. Off by default; costs a per-stage