Buckets:
| import Node from '../core/Node.js'; | |
| import { nodeObject, vec3 } from '../tsl/TSLBase.js'; | |
| import { hashArray } from '../core/NodeUtils.js'; | |
| const sortLights = ( lights ) => { | |
| return lights.sort( ( a, b ) => a.id - b.id ); | |
| }; | |
| const getLightNodeById = ( id, lightNodes ) => { | |
| for ( const lightNode of lightNodes ) { | |
| if ( lightNode.isAnalyticLightNode && lightNode.light.id === id ) { | |
| return lightNode; | |
| } | |
| } | |
| return null; | |
| }; | |
| const _lightsNodeRef = /*@__PURE__*/ new WeakMap(); | |
| /** | |
| * This node represents the scene's lighting and manages the lighting model's life cycle | |
| * for the current build 3D object. It is responsible for computing the total outgoing | |
| * light in a given lighting context. | |
| * | |
| * @augments Node | |
| */ | |
| class LightsNode extends Node { | |
| static get type() { | |
| return 'LightsNode'; | |
| } | |
| /** | |
| * Constructs a new lights node. | |
| */ | |
| constructor() { | |
| super( 'vec3' ); | |
| /** | |
| * A node representing the total diffuse light. | |
| * | |
| * @type {Node<vec3>} | |
| */ | |
| this.totalDiffuseNode = vec3().toVar(); | |
| /** | |
| * A node representing the total specular light. | |
| * | |
| * @type {Node<vec3>} | |
| */ | |
| this.totalSpecularNode = vec3().toVar(); | |
| /** | |
| * A node representing the outgoing light. | |
| * | |
| * @type {Node<vec3>} | |
| */ | |
| this.outgoingLightNode = vec3().toVar(); | |
| /** | |
| * An array representing the lights in the scene. | |
| * | |
| * @private | |
| * @type {Array<Light>} | |
| */ | |
| this._lights = []; | |
| /** | |
| * For each light in the scene, this node will create a | |
| * corresponding light node. | |
| * | |
| * @private | |
| * @type {?Array<LightingNode>} | |
| * @default null | |
| */ | |
| this._lightNodes = null; | |
| /** | |
| * A hash for identifying the current light nodes setup. | |
| * | |
| * @private | |
| * @type {?string} | |
| * @default null | |
| */ | |
| this._lightNodesHash = null; | |
| /** | |
| * `LightsNode` sets this property to `true` by default. | |
| * | |
| * @type {boolean} | |
| * @default true | |
| */ | |
| this.global = true; | |
| } | |
| /** | |
| * Overwrites the default {@link Node#customCacheKey} implementation by including | |
| * light data into the cache key. | |
| * | |
| * @return {number} The custom cache key. | |
| */ | |
| customCacheKey() { | |
| const hashData = []; | |
| const lights = this._lights; | |
| for ( let i = 0; i < lights.length; i ++ ) { | |
| const light = lights[ i ]; | |
| hashData.push( light.id ); | |
| if ( light.isSpotLight === true ) { | |
| const hashValue = ( light.map !== null ) ? light.map.id : - 1; | |
| hashData.push( hashValue ); | |
| } | |
| } | |
| return hashArray( hashData ); | |
| } | |
| /** | |
| * Computes a hash value for identifying the current light nodes setup. | |
| * | |
| * @param {NodeBuilder} builder - A reference to the current node builder. | |
| * @return {string} The computed hash. | |
| */ | |
| getHash( builder ) { | |
| if ( this._lightNodesHash === null ) { | |
| if ( this._lightNodes === null ) this.setupLightsNode( builder ); | |
| const hash = []; | |
| for ( const lightNode of this._lightNodes ) { | |
| hash.push( lightNode.getSelf().getHash() ); | |
| } | |
| this._lightNodesHash = 'lights-' + hash.join( ',' ); | |
| } | |
| return this._lightNodesHash; | |
| } | |
| analyze( builder ) { | |
| const properties = builder.getDataFromNode( this ); | |
| for ( const node of properties.nodes ) { | |
| node.build( builder ); | |
| } | |
| } | |
| /** | |
| * Creates lighting nodes for each scene light. This makes it possible to further | |
| * process lights in the node system. | |
| * | |
| * @param {NodeBuilder} builder - A reference to the current node builder. | |
| */ | |
| setupLightsNode( builder ) { | |
| const lightNodes = []; | |
| const previousLightNodes = this._lightNodes; | |
| const lights = sortLights( this._lights ); | |
| const nodeLibrary = builder.renderer.library; | |
| for ( const light of lights ) { | |
| if ( light.isNode ) { | |
| lightNodes.push( nodeObject( light ) ); | |
| } else { | |
| let lightNode = null; | |
| if ( previousLightNodes !== null ) { | |
| lightNode = getLightNodeById( light.id, previousLightNodes ); // reuse existing light node | |
| } | |
| if ( lightNode === null ) { | |
| // find the corresponding node type for a given light | |
| const lightNodeClass = nodeLibrary.getLightNodeClass( light.constructor ); | |
| if ( lightNodeClass === null ) { | |
| console.warn( `LightsNode.setupNodeLights: Light node not found for ${ light.constructor.name }` ); | |
| continue; | |
| } | |
| let lightNode = null; | |
| if ( ! _lightsNodeRef.has( light ) ) { | |
| lightNode = nodeObject( new lightNodeClass( light ) ); | |
| _lightsNodeRef.set( light, lightNode ); | |
| } else { | |
| lightNode = _lightsNodeRef.get( light ); | |
| } | |
| lightNodes.push( lightNode ); | |
| } | |
| } | |
| } | |
| this._lightNodes = lightNodes; | |
| } | |
| /** | |
| * Sets up a direct light in the lighting model. | |
| * | |
| * @param {Object} builder - The builder object containing the context and stack. | |
| * @param {Object} lightNode - The light node. | |
| * @param {Object} lightData - The light object containing color and direction properties. | |
| */ | |
| setupDirectLight( builder, lightNode, lightData ) { | |
| const { lightingModel, reflectedLight } = builder.context; | |
| lightingModel.direct( { | |
| ...lightData, | |
| lightNode, | |
| reflectedLight | |
| }, builder ); | |
| } | |
| setupDirectRectAreaLight( builder, lightNode, lightData ) { | |
| const { lightingModel, reflectedLight } = builder.context; | |
| lightingModel.directRectArea( { | |
| ...lightData, | |
| lightNode, | |
| reflectedLight | |
| }, builder ); | |
| } | |
| /** | |
| * Setups the internal lights by building all respective | |
| * light nodes. | |
| * | |
| * @param {NodeBuilder} builder - A reference to the current node builder. | |
| * @param {Array<LightingNode>} lightNodes - An array of lighting nodes. | |
| */ | |
| setupLights( builder, lightNodes ) { | |
| for ( const lightNode of lightNodes ) { | |
| lightNode.build( builder ); | |
| } | |
| } | |
| getLightNodes( builder ) { | |
| if ( this._lightNodes === null ) this.setupLightsNode( builder ); | |
| return this._lightNodes; | |
| } | |
| /** | |
| * The implementation makes sure that for each light in the scene | |
| * there is a corresponding light node. By building the light nodes | |
| * and evaluating the lighting model the outgoing light is computed. | |
| * | |
| * @param {NodeBuilder} builder - A reference to the current node builder. | |
| * @return {Node<vec3>} A node representing the outgoing light. | |
| */ | |
| setup( builder ) { | |
| const currentLightsNode = builder.lightsNode; | |
| builder.lightsNode = this; | |
| // | |
| let outgoingLightNode = this.outgoingLightNode; | |
| const context = builder.context; | |
| const lightingModel = context.lightingModel; | |
| const properties = builder.getDataFromNode( this ); | |
| if ( lightingModel ) { | |
| const { totalDiffuseNode, totalSpecularNode } = this; | |
| context.outgoingLight = outgoingLightNode; | |
| const stack = builder.addStack(); | |
| // | |
| properties.nodes = stack.nodes; | |
| // | |
| lightingModel.start( builder ); | |
| // | |
| const { backdrop, backdropAlpha } = context; | |
| const { directDiffuse, directSpecular, indirectDiffuse, indirectSpecular } = context.reflectedLight; | |
| let totalDiffuse = directDiffuse.add( indirectDiffuse ); | |
| if ( backdrop !== null ) { | |
| if ( backdropAlpha !== null ) { | |
| totalDiffuse = vec3( backdropAlpha.mix( totalDiffuse, backdrop ) ); | |
| } else { | |
| totalDiffuse = vec3( backdrop ); | |
| } | |
| context.material.transparent = true; | |
| } | |
| totalDiffuseNode.assign( totalDiffuse ); | |
| totalSpecularNode.assign( directSpecular.add( indirectSpecular ) ); | |
| outgoingLightNode.assign( totalDiffuseNode.add( totalSpecularNode ) ); | |
| // | |
| lightingModel.finish( builder ); | |
| // | |
| outgoingLightNode = outgoingLightNode.bypass( builder.removeStack() ); | |
| } else { | |
| properties.nodes = []; | |
| } | |
| // | |
| builder.lightsNode = currentLightsNode; | |
| return outgoingLightNode; | |
| } | |
| /** | |
| * Configures this node with an array of lights. | |
| * | |
| * @param {Array<Light>} lights - An array of lights. | |
| * @return {LightsNode} A reference to this node. | |
| */ | |
| setLights( lights ) { | |
| this._lights = lights; | |
| this._lightNodes = null; | |
| this._lightNodesHash = null; | |
| return this; | |
| } | |
| /** | |
| * Returns an array of the scene's lights. | |
| * | |
| * @return {Array<Light>} The scene's lights. | |
| */ | |
| getLights() { | |
| return this._lights; | |
| } | |
| /** | |
| * Whether the scene has lights or not. | |
| * | |
| * @type {boolean} | |
| */ | |
| get hasLights() { | |
| return this._lights.length > 0; | |
| } | |
| } | |
| export default LightsNode; | |
| /** | |
| * TSL function for creating an instance of `LightsNode` and configuring | |
| * it with the given array of lights. | |
| * | |
| * @tsl | |
| * @function | |
| * @param {Array<Light>} lights - An array of lights. | |
| * @return {LightsNode} The created lights node. | |
| */ | |
| export const lights = ( lights = [] ) => nodeObject( new LightsNode() ).setLights( lights ); | |
Xet Storage Details
- Size:
- 8.6 kB
- Xet hash:
- 48594134434bb6c9f328e48dab3ae9a1b732f53ea068360b8dedc664a62075f9
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.