ShaderNodeUtils.js 5.3 KB

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