123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215 |
- import Node from '../core/Node.js';
- import AnalyticLightNode from './AnalyticLightNode.js';
- import { nodeObject, nodeProxy, vec3 } from '../shadernode/ShaderNode.js';
- const LightNodes = new WeakMap();
- const sortLights = ( lights ) => {
- return lights.sort( ( a, b ) => a.id - b.id );
- };
- class LightsNode extends Node {
- constructor( lightNodes = [] ) {
- super( 'vec3' );
- this.totalDiffuseNode = vec3().temp( 'totalDiffuse' );
- this.totalSpecularNode = vec3().temp( 'totalSpecular' );
- this.outgoingLightNode = vec3().temp( 'outgoingLight' );
- this.lightNodes = lightNodes;
- this._hash = null;
- }
- get hasLight() {
- return this.lightNodes.length > 0;
- }
- getHash() {
- if ( this._hash === null ) {
- const hash = [];
- for ( const lightNode of this.lightNodes ) {
- hash.push( lightNode.getHash() );
- }
- this._hash = 'lights-' + hash.join( ',' );
- }
- return this._hash;
- }
- analyze( builder ) {
- const properties = builder.getDataFromNode( this );
- for ( const node of properties.nodes ) {
- node.build( builder );
- }
- }
- setup( builder ) {
- const context = builder.context;
- const lightingModel = context.lightingModel;
- let outgoingLightNode = this.outgoingLightNode;
- if ( lightingModel ) {
- const { lightNodes, totalDiffuseNode, totalSpecularNode } = this;
- context.outgoingLight = outgoingLightNode;
- const stack = builder.addStack();
- //
- const properties = builder.getDataFromNode( this );
- properties.nodes = stack.nodes;
- //
- lightingModel.start( context, stack, builder );
- // lights
- for ( const lightNode of lightNodes ) {
- lightNode.build( builder );
- }
- //
- lightingModel.indirectDiffuse( context, stack, builder );
- lightingModel.indirectSpecular( context, stack, builder );
- lightingModel.ambientOcclusion( context, stack, 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( context, stack, builder );
- //
- outgoingLightNode = outgoingLightNode.bypass( builder.removeStack() );
- }
- return outgoingLightNode;
- }
- _getLightNodeById( id ) {
- for ( const lightNode of this.lightNodes ) {
- if ( lightNode.isAnalyticLightNode && lightNode.light.id === id ) {
- return lightNode;
- }
- }
- return null;
- }
- fromLights( lights = [] ) {
- const lightNodes = [];
- lights = sortLights( lights );
- for ( const light of lights ) {
- let lightNode = this._getLightNodeById( light.id );
- if ( lightNode === null ) {
- const lightClass = light.constructor;
- const lightNodeClass = LightNodes.has( lightClass ) ? LightNodes.get( lightClass ) : AnalyticLightNode;
- lightNode = nodeObject( new lightNodeClass( light ) );
- }
- lightNodes.push( lightNode );
- }
- this.lightNodes = lightNodes;
- this._hash = null;
- return this;
- }
- }
- export default LightsNode;
- export const lights = ( lights ) => nodeObject( new LightsNode().fromLights( lights ) );
- export const lightsNode = nodeProxy( LightsNode );
- export function addLightNode( lightClass, lightNodeClass ) {
- if ( LightNodes.has( lightClass ) ) {
- console.warn( `Redefinition of light node ${ lightNodeClass.type }` );
- return;
- }
- if ( typeof lightClass !== 'function' ) throw new Error( `Light ${ lightClass.name } is not a class` );
- if ( typeof lightNodeClass !== 'function' || ! lightNodeClass.type ) throw new Error( `Light node ${ lightNodeClass.type } is not a class` );
- LightNodes.set( lightClass, lightNodeClass );
- }
|