rayzee 11.16.0 → 11.16.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rayzee",
3
- "version": "11.16.0",
3
+ "version": "11.16.1",
4
4
  "type": "module",
5
5
  "description": "Real-time WebGPU path tracing engine built on Three.js",
6
6
  "main": "dist/rayzee.umd.js",
@@ -3196,7 +3196,7 @@ export class RayzeeRenderer extends EventDispatcher {
3196
3196
  /** The camera rendered from, made once in init(). */
3197
3197
  _createCamera() {
3198
3198
 
3199
- const camera = new ViewCamera( 60, this.canvas.clientWidth / this.canvas.clientHeight || 1, 0.01, 1000 );
3199
+ const camera = new ViewCamera( 60, this.canvas.clientWidth / this.canvas.clientHeight, 0.01, 1000 );
3200
3200
  camera.position.set( 0, 0, 5 );
3201
3201
  return camera;
3202
3202
 
@@ -31,7 +31,8 @@ function cacheKey( from, to, context ) {
31
31
  /** Drop every cached processor and result. Call when the config or context changes. */
32
32
  export function clearColorCaches() {
33
33
 
34
- for ( const p of processorCache.values() ) p.dispose?.();
34
+ // A no-op pair caches null.
35
+ for ( const p of processorCache.values() ) p?.dispose?.();
35
36
  processorCache.clear();
36
37
  colorCache.clear();
37
38
  matrixCache.clear();
@@ -268,7 +268,7 @@ function mountedRoot( rt, text ) {
268
268
 
269
269
  if ( dir[ 0 ] === '.' ) continue;
270
270
  const path = `${base}/${dir}`;
271
- if ( FS.readdir( path ).some( n => n.endsWith( '.ocio' ) && new TextDecoder().decode( FS.readFile( `${path}/${n}` ) ) === text ) ) return path;
271
+ if ( FS.readdir( path ).some( n => /\.ocio$/i.test( n ) && new TextDecoder().decode( FS.readFile( `${path}/${n}` ) ) === text ) ) return path;
272
272
 
273
273
  }
274
274
 
@@ -29,6 +29,8 @@ class WorkerTransport {
29
29
  };
30
30
 
31
31
  this._ready = this.call( 'init', { namespace, root } );
32
+ // Nothing waits on it: a failed init, or a dispose before it answers, fails every later call instead.
33
+ this._ready.catch( () => {} );
32
34
 
33
35
  }
34
36
 
@@ -203,7 +203,8 @@ export class ReinsertionOptimizer {
203
203
 
204
204
  const searchStack = [];
205
205
 
206
- do {
206
+ // Up to and including the root's other child; the root's own parent ends it.
207
+ while ( true ) {
207
208
 
208
209
  // Search sibling subtree at current level
209
210
  searchStack.length = 0;
@@ -273,7 +274,7 @@ export class ReinsertionOptimizer {
273
274
  siblingNode = pivotInfo.isLeft ? pivotParent.rightChild : pivotParent.leftChild;
274
275
  pivotNode = pivotParent;
275
276
 
276
- } while ( parentMap.get( pivotNode ).parent !== null );
277
+ }
277
278
 
278
279
  // Reject trivial reinsertions (same position)
279
280
  if ( bestTo === siblingA || bestTo === parentA ) return null;
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  EventDispatcher, DirectionalLight, PointLight, SpotLight, RectAreaLight,
3
- Object3D, MathUtils
3
+ Object3D, MathUtils, Vector3
4
4
  } from 'three';
5
5
  import { toPortable, fromPortable } from '../Portable.js';
6
6
 
@@ -488,7 +488,8 @@ export class LightManager extends EventDispatcher {
488
488
  ? rawShape
489
489
  : rawShape === 1 ? 'ellipse'
490
490
  : 'rectangle'; // 'rect', undefined, 0 → rectangle
491
- const dir = light.getWorldDirection( light.position.clone() );
491
+ // The panel faces −Z, as lookAt() aims it; getWorldDirection() is +Z.
492
+ const dir = light.getWorldDirection( new Vector3() ).negate();
492
493
  descriptor.target = [ light.position.x + dir.x, light.position.y + dir.y, light.position.z + dir.z ];
493
494
 
494
495
  } else if ( light.type === 'SpotLight' && light.target ) {
@@ -1,5 +1,6 @@
1
1
  import { Vector3, Quaternion } from 'three';
2
2
  import { convertLinearTriple } from '../color/WorkingMatrix.js';
3
+ import { LIGHT_FLOATS } from '../pathtracer/UniformManager.js';
3
4
 
4
5
  // Blackbody temperature → linear Rec.709 (= linear sRGB) RGB.
5
6
  //
@@ -420,10 +421,10 @@ export class LightSerializer {
420
421
  updateShaderUniforms( material ) {
421
422
 
422
423
  // Divide flat array lengths by per-light stride to get actual light counts
423
- const directionalCount = Math.floor( this.lightData.directional.length / 12 );
424
- const areaCount = Math.floor( this.lightData.rectArea.length / 16 );
425
- const pointCount = Math.floor( this.lightData.point.length / 9 );
426
- const spotCount = Math.floor( this.lightData.spot.length / 20 );
424
+ const directionalCount = Math.floor( this.lightData.directional.length / LIGHT_FLOATS.directional );
425
+ const areaCount = Math.floor( this.lightData.rectArea.length / LIGHT_FLOATS.area );
426
+ const pointCount = Math.floor( this.lightData.point.length / LIGHT_FLOATS.point );
427
+ const spotCount = Math.floor( this.lightData.spot.length / LIGHT_FLOATS.spot );
427
428
 
428
429
  // Update light counts in shader defines
429
430
  material.defines.MAX_DIRECTIONAL_LIGHTS = directionalCount;
@@ -81,7 +81,9 @@ export function setMaterialProperty( app, materialIndex, property, value ) {
81
81
  // These rewrite triangle records, which a spilled scene has to read back first.
82
82
  if ( app._sdf?.spilled && TRIANGLE_PATCH_PROPERTIES.has( property ) ) {
83
83
 
84
- app.ensureSceneResident().then( () => app.setMaterialProperty( materialIndex, property, value ) );
84
+ app.ensureSceneResident()
85
+ .then( () => app.setMaterialProperty( materialIndex, property, value ) )
86
+ .catch( error => app._issues.warn( ISSUE_CODES.STORAGE_READ_FAILED, `reading the scene back for a material edit failed: ${error.message}` ) );
85
87
  return;
86
88
 
87
89
  }
@@ -11,7 +11,8 @@ export class ViewCamera extends PerspectiveCamera {
11
11
 
12
12
  constructor( fov, aspect, near, far ) {
13
13
 
14
- super( fov, aspect, near, far );
14
+ // Square until the canvas has an area: a side of 0 made the aspect 0 or Infinity, and the projection NaN.
15
+ super( fov, aspect > 0 && Number.isFinite( aspect ) ? aspect : 1, near, far );
15
16
 
16
17
  this.orthographic = false;
17
18
  /** Half the view's height in world units while orthographic, before `zoom`. */
@@ -40,7 +40,7 @@ export class CameraManager extends EventDispatcher {
40
40
  const width = canvas.clientWidth;
41
41
  const height = canvas.clientHeight;
42
42
 
43
- this.camera = new ViewCamera( 60, width / height || 1, 0.01, 1000 );
43
+ this.camera = new ViewCamera( 60, width / height, 0.01, 1000 );
44
44
  this.camera.position.set( 0, 0, 5 );
45
45
 
46
46
  this.controls = new OrbitControls( this.camera, canvas );
@@ -290,7 +290,7 @@ export class OIDNDenoiser extends EventDispatcher {
290
290
  this.config = { ...MODEL_CONFIG.DEFAULT_OPTIONS, ...options };
291
291
 
292
292
  // Destructure for easier access
293
- this.enabled = this.config.enableOIDN;
293
+ this._enabled = this.config.enableOIDN;
294
294
  this.quality = this.config.oidnQuality;
295
295
  this.maxTileSize = this.config.tileSize;
296
296
  // The size actually baked into the live UNet, so a change forces a rebuild.
@@ -326,27 +326,23 @@ export class OIDNDenoiser extends EventDispatcher {
326
326
  // A weight load requested while another is in flight, to run when that one finishes.
327
327
  this._reloadPending = false;
328
328
 
329
- // Initialize asynchronously
330
- this._initialize().catch( error => {
331
-
332
- log.error( 'init failed:', error );
333
- this.dispatchEvent( { type: 'error', error } );
334
-
335
- } );
329
+ // The weights download once it is on, not before.
330
+ if ( this._enabled ) this._setupUNetDenoiser();
336
331
 
337
332
  }
338
333
 
339
- async _initialize() {
340
-
341
- try {
334
+ get enabled() {
342
335
 
343
- await this._setupUNetDenoiser();
336
+ return this._enabled;
344
337
 
345
- } catch ( error ) {
338
+ }
346
339
 
347
- throw new Error( `Initialization failed: ${error.message}` );
340
+ set enabled( value ) {
348
341
 
349
- }
342
+ const switchedOn = !! value && ! this._enabled;
343
+ this._enabled = !! value;
344
+ // Loads the tier and size chosen while it was off; a no-op when those are already loaded.
345
+ if ( switchedOn ) this._setupUNetDenoiser();
350
346
 
351
347
  }
352
348
 
@@ -419,6 +415,8 @@ export class OIDNDenoiser extends EventDispatcher {
419
415
 
420
416
  _setupUNetDenoiser() {
421
417
 
418
+ if ( ! this._enabled ) return Promise.resolve();
419
+
422
420
  if ( this.state.isLoading ) {
423
421
 
424
422
  // Dropping this would leave `quality` describing weights that were never fetched, and