WGSLNodeFunction.js 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. import NodeFunction from '../../../nodes/core/NodeFunction.js';
  2. import NodeFunctionInput from '../../../nodes/core/NodeFunctionInput.js';
  3. const declarationRegexp = /^[fn]*\s*([a-z_0-9]+)?\s*\(([\s\S]*?)\)\s*[\-\>]*\s*([a-z_0-9]+(?:<[\s\S]+?>)?)/i;
  4. const propertiesRegexp = /([a-z_0-9]+)\s*:\s*([a-z_0-9]+(?:<[\s\S]+?>)?)/ig;
  5. const wgslTypeLib = {
  6. 'f32': 'float',
  7. 'i32': 'int',
  8. 'u32': 'uint',
  9. 'bool': 'bool',
  10. 'vec2<f32>': 'vec2',
  11. 'vec2<i32>': 'ivec2',
  12. 'vec2<u32>': 'uvec2',
  13. 'vec2<bool>': 'bvec2',
  14. 'vec2f': 'vec2',
  15. 'vec2i': 'ivec2',
  16. 'vec2u': 'uvec2',
  17. 'vec2b': 'bvec2',
  18. 'vec3<f32>': 'vec3',
  19. 'vec3<i32>': 'ivec3',
  20. 'vec3<u32>': 'uvec3',
  21. 'vec3<bool>': 'bvec3',
  22. 'vec3f': 'vec3',
  23. 'vec3i': 'ivec3',
  24. 'vec3u': 'uvec3',
  25. 'vec3b': 'bvec3',
  26. 'vec4<f32>': 'vec4',
  27. 'vec4<i32>': 'ivec4',
  28. 'vec4<u32>': 'uvec4',
  29. 'vec4<bool>': 'bvec4',
  30. 'vec4f': 'vec4',
  31. 'vec4i': 'ivec4',
  32. 'vec4u': 'uvec4',
  33. 'vec4b': 'bvec4',
  34. 'mat2x2<f32>': 'mat2',
  35. 'mat2x2f': 'mat2',
  36. 'mat3x3<f32>': 'mat3',
  37. 'mat3x3f': 'mat3',
  38. 'mat4x4<f32>': 'mat4',
  39. 'mat4x4f': 'mat4',
  40. 'sampler': 'sampler',
  41. 'texture_2d': 'texture',
  42. 'texture_cube': 'cubeTexture',
  43. 'texture_depth_2d': 'depthTexture',
  44. 'texture_storage_2d': 'storageTexture',
  45. 'texture_3d': 'texture3D'
  46. };
  47. const parse = ( source ) => {
  48. source = source.trim();
  49. const declaration = source.match( declarationRegexp );
  50. if ( declaration !== null && declaration.length === 4 ) {
  51. const inputsCode = declaration[ 2 ];
  52. const propsMatches = [];
  53. let match = null;
  54. while ( ( match = propertiesRegexp.exec( inputsCode ) ) !== null ) {
  55. propsMatches.push( { name: match[ 1 ], type: match[ 2 ] } );
  56. }
  57. // Process matches to correctly pair names and types
  58. const inputs = [];
  59. for ( let i = 0; i < propsMatches.length; i ++ ) {
  60. const { name, type } = propsMatches[ i ];
  61. let resolvedType = type;
  62. if ( resolvedType.startsWith( 'texture' ) ) {
  63. resolvedType = type.split( '<' )[ 0 ];
  64. }
  65. resolvedType = wgslTypeLib[ resolvedType ] || resolvedType;
  66. inputs.push( new NodeFunctionInput( resolvedType, name ) );
  67. }
  68. const blockCode = source.substring( declaration[ 0 ].length );
  69. const outputType = declaration[ 3 ] || 'void';
  70. const name = declaration[ 1 ] !== undefined ? declaration[ 1 ] : '';
  71. const type = wgslTypeLib[ outputType ] || outputType;
  72. return {
  73. type,
  74. inputs,
  75. name,
  76. inputsCode,
  77. blockCode,
  78. outputType
  79. };
  80. } else {
  81. throw new Error( 'FunctionNode: Function is not a WGSL code.' );
  82. }
  83. };
  84. class WGSLNodeFunction extends NodeFunction {
  85. constructor( source ) {
  86. const { type, inputs, name, inputsCode, blockCode, outputType } = parse( source );
  87. super( type, inputs, name );
  88. this.inputsCode = inputsCode;
  89. this.blockCode = blockCode;
  90. this.outputType = outputType;
  91. }
  92. getCode( name = this.name ) {
  93. const outputType = this.outputType !== 'void' ? '-> ' + this.outputType : '';
  94. return `fn ${ name } ( ${ this.inputsCode.trim() } ) ${ outputType }` + this.blockCode;
  95. }
  96. }
  97. export default WGSLNodeFunction;