@@ -55,6 +55,16 @@ const refreshUniforms = [
5555 'transmissionMap'
5656] ;
5757
58+
59+ /**
60+ * A WeakMap to cache lights data for node materials.
61+ * Cache lights data by render ID to avoid unnecessary recalculations.
62+ *
63+ * @private
64+ * @type {WeakMap<LightsNode,Object> }
65+ */
66+ const _lightsCache = new WeakMap ( ) ;
67+
5868/**
5969 * This class is used by {@link WebGPURenderer} as management component.
6070 * It's primary purpose is to determine whether render objects require a
@@ -196,6 +206,8 @@ class NodeMaterialObserver {
196206
197207 }
198208
209+ data . lights = this . getLightsData ( renderObject . lightsNode . getLights ( ) ) ;
210+
199211 this . renderObjects . set ( renderObject , data ) ;
200212
201213 }
@@ -299,9 +311,10 @@ class NodeMaterialObserver {
299311 * Returns `true` if the given render object has not changed its state.
300312 *
301313 * @param {RenderObject } renderObject - The render object.
314+ * @param {Array<Light> } lightsData - The current material lights.
302315 * @return {boolean } Whether the given render object has changed its state or not.
303316 */
304- equals ( renderObject ) {
317+ equals ( renderObject , lightsData ) {
305318
306319 const { object, material, geometry } = renderObject ;
307320
@@ -462,6 +475,22 @@ class NodeMaterialObserver {
462475
463476 }
464477
478+ // lights
479+
480+ if ( renderObjectData . lights ) {
481+
482+ for ( let i = 0 ; i < lightsData . length ; i ++ ) {
483+
484+ if ( renderObjectData . lights [ i ] . map !== lightsData [ i ] . map ) {
485+
486+ return false ;
487+
488+ }
489+
490+ }
491+
492+ }
493+
465494 // center
466495
467496 if ( renderObjectData . center ) {
@@ -488,6 +517,61 @@ class NodeMaterialObserver {
488517
489518 }
490519
520+ /**
521+ * Returns the lights data for the given material lights.
522+ *
523+ * @param {Array<Light> } materialLights - The material lights.
524+ * @return {Array<Object> } The lights data for the given material lights.
525+ */
526+ getLightsData ( materialLights ) {
527+
528+ const lights = [ ] ;
529+
530+ for ( const light of materialLights ) {
531+
532+ if ( light . isSpotLight === true && light . map !== null ) {
533+
534+ // only add lights that have a map
535+
536+ lights . push ( { map : light . map . version } ) ;
537+
538+ }
539+
540+ }
541+
542+ return lights ;
543+
544+ }
545+
546+ /**
547+ * Returns the lights for the given lights node and render ID.
548+ *
549+ * @param {LightsNode } lightsNode - The lights node.
550+ * @param {number } renderId - The render ID.
551+ * @return {Array } The lights for the given lights node and render ID.
552+ */
553+ getLights ( lightsNode , renderId ) {
554+
555+ if ( _lightsCache . has ( lightsNode ) ) {
556+
557+ const cached = _lightsCache . get ( lightsNode ) ;
558+
559+ if ( cached . renderId === renderId ) {
560+
561+ return cached . lightsData ;
562+
563+ }
564+
565+ }
566+
567+ const lightsData = this . getLightsData ( lightsNode . getLights ( ) ) ;
568+
569+ _lightsCache . set ( lightsNode , { renderId, lightsData } ) ;
570+
571+ return lightsData ;
572+
573+ }
574+
491575 /**
492576 * Checks if the given render object requires a refresh.
493577 *
@@ -516,7 +600,8 @@ class NodeMaterialObserver {
516600 if ( isStatic || isBundle )
517601 return false ;
518602
519- const notEqual = this . equals ( renderObject ) !== true ;
603+ const lightsData = this . getLights ( renderObject . lightsNode , renderId ) ;
604+ const notEqual = this . equals ( renderObject , lightsData ) !== true ;
520605
521606 return notEqual ;
522607
0 commit comments