NodeBuilder.js 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509
  1. import NodeUniform from './NodeUniform.js';
  2. import NodeAttribute from './NodeAttribute.js';
  3. import NodeVary from './NodeVary.js';
  4. import NodeVar from './NodeVar.js';
  5. import NodeCode from './NodeCode.js';
  6. import NodeKeywords from './NodeKeywords.js';
  7. import { NodeUpdateType } from './constants.js';
  8. class NodeBuilder {
  9. constructor( material, renderer ) {
  10. this.material = material;
  11. this.renderer = renderer;
  12. this.nodes = [];
  13. this.updateNodes = [];
  14. this.vertexShader = null;
  15. this.fragmentShader = null;
  16. this.slots = { vertex: [], fragment: [] };
  17. this.defines = { vertex: {}, fragment: {} };
  18. this.uniforms = { vertex: [], fragment: [] };
  19. this.nodeCodes = { vertex: [], fragment: [] };
  20. this.vars = { vertex: [], fragment: [] };
  21. this.attributes = [];
  22. this.varys = [];
  23. this.flow = { code: '' };
  24. this.context = {
  25. keywords: new NodeKeywords(),
  26. material: material
  27. };
  28. this.nodesData = new WeakMap();
  29. this.shaderStage = null;
  30. }
  31. addNode( node ) {
  32. if ( this.nodes.indexOf( node ) === - 1 ) {
  33. const updateType = node.getUpdateType( this );
  34. if ( updateType !== NodeUpdateType.None ) {
  35. this.updateNodes.push( node );
  36. }
  37. this.nodes.push( node );
  38. }
  39. }
  40. addSlot( shaderStage, slot ) {
  41. this.slots[ shaderStage ].push( slot );
  42. }
  43. define( shaderStage, name, value = '' ) {
  44. this.defines[ shaderStage ][ name ] = value;
  45. }
  46. setContext( context ) {
  47. this.context = context;
  48. }
  49. getContext() {
  50. return this.context;
  51. }
  52. getContextParameter( name ) {
  53. return this.context[ name ];
  54. }
  55. getTexture( /* textureProperty, uvSnippet */ ) {
  56. console.warn( 'Abstract function.' );
  57. }
  58. getConst( type, value ) {
  59. if ( type === 'float' ) return value + ( value % 1 ? '' : '.0' );
  60. if ( type === 'vec2' ) return `vec2( ${value.x}, ${value.y} )`;
  61. if ( type === 'vec3' ) return `vec3( ${value.x}, ${value.y}, ${value.z} )`;
  62. if ( type === 'vec4' ) return `vec4( ${value.x}, ${value.y}, ${value.z}, ${value.w} )`;
  63. if ( type === 'color' ) return `vec3( ${value.r}, ${value.g}, ${value.b} )`;
  64. throw new Error( `NodeBuilder: Type '${type}' not found in generate constant attempt.` );
  65. }
  66. getAttribute( name, type ) {
  67. const attributes = this.attributes;
  68. // find attribute
  69. for ( const attribute of attributes ) {
  70. if ( attribute.name === name ) {
  71. return attribute;
  72. }
  73. }
  74. // create a new if no exist
  75. const attribute = new NodeAttribute( name, type );
  76. attributes.push( attribute );
  77. return attribute;
  78. }
  79. getPropertyName( node ) {
  80. return node.name;
  81. }
  82. isVector( type ) {
  83. return /vec\d/.test( type );
  84. }
  85. isMatrix( type ) {
  86. return /mat\d/.test( type );
  87. }
  88. isShaderStage( shaderStage ) {
  89. return this.shaderStage === shaderStage;
  90. }
  91. getVectorType( type ) {
  92. if ( type === 'color' ) return 'vec3';
  93. if ( type === 'texture' ) return 'vec4';
  94. return type;
  95. }
  96. getTypeFromLength( type ) {
  97. if ( type === 1 ) return 'float';
  98. if ( type === 2 ) return 'vec2';
  99. if ( type === 3 ) return 'vec3';
  100. if ( type === 4 ) return 'vec4';
  101. return 0;
  102. }
  103. getTypeLength( type ) {
  104. type = this.getVectorType( type );
  105. if ( type === 'float' ) return 1;
  106. if ( type === 'vec2' ) return 2;
  107. if ( type === 'vec3' ) return 3;
  108. if ( type === 'vec4' ) return 4;
  109. return 0;
  110. }
  111. getDataFromNode( node, shaderStage = null ) {
  112. let nodeData = this.nodesData.get( node );
  113. if ( nodeData === undefined ) {
  114. nodeData = { vertex: {}, fragment: {} };
  115. this.nodesData.set( node, nodeData );
  116. }
  117. return shaderStage ? nodeData[ shaderStage ] : nodeData;
  118. }
  119. getUniformFromNode( node, shaderStage, type ) {
  120. const nodeData = this.getDataFromNode( node, shaderStage );
  121. let nodeUniform = nodeData.uniform;
  122. if ( nodeUniform === undefined ) {
  123. const uniforms = this.uniforms[ shaderStage ];
  124. const index = uniforms.length;
  125. nodeUniform = new NodeUniform( 'nodeUniform' + index, type, node );
  126. uniforms.push( nodeUniform );
  127. nodeData.uniform = nodeUniform;
  128. }
  129. return nodeUniform;
  130. }
  131. getVarFromNode( node, type, shaderStage = this.shaderStage ) {
  132. const nodeData = this.getDataFromNode( node, shaderStage );
  133. let nodeVar = nodeData.variable;
  134. if ( nodeVar === undefined ) {
  135. const vars = this.vars[ shaderStage ];
  136. const index = vars.length;
  137. nodeVar = new NodeVar( 'nodeVar' + index, type );
  138. vars.push( nodeVar );
  139. nodeData.variable = nodeVar;
  140. }
  141. return nodeVar;
  142. }
  143. getVaryFromNode( node, type ) {
  144. const nodeData = this.getDataFromNode( node );
  145. let nodeVary = nodeData.vary;
  146. if ( nodeVary === undefined ) {
  147. const varys = this.varys;
  148. const index = varys.length;
  149. nodeVary = new NodeVary( 'nodeVary' + index, type );
  150. varys.push( nodeVary );
  151. nodeData.vary = nodeVary;
  152. }
  153. return nodeVary;
  154. }
  155. getCodeFromNode( node, type, shaderStage = this.shaderStage ) {
  156. const nodeData = this.getDataFromNode( node );
  157. let nodeCode = nodeData.code;
  158. if ( nodeCode === undefined ) {
  159. const nodeCodes = this.nodeCodes[ shaderStage ];
  160. const index = nodeCodes.length;
  161. nodeCode = new NodeCode( 'nodeCode' + index, type );
  162. nodeCodes.push( nodeCode );
  163. nodeData.code = nodeCode;
  164. }
  165. return nodeCode;
  166. }
  167. /*
  168. analyzeNode( node ) {
  169. }
  170. */
  171. addFlowCode( code ) {
  172. if ( ! /;\s*$/.test( code ) ) {
  173. code += '; ';
  174. }
  175. this.flow.code += code;
  176. }
  177. flowNode( node, output ) {
  178. const previousFlow = this.flow;
  179. const flow = {
  180. code: previousFlow.code,
  181. };
  182. this.flow = flow;
  183. flow.result = node.build( this, output );
  184. this.flow = previousFlow;
  185. return flow;
  186. }
  187. _buildDefines( shader ) {
  188. const defines = this.defines[ shader ];
  189. let code = '';
  190. for ( const name in defines ) {
  191. code += `#define ${name} ${defines[ name ]}\n`;
  192. }
  193. return code;
  194. }
  195. getAttributesBodySnippet( /*shaderStage*/ ) {
  196. console.warn( 'Abstract function.' );
  197. }
  198. getAttributesHeaderSnippet( /*shaderStage*/ ) {
  199. console.warn( 'Abstract function.' );
  200. }
  201. getVarysHeaderSnippet( /*shaderStage*/ ) {
  202. console.warn( 'Abstract function.' );
  203. }
  204. getVarysBodySnippet( /*shaderStage*/ ) {
  205. console.warn( 'Abstract function.' );
  206. }
  207. getVarsHeaderSnippet( /*shaderStage*/ ) {
  208. console.warn( 'Abstract function.' );
  209. }
  210. getVarsBodySnippet( /*shaderStage*/ ) {
  211. console.warn( 'Abstract function.' );
  212. }
  213. getUniformsHeaderSnippet( /*shaderStage*/ ) {
  214. console.warn( 'Abstract function.' );
  215. }
  216. getUniformsHeaderCodes( shaderStage ) {
  217. const nodeCodes = this.nodeCodes[ shaderStage ];
  218. let code = '';
  219. for ( const nodeCode of nodeCodes ) {
  220. code += nodeCode.code + '\n';
  221. }
  222. return code;
  223. }
  224. getHash() {
  225. return this.vertexShader + this.fragmentShader;
  226. }
  227. build() {
  228. const shaderStages = [ 'vertex', 'fragment' ];
  229. const shaderData = {};
  230. for ( const shaderStage of shaderStages ) {
  231. this.shaderStage = shaderStage;
  232. const slots = this.slots[ shaderStage ];
  233. for ( const slot of slots ) {
  234. const flowData = this.flowNode( slot.node, slot.output );
  235. this.define( shaderStage, `NODE_CODE_${slot.name}`, flowData.code );
  236. this.define( shaderStage, `NODE_${slot.name}`, flowData.result );
  237. }
  238. }
  239. this.shaderStage = null;
  240. for ( const shaderStage of shaderStages ) {
  241. this.define( shaderStage, 'NODE_HEADER_UNIFORMS', this.getUniformsHeaderSnippet( shaderStage ) );
  242. this.define( shaderStage, 'NODE_HEADER_ATTRIBUTES', this.getAttributesHeaderSnippet( shaderStage ) );
  243. this.define( shaderStage, 'NODE_HEADER_VARYS', this.getVarysHeaderSnippet( shaderStage ) );
  244. this.define( shaderStage, 'NODE_BODY_VARYS', this.getVarysBodySnippet( shaderStage ) );
  245. this.define( shaderStage, 'NODE_BODY_VARS', this.getVarsBodySnippet( shaderStage ) );
  246. let headerCode = '';
  247. headerCode += this.getVarsHeaderSnippet( shaderStage ) + '\n';
  248. headerCode += this.getUniformsHeaderCodes( shaderStage );
  249. shaderData[ shaderStage ] = this._buildDefines( shaderStage ) + '\n' + headerCode;
  250. }
  251. this.vertexShader = shaderData.vertex;
  252. this.fragmentShader = shaderData.fragment;
  253. return this;
  254. }
  255. format( snippet, fromType, toType ) {
  256. fromType = this.getVectorType( fromType );
  257. toType = this.getVectorType( toType );
  258. const typeToType = `${fromType} to ${toType}`;
  259. switch ( typeToType ) {
  260. case 'float to vec2' : return `vec2( ${snippet} )`;
  261. case 'float to vec3' : return `vec3( ${snippet} )`;
  262. case 'float to vec4' : return `vec4( vec3( ${snippet} ), 1.0 )`;
  263. case 'vec2 to float' : return `${snippet}.x`;
  264. case 'vec2 to vec3' : return `vec3( ${snippet}, 0.0 )`;
  265. case 'vec2 to vec4' : return `vec4( ${snippet}.xy, 0.0, 1.0 )`;
  266. case 'vec3 to float' : return `${snippet}.x`;
  267. case 'vec3 to vec2' : return `${snippet}.xy`;
  268. case 'vec3 to vec4' : return `vec4( ${snippet}, 1.0 )`;
  269. case 'vec4 to float' : return `${snippet}.x`;
  270. case 'vec4 to vec2' : return `${snippet}.xy`;
  271. case 'vec4 to vec3' : return `${snippet}.xyz`;
  272. case 'mat3 to float' : return `( ${snippet} * vec3( 1.0 ) ).x`;
  273. case 'mat3 to vec2' : return `( ${snippet} * vec3( 1.0 ) ).xy`;
  274. case 'mat3 to vec3' : return `( ${snippet} * vec3( 1.0 ) ).xyz`;
  275. case 'mat3 to vec4' : return `vec4( ${snippet} * vec3( 1.0 ), 1.0 )`;
  276. case 'mat4 to float' : return `( ${snippet} * vec4( 1.0 ) ).x`;
  277. case 'mat4 to vec2' : return `( ${snippet} * vec4( 1.0 ) ).xy`;
  278. case 'mat4 to vec3' : return `( ${snippet} * vec4( 1.0 ) ).xyz`;
  279. case 'mat4 to vec4' : return `( ${snippet} * vec4( 1.0 ) )`;
  280. }
  281. return snippet;
  282. }
  283. }
  284. export default NodeBuilder;