| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300 |
- import { LightsNode, NodeUtils, warn } from 'three/webgpu';
- import { nodeObject } from 'three/tsl';
- import AmbientLightDataNode from './data/AmbientLightDataNode.js';
- import DirectionalLightDataNode from './data/DirectionalLightDataNode.js';
- import PointLightDataNode from './data/PointLightDataNode.js';
- import SpotLightDataNode from './data/SpotLightDataNode.js';
- import HemisphereLightDataNode from './data/HemisphereLightDataNode.js';
- const _lightNodeRef = /*@__PURE__*/ new WeakMap();
- const _hashData = [];
- const _lightTypeToDataNode = {
- AmbientLight: AmbientLightDataNode,
- DirectionalLight: DirectionalLightDataNode,
- PointLight: PointLightDataNode,
- SpotLight: SpotLightDataNode,
- HemisphereLight: HemisphereLightDataNode
- };
- const _lightTypeToMaxProp = {
- DirectionalLight: 'maxDirectionalLights',
- PointLight: 'maxPointLights',
- SpotLight: 'maxSpotLights',
- HemisphereLight: 'maxHemisphereLights'
- };
- const sortLights = ( lights ) => lights.sort( ( a, b ) => a.id - b.id );
- const isSpecialSpotLight = ( light ) => {
- return light.isSpotLight === true && ( light.map !== null || light.colorNode !== undefined );
- };
- const canBatchLight = ( light ) => {
- return light.isNode !== true &&
- light.castShadow !== true &&
- isSpecialSpotLight( light ) === false &&
- _lightTypeToDataNode[ light.constructor.name ] !== undefined;
- };
- const getOrCreateLightNode = ( light, nodeLibrary ) => {
- const lightNodeClass = nodeLibrary.getLightNodeClass( light.constructor );
- if ( lightNodeClass === null ) {
- warn( `DynamicLightsNode: Light node not found for ${ light.constructor.name }.` );
- return null;
- }
- if ( _lightNodeRef.has( light ) === false ) {
- _lightNodeRef.set( light, new lightNodeClass( light ) );
- }
- return _lightNodeRef.get( light );
- };
- /**
- * A custom version of `LightsNode` that batches supported analytic lights into
- * uniform arrays and loops.
- *
- * Unsupported lights, node lights, shadow-casting lights, and projected spot
- * lights keep the default per-light path.
- *
- * @augments LightsNode
- * @three_import import { DynamicLightsNode } from 'three/addons/tsl/lighting/DynamicLightsNode.js';
- */
- class DynamicLightsNode extends LightsNode {
- static get type() {
- return 'DynamicLightsNode';
- }
- /**
- * Constructs a new dynamic lights node.
- *
- * @param {Object} [options={}] - Dynamic lighting configuration.
- * @param {number} [options.maxDirectionalLights=8] - Maximum number of batched directional lights.
- * @param {number} [options.maxPointLights=16] - Maximum number of batched point lights.
- * @param {number} [options.maxSpotLights=16] - Maximum number of batched spot lights.
- * @param {number} [options.maxHemisphereLights=4] - Maximum number of batched hemisphere lights.
- */
- constructor( options = {} ) {
- super();
- this.maxDirectionalLights = options.maxDirectionalLights !== undefined ? options.maxDirectionalLights : 8;
- this.maxPointLights = options.maxPointLights !== undefined ? options.maxPointLights : 16;
- this.maxSpotLights = options.maxSpotLights !== undefined ? options.maxSpotLights : 16;
- this.maxHemisphereLights = options.maxHemisphereLights !== undefined ? options.maxHemisphereLights : 4;
- this._dataNodes = new Map();
- }
- customCacheKey() {
- const typeSet = new Set();
- for ( let i = 0; i < this._lights.length; i ++ ) {
- const light = this._lights[ i ];
- if ( canBatchLight( light ) ) {
- typeSet.add( light.constructor.name );
- } else {
- _hashData.push( light.id );
- _hashData.push( light.castShadow ? 1 : 0 );
- if ( light.isSpotLight === true ) {
- const hashMap = light.map !== null ? light.map.id : - 1;
- const hashColorNode = light.colorNode ? light.colorNode.getCacheKey() : - 1;
- _hashData.push( hashMap, hashColorNode );
- }
- }
- }
- for ( const typeName of this._dataNodes.keys() ) {
- typeSet.add( typeName );
- }
- for ( const typeName of [ ...typeSet ].sort() ) {
- _hashData.push( NodeUtils.hashString( typeName ) );
- }
- const cacheKey = NodeUtils.hashArray( _hashData );
- _hashData.length = 0;
- return cacheKey;
- }
- setupLightsNode( builder ) {
- const lightNodes = [];
- const lightsByType = new Map();
- const lights = sortLights( this._lights );
- const nodeLibrary = builder.renderer.library;
- for ( const light of lights ) {
- if ( light.isNode === true ) {
- lightNodes.push( nodeObject( light ) );
- continue;
- }
- if ( canBatchLight( light ) ) {
- const typeName = light.constructor.name;
- const typeLights = lightsByType.get( typeName );
- if ( typeLights === undefined ) {
- lightsByType.set( typeName, [ light ] );
- } else {
- typeLights.push( light );
- }
- continue;
- }
- const lightNode = getOrCreateLightNode( light, nodeLibrary );
- if ( lightNode !== null ) {
- lightNodes.push( lightNode );
- }
- }
- for ( const [ typeName, typeLights ] of lightsByType ) {
- let dataNode = this._dataNodes.get( typeName );
- if ( dataNode === undefined ) {
- const DataNodeClass = _lightTypeToDataNode[ typeName ];
- const maxProp = _lightTypeToMaxProp[ typeName ];
- const maxCount = maxProp !== undefined ? this[ maxProp ] : undefined;
- dataNode = maxCount !== undefined ? new DataNodeClass( maxCount ) : new DataNodeClass();
- this._dataNodes.set( typeName, dataNode );
- }
- dataNode.setLights( typeLights );
- lightNodes.push( dataNode );
- }
- for ( const [ typeName, dataNode ] of this._dataNodes ) {
- if ( lightsByType.has( typeName ) === false ) {
- dataNode.setLights( [] );
- lightNodes.push( dataNode );
- }
- }
- this._lightNodes = lightNodes;
- }
- setLights( lights ) {
- super.setLights( lights );
- if ( this._dataNodes.size > 0 ) {
- this._updateDataNodeLights( lights );
- }
- return this;
- }
- _updateDataNodeLights( lights ) {
- const lightsByType = new Map();
- for ( const light of lights ) {
- if ( canBatchLight( light ) === false ) continue;
- const typeName = light.constructor.name;
- const typeLights = lightsByType.get( typeName );
- if ( typeLights === undefined ) {
- lightsByType.set( typeName, [ light ] );
- } else {
- typeLights.push( light );
- }
- }
- for ( const [ typeName, dataNode ] of this._dataNodes ) {
- dataNode.setLights( lightsByType.get( typeName ) || [] );
- }
- }
- get hasLights() {
- return super.hasLights || this._dataNodes.size > 0;
- }
- }
- export default DynamicLightsNode;
- /**
- * TSL function that creates a dynamic lights node.
- *
- * @tsl
- * @function
- * @param {Object} [options={}] - Dynamic lighting configuration.
- * @return {DynamicLightsNode} The created dynamic lights node.
- */
- export const dynamicLights = ( options = {} ) => new DynamicLightsNode( options );
|