ShaderNode.js 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. import Node from '../core/Node.js';
  2. import ArrayElementNode from '../utils/ArrayElementNode.js';
  3. import ConvertNode from '../utils/ConvertNode.js';
  4. import JoinNode from '../utils/JoinNode.js';
  5. import SplitNode from '../utils/SplitNode.js';
  6. import ConstNode from '../core/ConstNode.js';
  7. import StackNode from '../core/StackNode.js';
  8. import { getValueFromType } from '../core/NodeUtils.js';
  9. import * as NodeElements from './ShaderNodeElements.js';
  10. const shaderNodeHandler = {
  11. construct( NodeClosure, params ) {
  12. const inputs = params.shift();
  13. return NodeClosure( nodeObjects( inputs ), ...params );
  14. },
  15. get: function ( node, prop, nodeObj ) {
  16. if ( typeof prop === 'string' && node[ prop ] === undefined ) {
  17. if ( /^[xyzwrgbastpq]{1,4}$/.test( prop ) === true ) {
  18. // accessing properties ( swizzle )
  19. prop = prop
  20. .replace( /r|s/g, 'x' )
  21. .replace( /g|t/g, 'y' )
  22. .replace( /b|p/g, 'z' )
  23. .replace( /a|q/g, 'w' );
  24. return nodeObject( new SplitNode( node, prop ) );
  25. } else if ( /^\d+$/.test( prop ) === true ) {
  26. // accessing array
  27. return nodeObject( new ArrayElementNode( node, new ConstNode( Number( prop ), 'uint' ) ) );
  28. } else if ( NodeElements[ prop ] ) {
  29. const nodeElement = NodeElements[ prop ];
  30. return ( ...params ) => nodeElement( nodeObj, ...params );
  31. }
  32. }
  33. return node[ prop ];
  34. }
  35. };
  36. const nodeObjectsCacheMap = new WeakMap();
  37. const ShaderNodeObject = function ( obj ) {
  38. const type = typeof obj;
  39. if ( ( type === 'number' ) || ( type === 'boolean' ) ) {
  40. return nodeObject( getAutoTypedConstNode( obj ) );
  41. } else if ( type === 'object' ) {
  42. if ( obj && obj.isNode === true ) {
  43. let nodeObject = nodeObjectsCacheMap.get( obj );
  44. if ( nodeObject === undefined ) {
  45. nodeObject = new Proxy( obj, shaderNodeHandler );
  46. nodeObjectsCacheMap.set( obj, nodeObject );
  47. nodeObjectsCacheMap.set( nodeObject, nodeObject );
  48. }
  49. return nodeObject;
  50. }
  51. }
  52. return obj;
  53. };
  54. const ShaderNodeObjects = function ( objects ) {
  55. for ( const name in objects ) {
  56. objects[ name ] = nodeObject( objects[ name ] );
  57. }
  58. return objects;
  59. };
  60. const ShaderNodeArray = function ( array ) {
  61. const len = array.length;
  62. for ( let i = 0; i < len; i ++ ) {
  63. array[ i ] = nodeObject( array[ i ] );
  64. }
  65. return array;
  66. };
  67. const ShaderNodeProxy = function ( NodeClass, scope = null, factor = null, settings = null ) {
  68. const assignNode = ( node ) => nodeObject( settings !== null ? Object.assign( node, settings ) : node );
  69. if ( scope === null ) {
  70. return ( ...params ) => {
  71. return assignNode( new NodeClass( ...nodeArray( params ) ) );
  72. };
  73. } else if ( factor !== null ) {
  74. factor = nodeObject( factor );
  75. return ( ...params ) => {
  76. return assignNode( new NodeClass( scope, ...nodeArray( params ), factor ) );
  77. };
  78. } else {
  79. return ( ...params ) => {
  80. return assignNode( new NodeClass( scope, ...nodeArray( params ) ) );
  81. };
  82. }
  83. };
  84. const ShaderNodeImmutable = function ( NodeClass, ...params ) {
  85. return nodeObject( new NodeClass( ...nodeArray( params ) ) );
  86. };
  87. class ShaderNodeInternal extends Node {
  88. constructor( jsFunc ) {
  89. super();
  90. this._jsFunc = jsFunc;
  91. }
  92. call( inputs, builder ) {
  93. inputs = nodeObjects( inputs );
  94. return nodeObject( this._jsFunc( inputs, builder ) );
  95. }
  96. getNodeType( builder ) {
  97. const { outputNode } = builder.getNodeProperties( this );
  98. return outputNode ? outputNode.getNodeType( builder ) : super.getNodeType( builder );
  99. }
  100. construct( builder ) {
  101. const stackNode = new StackNode();
  102. stackNode.outputNode = this.call( {}, stackNode, builder );
  103. return stackNode;
  104. }
  105. }
  106. const ShaderNodeScript = function ( jsFunc ) {
  107. return new ShaderNodeInternal( jsFunc );
  108. };
  109. export const ShaderNode = new Proxy( ShaderNodeScript, shaderNodeHandler );
  110. export const nodeObject = ( val ) => /* new */ ShaderNodeObject( val );
  111. export const nodeObjects = ( val ) => new ShaderNodeObjects( val );
  112. export const nodeArray = ( val ) => new ShaderNodeArray( val );
  113. export const nodeProxy = ( ...val ) => new ShaderNodeProxy( ...val );
  114. export const nodeImmutable = ( ...val ) => new ShaderNodeImmutable( ...val );
  115. const bools = [ false, true ];
  116. const uints = [ 0, 1, 2, 3 ];
  117. const ints = [ - 1, - 2 ];
  118. const floats = [ 0.5, 1.5, 1 / 3, 1e-6, 1e6, Math.PI, Math.PI * 2, 1 / Math.PI, 2 / Math.PI, 1 / ( Math.PI * 2 ), Math.PI / 2 ];
  119. const boolsCacheMap = new Map();
  120. for ( const bool of bools ) boolsCacheMap.set( bool, new ConstNode( bool ) );
  121. const uintsCacheMap = new Map();
  122. for ( const uint of uints ) uintsCacheMap.set( uint, new ConstNode( uint, 'uint' ) );
  123. const intsCacheMap = new Map( [ ...uintsCacheMap ].map( el => new ConstNode( el.value, 'int' ) ) );
  124. for ( const int of ints ) intsCacheMap.set( int, new ConstNode( int, 'int' ) );
  125. const floatsCacheMap = new Map( [ ...intsCacheMap ].map( el => new ConstNode( el.value ) ) );
  126. for ( const float of floats ) floatsCacheMap.set( float, new ConstNode( float ) );
  127. for ( const float of floats ) floatsCacheMap.set( - float, new ConstNode( - float ) );
  128. export const cacheMaps = { bool: boolsCacheMap, uint: uintsCacheMap, ints: intsCacheMap, float: floatsCacheMap };
  129. const constNodesCacheMap = new Map( [ ...boolsCacheMap, ...floatsCacheMap ] );
  130. const getAutoTypedConstNode = ( value ) => {
  131. if ( constNodesCacheMap.has( value ) ) {
  132. return constNodesCacheMap.get( value );
  133. } else if ( value.isNode === true ) {
  134. return value;
  135. } else {
  136. return new ConstNode( value );
  137. }
  138. };
  139. export const ConvertType = function ( type, cacheMap = null ) {
  140. return ( ...params ) => {
  141. if ( params.length === 0 ) {
  142. return nodeObject( new ConstNode( getValueFromType( type ), type ) );
  143. } else {
  144. if ( type === 'color' && params[ 0 ].isNode !== true ) {
  145. params = [ getValueFromType( type, ...params ) ];
  146. }
  147. if ( params.length === 1 && cacheMap !== null && cacheMap.has( params[ 0 ] ) ) {
  148. return cacheMap.get( params[ 0 ] );
  149. }
  150. const nodes = params.map( getAutoTypedConstNode );
  151. if ( nodes.length === 1 ) {
  152. return nodeObject( nodes[ 0 ].nodeType === type ? nodes[ 0 ] : new ConvertNode( nodes[ 0 ], type ) );
  153. }
  154. return nodeObject( new JoinNode( nodes, type ) );
  155. }
  156. };
  157. };
  158. export const getConstNodeType = ( value ) => value.nodeType || value.convertTo || ( typeof value === 'string' ? value : null );