@gnsx/three 0.184.6 → 0.184.8

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.
@@ -514,29 +514,74 @@ class Inspector extends RendererInspector {
514
514
 
515
515
  }
516
516
 
517
+ // WITH_GENESYS
518
+ const STORAGE_KEY_THREE_INSPECTOR = 'threejs-inspector';
519
+
520
+ function getStorageOrNull() {
521
+
522
+ try {
523
+
524
+ const storage = globalThis.localStorage;
525
+ if ( storage === undefined || storage === null ) return null;
526
+ if ( typeof storage.getItem !== 'function' || typeof storage.setItem !== 'function' ) return null;
527
+ return storage;
528
+
529
+ } catch {
530
+
531
+ return null;
532
+
533
+ }
534
+
535
+ }
536
+
517
537
  function getItem( id ) {
518
538
 
519
- const data = JSON.parse( localStorage.getItem( 'threejs-inspector' ) || '{}' );
520
- return data[ id ] || {};
539
+ const storage = getStorageOrNull();
540
+ if ( storage === null ) return {};
541
+
542
+ try {
543
+
544
+ const raw = storage.getItem( STORAGE_KEY_THREE_INSPECTOR );
545
+ const data = JSON.parse( raw || '{}' );
546
+ return data[ id ] || {};
547
+
548
+ } catch {
549
+
550
+ return {};
551
+
552
+ }
521
553
 
522
554
  }
523
555
 
524
556
  function setItem( id, state ) {
525
557
 
526
- const data = JSON.parse( localStorage.getItem( 'threejs-inspector' ) || '{}' );
558
+ const storage = getStorageOrNull();
559
+ if ( storage === null ) return;
527
560
 
528
- if ( state === null ) {
561
+ try {
529
562
 
530
- delete data[ id ];
563
+ const raw = storage.getItem( STORAGE_KEY_THREE_INSPECTOR );
564
+ const data = JSON.parse( raw || '{}' );
531
565
 
532
- } else {
566
+ if ( state === null ) {
533
567
 
534
- data[ id ] = state;
568
+ delete data[ id ];
535
569
 
536
- }
570
+ } else {
571
+
572
+ data[ id ] = state;
537
573
 
538
- localStorage.setItem( 'threejs-inspector', JSON.stringify( data ) );
574
+ }
575
+
576
+ storage.setItem( STORAGE_KEY_THREE_INSPECTOR, JSON.stringify( data ) );
577
+
578
+ } catch {
579
+
580
+ // Headless / private mode / blocked storage: skip persistence
581
+
582
+ }
539
583
 
540
584
  }
585
+ // !WITH_GENESYS
541
586
 
542
587
  export { Inspector, getItem, setItem };
@@ -232,7 +232,9 @@ class Settings extends Parameters {
232
232
 
233
233
  const extUrl = new URL( extension.url, new URL( _EXTENSIONS_PATH, import.meta.url ) ).href;
234
234
 
235
- const module = await import( extUrl );
235
+ // WITH_GENESYS
236
+ const module = await import( /* webpackIgnore: true */ extUrl );
237
+ // !WITH_GENESYS
236
238
 
237
239
  const keys = Object.keys( module );
238
240
  const ExtensionClass = module[ keys[ 0 ] ];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gnsx/three",
3
- "version": "0.184.6",
3
+ "version": "0.184.8",
4
4
  "description": "JavaScript 3D library",
5
5
  "type": "module",
6
6
  "main": "./build/three.cjs",
@@ -1047,7 +1047,10 @@ class NodeMaterial extends Material {
1047
1047
 
1048
1048
  if ( materialLightsNode.length > 0 ) {
1049
1049
 
1050
- lightsN = builder.renderer.lighting.createNode( [ ...lightsN.getLights(), ...materialLightsNode ] );
1050
+ // WITH_GENESYS
1051
+ lightsN = builder.renderer.lighting.createMaterialNode( lightsN, materialLightsNode );
1052
+ // !WITH_GENESYS
1053
+ // lightsN = builder.renderer.lighting.createNode( [ ...lightsN.getLights(), ...materialLightsNode ] );
1051
1054
 
1052
1055
  }
1053
1056
 
@@ -118,6 +118,9 @@ export { default as LightingContextNode } from './lighting/LightingContextNode.j
118
118
  export { default as LightingNode } from './lighting/LightingNode.js';
119
119
  export { default as LightProbeNode } from './lighting/LightProbeNode.js';
120
120
  export { default as LightsNode } from './lighting/LightsNode.js';
121
+ // WITH_GENESYS
122
+ export { MaterialLightsNode } from './lighting/LightsNode.js';
123
+ // !WITH_GENESYS
121
124
  export { default as PointLightNode } from './lighting/PointLightNode.js';
122
125
  export { default as PointShadowNode } from './lighting/PointShadowNode.js';
123
126
  export { default as ProjectorLightNode } from './lighting/ProjectorLightNode.js';
@@ -76,3 +76,29 @@ export default RendererReferenceNode;
76
76
  * @returns {RendererReferenceNode}
77
77
  */
78
78
  export const rendererReference = ( name, type, renderer = null ) => new RendererReferenceNode( name, type, renderer );
79
+
80
+ // WITH_GENESYS
81
+ /**
82
+ * Clears strong links from a {@link RendererReferenceNode} to a disposed renderer.
83
+ * Module-level nodes (e.g. `toneMappingExposure`) set `.reference` each frame via
84
+ * `updateReference()` and are not cleared by {@link Renderer#dispose} otherwise.
85
+ *
86
+ * @param {RendererReferenceNode} node - The reference node to detach.
87
+ * @param {Renderer} renderer - The renderer being disposed.
88
+ */
89
+ export function detachRendererReference( node, renderer ) {
90
+
91
+ if ( node.reference === renderer ) {
92
+
93
+ node.reference = null;
94
+
95
+ }
96
+
97
+ if ( node.renderer === renderer ) {
98
+
99
+ node.renderer = null;
100
+
101
+ }
102
+
103
+ }
104
+ // !WITH_GENESYS
@@ -418,6 +418,114 @@ class LightsNode extends Node {
418
418
 
419
419
  }
420
420
 
421
+ // WITH_GENESYS
422
+ /**
423
+ * A lights node that composes a live scene lights node with material-local
424
+ * lighting nodes like environment, AO and light map lighting.
425
+ *
426
+ * @augments LightsNode
427
+ */
428
+ class MaterialLightsNode extends LightsNode {
429
+
430
+ static get type() {
431
+
432
+ return 'MaterialLightsNode';
433
+
434
+ }
435
+
436
+ /**
437
+ * Constructs a new material lights node.
438
+ *
439
+ * @param {LightsNode} lightsNode - The live scene lights node.
440
+ * @param {Array<LightingNode>} materialLights - Material-local lighting nodes.
441
+ */
442
+ constructor( lightsNode, materialLights = [] ) {
443
+
444
+ super();
445
+
446
+ this.lightsNode = lightsNode;
447
+ this.materialLights = materialLights;
448
+ this.updateType = lightsNode.getUpdateType();
449
+ this.updateBeforeType = lightsNode.getUpdateBeforeType();
450
+ this.updateAfterType = lightsNode.getUpdateAfterType();
451
+
452
+ }
453
+
454
+ customCacheKey() {
455
+
456
+ const hash = [ this.lightsNode.getCacheKey( true ) ];
457
+
458
+ for ( const lightNode of this.materialLights ) {
459
+
460
+ hash.push( lightNode.getCacheKey( true ) );
461
+
462
+ }
463
+
464
+ return hashArray( hash );
465
+
466
+ }
467
+
468
+ setupLights( builder, materialLights ) {
469
+
470
+ this.lightsNode.setupLights( builder, this.lightsNode.getLightNodes( builder ) );
471
+
472
+ for ( const lightNode of materialLights ) {
473
+
474
+ lightNode.build( builder );
475
+
476
+ }
477
+
478
+ }
479
+
480
+ update( frame ) {
481
+
482
+ if ( this.lightsNode.update ) this.lightsNode.update( frame );
483
+
484
+ }
485
+
486
+ updateBefore( frame ) {
487
+
488
+ if ( this.lightsNode.updateBefore ) this.lightsNode.updateBefore( frame );
489
+
490
+ }
491
+
492
+ updateAfter( frame ) {
493
+
494
+ if ( this.lightsNode.updateAfter ) this.lightsNode.updateAfter( frame );
495
+
496
+ }
497
+
498
+ dispose() {
499
+
500
+ super.dispose();
501
+
502
+ this.lightsNode = null;
503
+ this.materialLights = null;
504
+
505
+ }
506
+
507
+ getLightNodes() {
508
+
509
+ return this.materialLights;
510
+
511
+ }
512
+
513
+ getLights() {
514
+
515
+ return this.lightsNode.getLights();
516
+
517
+ }
518
+
519
+ get hasLights() {
520
+
521
+ return this.lightsNode.getScope().hasLights || this.materialLights.length > 0;
522
+
523
+ }
524
+
525
+ }
526
+ export { MaterialLightsNode };
527
+ // !WITH_GENESYS
528
+
421
529
  export default LightsNode;
422
530
 
423
531
  /**
@@ -1,4 +1,9 @@
1
- import { LightsNode } from '../../nodes/Nodes.js';
1
+ import {
2
+ LightsNode,
3
+ // WITH_GENESYS
4
+ MaterialLightsNode,
5
+ // !WITH_GENESYS
6
+ } from '../../nodes/Nodes.js';
2
7
 
3
8
  const _defaultLights = /*@__PURE__*/ new LightsNode();
4
9
  const _weakMap = /*@__PURE__*/ new WeakMap();
@@ -26,6 +31,22 @@ class Lighting {
26
31
 
27
32
  }
28
33
 
34
+ // WITH_GENESYS
35
+ /**
36
+ * Creates a lights node that composes live scene lighting with material-local
37
+ * lighting nodes such as environment, AO and light map lighting.
38
+ *
39
+ * @param {LightsNode} lightsNode - The live scene lights node.
40
+ * @param {Array<LightingNode>} materialLights - Material-local lighting nodes.
41
+ * @return {MaterialLightsNode} The material lights node.
42
+ */
43
+ createMaterialNode( lightsNode, materialLights = [] ) {
44
+
45
+ return new MaterialLightsNode( lightsNode, materialLights );
46
+
47
+ }
48
+ // !WITH_GENESYS
49
+
29
50
  /**
30
51
  * Returns a lights node for the given scene and camera.
31
52
  *
@@ -72,6 +72,23 @@ class RenderObjects {
72
72
  */
73
73
  this.chainMaps = {};
74
74
 
75
+ // WITH_GENESYS
76
+ /**
77
+ * Strongly tracks every render object created through {@link createRenderObject}
78
+ * so {@link dispose} can iterate them. ChainMap is `WeakMap`-backed and not
79
+ * iterable, so without this set the dispose listeners installed by
80
+ * `RenderObject` on shared materials and geometries (e.g. PMREM, `QuadGeometry`)
81
+ * would leak when the renderer is torn down while those singletons live on.
82
+ *
83
+ * Entries are removed in the same `onDispose` callback that cleans the chain
84
+ * map entry, so steady-state behavior is unchanged.
85
+ *
86
+ * @private
87
+ * @type {Set<RenderObject>}
88
+ */
89
+ this._renderObjects = new Set();
90
+ // !WITH_GENESYS
91
+
75
92
  }
76
93
 
77
94
  /**
@@ -172,6 +189,20 @@ class RenderObjects {
172
189
  */
173
190
  dispose() {
174
191
 
192
+ // WITH_GENESYS
193
+ // Snapshot first: `RenderObject.dispose()` fires `onDispose` which
194
+ // mutates `_renderObjects` (and the chain map). Iterating the live set
195
+ // would skip entries.
196
+ const renderObjects = Array.from( this._renderObjects );
197
+ this._renderObjects.clear();
198
+
199
+ for ( const renderObject of renderObjects ) {
200
+
201
+ renderObject.dispose();
202
+
203
+ }
204
+
205
+ // !WITH_GENESYS
175
206
  this.chainMaps = {};
176
207
 
177
208
  }
@@ -198,6 +229,10 @@ class RenderObjects {
198
229
 
199
230
  const renderObject = new RenderObject( nodes, geometries, renderer, object, material, scene, camera, lightsNode, renderContext, clippingContext );
200
231
 
232
+ // WITH_GENESYS
233
+ this._renderObjects.add( renderObject );
234
+ // !WITH_GENESYS
235
+
201
236
  renderObject.onDispose = () => {
202
237
 
203
238
  this.pipelines.delete( renderObject );
@@ -206,6 +241,10 @@ class RenderObjects {
206
241
 
207
242
  chainMap.delete( renderObject.getChainArray() );
208
243
 
244
+ // WITH_GENESYS
245
+ this._renderObjects.delete( renderObject );
246
+ // !WITH_GENESYS
247
+
209
248
  };
210
249
 
211
250
  return renderObject;
@@ -33,6 +33,10 @@ import { RenderTarget } from '../../core/RenderTarget.js';
33
33
  import { DoubleSide, BackSide, FrontSide, SRGBColorSpace, NoToneMapping, LinearFilter, HalfFloatType, RGBAFormat, PCFShadowMap, VSMShadowMap } from '../../constants.js';
34
34
 
35
35
  import { float, vec3, vec4, Fn } from '../../nodes/tsl/TSLCore.js';
36
+ // WITH_GENESYS
37
+ import { detachRendererReference } from '../../nodes/accessors/RendererReferenceNode.js';
38
+ import { toneMappingExposure } from '../../nodes/display/ToneMappingNode.js';
39
+ // !WITH_GENESYS
36
40
  import { reference } from '../../nodes/accessors/ReferenceNode.js';
37
41
  import { highpModelNormalViewMatrix, highpModelViewMatrix } from '../../nodes/accessors/ModelNode.js';
38
42
  import { context } from '../../nodes/core/ContextNode.js';
@@ -2529,8 +2533,20 @@ class Renderer {
2529
2533
 
2530
2534
  } );
2531
2535
 
2536
+ // WITH_GENESYS
2537
+ // Drop the strong reference to the active `RenderContext` so module-level
2538
+ // `WeakMap<RenderContext, …>` caches can release entries once
2539
+ // `_renderContexts.dispose()` has cleared its dictionary.
2540
+ this._currentRenderContext = null;
2541
+ // !WITH_GENESYS
2542
+
2532
2543
  }
2533
2544
 
2545
+ // WITH_GENESYS
2546
+ // Module-level TSL `toneMappingExposure` retains the last renderer in `.reference`.
2547
+ detachRendererReference( toneMappingExposure, this );
2548
+ // !WITH_GENESYS
2549
+
2534
2550
  this.setRenderTarget( null );
2535
2551
  this.setAnimationLoop( null );
2536
2552
 
@@ -55,6 +55,30 @@ class Textures extends DataMap {
55
55
  */
56
56
  this._htmlTextures = new Set();
57
57
 
58
+ // WITH_GENESYS
59
+ /**
60
+ * Strongly tracks every texture that has had a `'dispose'` listener
61
+ * installed by {@link updateTexture}. `DataMap` is `WeakMap`-backed and
62
+ * not iterable; without this set the listener (whose closure references
63
+ * `Textures` → `renderer`) would leak when {@link dispose} is called
64
+ * while shared textures live on.
65
+ *
66
+ * @private
67
+ * @type {Set<Texture>}
68
+ */
69
+ this._textures = new Set();
70
+
71
+ /**
72
+ * Strongly tracks every render target that has had a `'dispose'` listener
73
+ * installed by {@link updateRenderTarget}. Same rationale as
74
+ * {@link _textures}.
75
+ *
76
+ * @private
77
+ * @type {Set<RenderTarget>}
78
+ */
79
+ this._renderTargets = new Set();
80
+ // !WITH_GENESYS
81
+
58
82
  }
59
83
 
60
84
  /**
@@ -181,6 +205,10 @@ class Textures extends DataMap {
181
205
 
182
206
  renderTarget.addEventListener( 'dispose', renderTargetData.onDispose );
183
207
 
208
+ // WITH_GENESYS
209
+ this._renderTargets.add( renderTarget );
210
+ // !WITH_GENESYS
211
+
184
212
  }
185
213
 
186
214
  }
@@ -401,6 +429,10 @@ class Textures extends DataMap {
401
429
 
402
430
  texture.addEventListener( 'dispose', textureData.onDispose );
403
431
 
432
+ // WITH_GENESYS
433
+ this._textures.add( texture );
434
+ // !WITH_GENESYS
435
+
404
436
  }
405
437
 
406
438
  //
@@ -569,6 +601,10 @@ class Textures extends DataMap {
569
601
  this.delete( renderTarget );
570
602
  this.backend.delete( renderTarget );
571
603
 
604
+ // WITH_GENESYS
605
+ this._renderTargets.delete( renderTarget );
606
+ // !WITH_GENESYS
607
+
572
608
  this.info.memory.renderTargets --;
573
609
 
574
610
  }
@@ -618,12 +654,54 @@ class Textures extends DataMap {
618
654
 
619
655
  this.delete( texture );
620
656
 
657
+ // WITH_GENESYS
658
+ this._textures.delete( texture );
659
+ // !WITH_GENESYS
660
+
621
661
  this.info.destroyTexture( texture );
622
662
 
623
663
  }
624
664
 
625
665
  }
626
666
 
667
+ // WITH_GENESYS
668
+ /**
669
+ * Frees internal resources. Destroys every tracked render target and
670
+ * texture so the `'dispose'` listeners installed by {@link updateTexture}
671
+ * and {@link updateRenderTarget} are removed; without this, listeners on
672
+ * shared textures (PMREM, environment maps, etc.) would retain this
673
+ * `Textures` instance (and its renderer) across renderer teardown.
674
+ *
675
+ * @override
676
+ */
677
+ dispose() {
678
+
679
+ // Snapshot first: `_destroy*` mutates the sets.
680
+ const renderTargets = Array.from( this._renderTargets );
681
+ this._renderTargets.clear();
682
+
683
+ for ( const renderTarget of renderTargets ) {
684
+
685
+ this._destroyRenderTarget( renderTarget );
686
+
687
+ }
688
+
689
+ const textures = Array.from( this._textures );
690
+ this._textures.clear();
691
+
692
+ for ( const texture of textures ) {
693
+
694
+ this._destroyTexture( texture );
695
+
696
+ }
697
+
698
+ this._htmlTextures.clear();
699
+
700
+ super.dispose();
701
+
702
+ }
703
+ // !WITH_GENESYS
704
+
627
705
  }
628
706
 
629
707
  export default Textures;