nodeMaterialBuildState.ts 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394
  1. import { NodeMaterialConnectionPoint } from './nodeMaterialBlockConnectionPoint';
  2. import { NodeMaterialBlockConnectionPointTypes } from './nodeMaterialBlockConnectionPointTypes';
  3. import { NodeMaterialWellKnownValues } from './nodeMaterialWellKnownValues';
  4. import { NodeMaterialBlockTargets } from './nodeMaterialBlockTargets';
  5. import { NodeMaterialBuildStateSharedData } from './nodeMaterialBuildStateSharedData';
  6. import { Effect } from '../effect';
  7. /**
  8. * Class used to store node based material build state
  9. */
  10. export class NodeMaterialBuildState {
  11. /**
  12. * Gets the list of emitted attributes
  13. */
  14. public attributes = new Array<string>();
  15. /**
  16. * Gets the list of emitted uniforms
  17. */
  18. public uniforms = new Array<string>();
  19. /**
  20. * Gets the list of emitted samplers
  21. */
  22. public samplers = new Array<string>();
  23. /**
  24. * Gets the list of emitted functions
  25. */
  26. public functions: { [key: string]: string } = {};
  27. /**
  28. * Gets the target of the compilation state
  29. */
  30. public target: NodeMaterialBlockTargets;
  31. /**
  32. * Shared data between multiple NodeMaterialBuildState instances
  33. */
  34. public sharedData: NodeMaterialBuildStateSharedData;
  35. /** @hidden */
  36. public _vertexState: NodeMaterialBuildState;
  37. private _attributeDeclaration = "";
  38. private _uniformDeclaration = "";
  39. private _samplerDeclaration = "";
  40. private _varyingTransfer = "";
  41. private _repeatableContentAnchorIndex = 0;
  42. /** @hidden */
  43. public _builtCompilationString = "";
  44. /**
  45. * Gets the emitted compilation strings
  46. */
  47. public compilationString = "";
  48. /**
  49. * Finalize the compilation strings
  50. * @param state defines the current compilation state
  51. */
  52. public finalize(state: NodeMaterialBuildState) {
  53. let emitComments = state.sharedData.emitComments;
  54. let isFragmentMode = (this.target === NodeMaterialBlockTargets.Fragment);
  55. this.compilationString = `\r\n${emitComments ? "//Entry point\r\n" : ""}void main(void) {\r\n${this.compilationString}`;
  56. let functionCode = "";
  57. for (var functionName in this.functions) {
  58. functionCode += this.functions[functionName] + `\r\n`;
  59. }
  60. this.compilationString = `\r\n${functionCode}\r\n${this.compilationString}`;
  61. if (!isFragmentMode && this._varyingTransfer) {
  62. this.compilationString = `${this.compilationString}\r\n${this._varyingTransfer}`;
  63. }
  64. this.compilationString = `${this.compilationString}\r\n}`;
  65. if (this.sharedData.varyingDeclaration) {
  66. this.compilationString = `\r\n${emitComments ? "//Varyings\r\n" : ""}${this.sharedData.varyingDeclaration}\r\n${this.compilationString}`;
  67. }
  68. if (this._samplerDeclaration) {
  69. this.compilationString = `\r\n${emitComments ? "//Samplers\r\n" : ""}${this._samplerDeclaration}\r\n${this.compilationString}`;
  70. }
  71. if (this._uniformDeclaration) {
  72. this.compilationString = `\r\n${emitComments ? "//Uniforms\r\n" : ""}${this._uniformDeclaration}\r\n${this.compilationString}`;
  73. }
  74. if (this._attributeDeclaration && !isFragmentMode) {
  75. this.compilationString = `\r\n${emitComments ? "//Attributes\r\n" : ""}${this._attributeDeclaration}\r\n${this.compilationString}`;
  76. }
  77. this._builtCompilationString = this.compilationString;
  78. }
  79. /** @hidden */
  80. public get _repeatableContentAnchor(): string {
  81. return `###___ANCHOR${this._repeatableContentAnchorIndex++}___###`;
  82. }
  83. /** @hidden */
  84. public _getFreeVariableName(prefix: string): string {
  85. if (this.sharedData.variableNames[prefix] === undefined) {
  86. this.sharedData.variableNames[prefix] = 0;
  87. // Check reserved words
  88. if (prefix === "output" || prefix === "texture") {
  89. return prefix + this.sharedData.variableNames[prefix];
  90. }
  91. return prefix;
  92. } else {
  93. this.sharedData.variableNames[prefix]++;
  94. }
  95. return prefix + this.sharedData.variableNames[prefix];
  96. }
  97. /** @hidden */
  98. public _getFreeDefineName(prefix: string): string {
  99. if (this.sharedData.defineNames[prefix] === undefined) {
  100. this.sharedData.defineNames[prefix] = 0;
  101. } else {
  102. this.sharedData.defineNames[prefix]++;
  103. }
  104. return prefix + this.sharedData.defineNames[prefix];
  105. }
  106. /** @hidden */
  107. public _excludeVariableName(name: string) {
  108. this.sharedData.variableNames[name] = 0;
  109. }
  110. /** @hidden */
  111. public _getGLType(type: NodeMaterialBlockConnectionPointTypes): string {
  112. switch (type) {
  113. case NodeMaterialBlockConnectionPointTypes.Float:
  114. return "float";
  115. case NodeMaterialBlockConnectionPointTypes.Int:
  116. return "int";
  117. case NodeMaterialBlockConnectionPointTypes.Vector2:
  118. return "vec2";
  119. case NodeMaterialBlockConnectionPointTypes.Color3:
  120. case NodeMaterialBlockConnectionPointTypes.Vector3:
  121. case NodeMaterialBlockConnectionPointTypes.Vector3OrColor3:
  122. return "vec3";
  123. case NodeMaterialBlockConnectionPointTypes.Color4:
  124. case NodeMaterialBlockConnectionPointTypes.Vector4:
  125. case NodeMaterialBlockConnectionPointTypes.Vector4OrColor4:
  126. case NodeMaterialBlockConnectionPointTypes.Vector3OrVector4:
  127. case NodeMaterialBlockConnectionPointTypes.Color3OrColor4:
  128. return "vec4";
  129. case NodeMaterialBlockConnectionPointTypes.Matrix:
  130. return "mat4";
  131. case NodeMaterialBlockConnectionPointTypes.Texture:
  132. return "sampler2D";
  133. case NodeMaterialBlockConnectionPointTypes.Texture3D:
  134. return "sampler3D";
  135. }
  136. return "";
  137. }
  138. /** @hidden */
  139. public _emitFunction(name: string, code: string, comments: string) {
  140. if (this.functions[name]) {
  141. return;
  142. }
  143. if (this.sharedData.emitComments) {
  144. code = comments + `\r\n` + code;
  145. }
  146. this.functions[name] = code;
  147. }
  148. /** @hidden */
  149. public _emitCodeFromInclude(includeName: string, comments: string, options?: {
  150. replaceStrings?: { search: RegExp, replace: string }[],
  151. }) {
  152. let code = Effect.IncludesShadersStore[includeName] + "\r\n";
  153. if (this.sharedData.emitComments) {
  154. code = comments + `\r\n` + code;
  155. }
  156. if (!options) {
  157. return code;
  158. }
  159. if (options.replaceStrings) {
  160. for (var index = 0; index < options.replaceStrings.length; index++) {
  161. let replaceString = options.replaceStrings[index];
  162. code = code.replace(replaceString.search, replaceString.replace);
  163. }
  164. }
  165. return code;
  166. }
  167. /** @hidden */
  168. public _emitFunctionFromInclude(includeName: string, comments: string, options?: {
  169. repeatKey?: string,
  170. removeAttributes?: boolean,
  171. removeUniforms?: boolean,
  172. removeVaryings?: boolean,
  173. removeIfDef?: boolean,
  174. replaceStrings?: { search: RegExp, replace: string }[],
  175. }) {
  176. if (this.functions[includeName]) {
  177. return;
  178. }
  179. if (!options || (!options.removeAttributes && !options.removeUniforms && !options.removeVaryings && !options.removeIfDef && !options.replaceStrings)) {
  180. if (options && options.repeatKey) {
  181. this.functions[includeName] = `#include<${includeName}>[0..${options.repeatKey}]\r\n`;
  182. } else {
  183. this.functions[includeName] = `#include<${includeName}>\r\n`;
  184. }
  185. if (this.sharedData.emitComments) {
  186. this.functions[includeName] = comments + `\r\n` + this.functions[includeName];
  187. }
  188. return;
  189. }
  190. this.functions[includeName] = Effect.IncludesShadersStore[includeName];
  191. if (this.sharedData.emitComments) {
  192. this.functions[includeName] = comments + `\r\n` + this.functions[includeName];
  193. }
  194. if (options.removeIfDef) {
  195. this.functions[includeName] = this.functions[includeName].replace(/^\s*?#ifdef.+$/gm, "");
  196. this.functions[includeName] = this.functions[includeName].replace(/^\s*?#endif.*$/gm, "");
  197. this.functions[includeName] = this.functions[includeName].replace(/^\s*?#else.*$/gm, "");
  198. this.functions[includeName] = this.functions[includeName].replace(/^\s*?#elif.*$/gm, "");
  199. }
  200. if (options.removeAttributes) {
  201. this.functions[includeName] = this.functions[includeName].replace(/^\s*?attribute.+$/gm, "");
  202. }
  203. if (options.removeUniforms) {
  204. this.functions[includeName] = this.functions[includeName].replace(/^\s*?uniform.+$/gm, "");
  205. }
  206. if (options.removeVaryings) {
  207. this.functions[includeName] = this.functions[includeName].replace(/^\s*?varying.+$/gm, "");
  208. }
  209. if (options.replaceStrings) {
  210. for (var index = 0; index < options.replaceStrings.length; index++) {
  211. let replaceString = options.replaceStrings[index];
  212. this.functions[includeName] = this.functions[includeName].replace(replaceString.search, replaceString.replace);
  213. }
  214. }
  215. }
  216. /** @hidden */
  217. public _emitVaryings(point: NodeMaterialConnectionPoint, define: string = "", force = false, fromFragment = false, replacementName: string = "") {
  218. let name = replacementName || point.associatedVariableName;
  219. if (point.isVarying || force) {
  220. if (this.sharedData.varyings.indexOf(name) !== -1) {
  221. return;
  222. }
  223. this.sharedData.varyings.push(name);
  224. if (define) {
  225. this.sharedData.varyingDeclaration += `#ifdef ${define}\r\n`;
  226. }
  227. this.sharedData.varyingDeclaration += `varying ${this._getGLType(point.type)} ${name};\r\n`;
  228. if (define) {
  229. this.sharedData.varyingDeclaration += `#endif\r\n`;
  230. }
  231. if (this.target === NodeMaterialBlockTargets.Vertex && fromFragment) {
  232. if (define) {
  233. this.sharedData.varyingDeclaration += `#ifdef ${define}\r\n`;
  234. }
  235. this._varyingTransfer += `${name} = ${point.name};\r\n`;
  236. if (define) {
  237. this.sharedData.varyingDeclaration += `#endif\r\n`;
  238. }
  239. }
  240. }
  241. }
  242. private _emitDefine(define: string): string {
  243. if (define[0] === "!") {
  244. return `#ifndef ${define.substring(1)}\r\n`;
  245. }
  246. return `#ifdef ${define}\r\n`;
  247. }
  248. /** @hidden */
  249. public _emitUniformOrAttributes(point: NodeMaterialConnectionPoint, define?: string) {
  250. define = define || point.define;
  251. // Samplers
  252. if (point.type === NodeMaterialBlockConnectionPointTypes.Texture) {
  253. point.name = this._getFreeVariableName(point.name);
  254. point.associatedVariableName = point.name;
  255. if (this.samplers.indexOf(point.name) !== -1) {
  256. return;
  257. }
  258. this.samplers.push(point.name);
  259. if (define) {
  260. this._uniformDeclaration += this._emitDefine(define);
  261. }
  262. this._samplerDeclaration += `uniform ${this._getGLType(point.type)} ${point.name};\r\n`;
  263. if (define) {
  264. this._uniformDeclaration += `#endif\r\n`;
  265. }
  266. this.sharedData.uniformConnectionPoints.push(point);
  267. return;
  268. }
  269. if (!point.isUniform && !point.isAttribute) {
  270. return;
  271. }
  272. // Uniforms
  273. if (point.isUniform) {
  274. if (!point.associatedVariableName) {
  275. point.associatedVariableName = this._getFreeVariableName(point.name);
  276. }
  277. if (this.uniforms.indexOf(point.associatedVariableName) !== -1) {
  278. return;
  279. }
  280. this.uniforms.push(point.associatedVariableName);
  281. if (define) {
  282. this._uniformDeclaration += this._emitDefine(define);
  283. }
  284. this._uniformDeclaration += `uniform ${this._getGLType(point.type)} ${point.associatedVariableName};\r\n`;
  285. if (define) {
  286. this._uniformDeclaration += `#endif\r\n`;
  287. }
  288. // well known
  289. let hints = this.sharedData.hints;
  290. if (point._wellKnownValue !== null) {
  291. switch (point._wellKnownValue) {
  292. case NodeMaterialWellKnownValues.WorldView:
  293. hints.needWorldViewMatrix = true;
  294. break;
  295. case NodeMaterialWellKnownValues.WorldViewProjection:
  296. hints.needWorldViewProjectionMatrix = true;
  297. break;
  298. }
  299. }
  300. this.sharedData.uniformConnectionPoints.push(point);
  301. return;
  302. }
  303. // Attribute
  304. if (point.isAttribute) {
  305. point.associatedVariableName = point.name;
  306. if (this.target === NodeMaterialBlockTargets.Fragment) { // Attribute for fragment need to be carried over by varyings
  307. this._vertexState._emitUniformOrAttributes(point);
  308. if (point._needToEmitVarying) {
  309. this._vertexState._emitVaryings(point, undefined, true, true, "v" + point.associatedVariableName);
  310. point.associatedVariableName = "v" + point.associatedVariableName;
  311. }
  312. return;
  313. }
  314. if (this.attributes.indexOf(point.associatedVariableName) !== -1) {
  315. return;
  316. }
  317. this.attributes.push(point.associatedVariableName);
  318. if (define) {
  319. this._attributeDeclaration += this._emitDefine(define);
  320. }
  321. this._attributeDeclaration += `attribute ${this._getGLType(point.type)} ${point.associatedVariableName};\r\n`;
  322. if (define) {
  323. this._attributeDeclaration += `#endif\r\n`;
  324. }
  325. }
  326. }
  327. }