LightsNode.js 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  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. setup( builder ) {
  21. const context = builder.context;
  22. const lightingModel = context.lightingModel;
  23. let outgoingLightNode = this.outgoingLightNode;
  24. if ( lightingModel ) {
  25. const { lightNodes, totalDiffuseNode, totalSpecularNode } = this;
  26. context.outgoingLight = outgoingLightNode;
  27. const stack = builder.addStack();
  28. //
  29. lightingModel.start( context, stack, builder );
  30. // lights
  31. for ( const lightNode of lightNodes ) {
  32. lightNode.build( builder );
  33. }
  34. //
  35. lightingModel.indirectDiffuse( context, stack, builder );
  36. lightingModel.indirectSpecular( context, stack, builder );
  37. lightingModel.ambientOcclusion( context, stack, builder );
  38. //
  39. const { backdrop, backdropAlpha } = context;
  40. const { directDiffuse, directSpecular, indirectDiffuse, indirectSpecular } = context.reflectedLight;
  41. let totalDiffuse = directDiffuse.add( indirectDiffuse );
  42. if ( backdrop !== null ) {
  43. totalDiffuse = vec3( backdropAlpha !== null ? backdropAlpha.mix( totalDiffuse, backdrop ) : backdrop );
  44. }
  45. totalDiffuseNode.assign( totalDiffuse );
  46. totalSpecularNode.assign( directSpecular.add( indirectSpecular ) );
  47. outgoingLightNode.assign( totalDiffuseNode.add( totalSpecularNode ) );
  48. //
  49. lightingModel.finish( context, stack, builder );
  50. //
  51. outgoingLightNode = outgoingLightNode.bypass( builder.removeStack() );
  52. }
  53. return outgoingLightNode;
  54. }
  55. getHash( builder ) {
  56. if ( this._hash === null ) {
  57. let hash = '';
  58. const lightNodes = this.lightNodes;
  59. for ( const lightNode of lightNodes ) {
  60. hash += lightNode.getHash( builder ) + ' ';
  61. }
  62. this._hash = hash;
  63. }
  64. return this._hash;
  65. }
  66. getLightNodeByHash( hash ) {
  67. const lightNodes = this.lightNodes;
  68. for ( const lightNode of lightNodes ) {
  69. if ( lightNode.light.uuid === hash ) {
  70. return lightNode;
  71. }
  72. }
  73. return null;
  74. }
  75. fromLights( lights = [] ) {
  76. const lightNodes = [];
  77. lights = sortLights( lights );
  78. for ( const light of lights ) {
  79. let lightNode = this.getLightNodeByHash( light.uuid );
  80. if ( lightNode === null ) {
  81. const lightClass = light.constructor;
  82. const lightNodeClass = LightNodes.has( lightClass ) ? LightNodes.get( lightClass ) : AnalyticLightNode;
  83. lightNode = nodeObject( new lightNodeClass( light ) );
  84. }
  85. lightNodes.push( lightNode );
  86. }
  87. this.lightNodes = lightNodes;
  88. this._hash = null;
  89. return this;
  90. }
  91. }
  92. export default LightsNode;
  93. export const lights = ( lights ) => nodeObject( new LightsNode().fromLights( lights ) );
  94. export const lightsWithoutWrap = nodeProxy( LightsNode );
  95. export function addLightNode( lightClass, lightNodeClass ) {
  96. if ( LightNodes.has( lightClass ) ) throw new Error( `Redefinition of light node ${ lightNodeClass.type }` );
  97. if ( typeof lightClass !== 'function' ) throw new Error( `Light ${ lightClass.name } is not a class` );
  98. if ( typeof lightNodeClass !== 'function' || ! lightNodeClass.type ) throw new Error( `Light node ${ lightNodeClass.type } is not a class` );
  99. LightNodes.set( lightClass, lightNodeClass );
  100. }