LightsNode.js 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. import Node from '../core/Node.js';
  2. import AnalyticLightNode from './AnalyticLightNode.js';
  3. import { nodeObject, nodeProxy, vec3 } from '../shadernode/ShaderNode.js';
  4. const LightNodes = new WeakMap();
  5. const sortLights = ( lights ) => {
  6. return lights.sort( ( a, b ) => a.id - b.id );
  7. };
  8. class LightsNode extends Node {
  9. constructor( lightNodes = [] ) {
  10. super( 'vec3' );
  11. this.totalDiffuseNode = vec3().temp( 'totalDiffuse' );
  12. this.totalSpecularNode = vec3().temp( 'totalSpecular' );
  13. this.outgoingLightNode = vec3().temp( 'outgoingLight' );
  14. this.lightNodes = lightNodes;
  15. this._hash = null;
  16. }
  17. get hasLight() {
  18. return this.lightNodes.length > 0;
  19. }
  20. getHash() {
  21. if ( this._hash === null ) {
  22. const hash = [];
  23. for ( const lightNode of this.lightNodes ) {
  24. hash.push( lightNode.getHash() );
  25. }
  26. this._hash = 'lights-' + hash.join( ',' );
  27. }
  28. return this._hash;
  29. }
  30. analyze( builder ) {
  31. const properties = builder.getDataFromNode( this );
  32. for ( const node of properties.nodes ) {
  33. node.build( builder );
  34. }
  35. }
  36. setup( builder ) {
  37. const context = builder.context;
  38. const lightingModel = context.lightingModel;
  39. let outgoingLightNode = this.outgoingLightNode;
  40. if ( lightingModel ) {
  41. const { lightNodes, totalDiffuseNode, totalSpecularNode } = this;
  42. context.outgoingLight = outgoingLightNode;
  43. const stack = builder.addStack();
  44. //
  45. const properties = builder.getDataFromNode( this );
  46. properties.nodes = stack.nodes;
  47. //
  48. lightingModel.start( context, stack, builder );
  49. // lights
  50. for ( const lightNode of lightNodes ) {
  51. lightNode.build( builder );
  52. }
  53. //
  54. lightingModel.indirectDiffuse( context, stack, builder );
  55. lightingModel.indirectSpecular( context, stack, builder );
  56. lightingModel.ambientOcclusion( context, stack, builder );
  57. //
  58. const { backdrop, backdropAlpha } = context;
  59. const { directDiffuse, directSpecular, indirectDiffuse, indirectSpecular } = context.reflectedLight;
  60. let totalDiffuse = directDiffuse.add( indirectDiffuse );
  61. if ( backdrop !== null ) {
  62. if ( backdropAlpha !== null ) {
  63. totalDiffuse = vec3( backdropAlpha.mix( totalDiffuse, backdrop ) );
  64. } else {
  65. totalDiffuse = vec3( backdrop );
  66. }
  67. context.material.transparent = true;
  68. }
  69. totalDiffuseNode.assign( totalDiffuse );
  70. totalSpecularNode.assign( directSpecular.add( indirectSpecular ) );
  71. outgoingLightNode.assign( totalDiffuseNode.add( totalSpecularNode ) );
  72. //
  73. lightingModel.finish( context, stack, builder );
  74. //
  75. outgoingLightNode = outgoingLightNode.bypass( builder.removeStack() );
  76. }
  77. return outgoingLightNode;
  78. }
  79. _getLightNodeById( id ) {
  80. for ( const lightNode of this.lightNodes ) {
  81. if ( lightNode.isAnalyticLightNode && lightNode.light.id === id ) {
  82. return lightNode;
  83. }
  84. }
  85. return null;
  86. }
  87. fromLights( lights = [] ) {
  88. const lightNodes = [];
  89. lights = sortLights( lights );
  90. for ( const light of lights ) {
  91. let lightNode = this._getLightNodeById( light.id );
  92. if ( lightNode === null ) {
  93. const lightClass = light.constructor;
  94. const lightNodeClass = LightNodes.has( lightClass ) ? LightNodes.get( lightClass ) : AnalyticLightNode;
  95. lightNode = nodeObject( new lightNodeClass( light ) );
  96. }
  97. lightNodes.push( lightNode );
  98. }
  99. this.lightNodes = lightNodes;
  100. this._hash = null;
  101. return this;
  102. }
  103. }
  104. export default LightsNode;
  105. export const lights = ( lights ) => nodeObject( new LightsNode().fromLights( lights ) );
  106. export const lightsNode = nodeProxy( LightsNode );
  107. export function addLightNode( lightClass, lightNodeClass ) {
  108. if ( LightNodes.has( lightClass ) ) {
  109. console.warn( `Redefinition of light node ${ lightNodeClass.type }` );
  110. return;
  111. }
  112. if ( typeof lightClass !== 'function' ) throw new Error( `Light ${ lightClass.name } is not a class` );
  113. if ( typeof lightNodeClass !== 'function' || ! lightNodeClass.type ) throw new Error( `Light node ${ lightNodeClass.type } is not a class` );
  114. LightNodes.set( lightClass, lightNodeClass );
  115. }