Home Reference Source

cables_dev/cables/src/libs/cgl/shadergraph/cgl_shadergraphop.js

  1. class ShaderGraphOp
  2. {
  3. constructor(op, src)
  4. {
  5. op.sgOp = this;
  6. this._op = op;
  7. this._inPorts = [];
  8. this._outPorts = [];
  9. this._defines = [];
  10. this.enabled = true;
  11. this.info = null;
  12.  
  13. if (src)
  14. this.parseCode(src);
  15.  
  16. this._op.on("onLinkChanged", this.updateGraph.bind(this));
  17. this.addPortWatcher();
  18. }
  19.  
  20. addPortWatcher()
  21. {
  22. for (let i = 0; i < this._op.portsIn.length; i++)
  23. {
  24. if (this._op.portsIn[i].type != CABLES.OP_PORT_TYPE_OBJECT) continue;
  25.  
  26.  
  27. if (this._op.portsIn[i].uiAttribs.objType && this._op.portsIn[i].uiAttribs.objType.indexOf("sg_") == 0) this._op.portsIn[i].setUiAttribs({ "display": "sg_vec" });
  28.  
  29. this._op.portsIn[i].on("change", this.updateGraph.bind(this));
  30. }
  31. }
  32.  
  33. updateGraph()
  34. {
  35. for (let i = 0; i < this._op.portsOut.length; i++)
  36. {
  37. if (this._op.portsOut[i].type != CABLES.OP_PORT_TYPE_OBJECT) continue;
  38. // this._op.portsOut[i].setRef(null);
  39. this._op.portsOut[i].setRef({});
  40. }
  41. }
  42.  
  43. isTypeDef(str)
  44. {
  45. return str == "void" || str == "float" || str == "sampler2D" || str == "vec2" || str == "vec3" || str == "vec4" || str == "void" || str == "mat4" || str == "mat3" || str == "mat2" || str == "out";
  46. }
  47.  
  48. parseCode(_code)
  49. {
  50. let code = _code;
  51. let info = { "functions": [], "uniforms": [] };
  52.  
  53. const origLines = code.split("\n");
  54. const prelines = code.split("\n");
  55.  
  56. for (let i = 0; i < prelines.length; i++)
  57. prelines[i] += "###line:" + i + ":###";
  58.  
  59. code = prelines.join("\n");
  60.  
  61. code = code.replaceAll("{{", ""); // remove spaces before brackets
  62. code = code.replaceAll("}}", ""); // remove spaces before brackets
  63.  
  64. // code = code.replaceAll(/\/\*[\s\S]*?\*\/|\/\/.*/g, ""); // remove comments
  65. // code = code.replaceAll(/{[^{}]*}/g, "{}"); // remove function content
  66. code = code.replaceAll("\n{", "{");
  67. code = code.replaceAll(";", " ;"); // add spaces for better splitting
  68. // code = code.replaceAll("{", "{"); // remove spaces before brackets
  69. code = code.replaceAll("(", " ( "); // add spaces for better splitting
  70. code = code.replaceAll(")", " ) "); // add spaces for better splitting
  71. code = code.replaceAll(",", " , "); // add spaces for better splitting
  72. code = code.replace(/ +(?= )/g, ""); // remove double whitespaces
  73.  
  74. // console.log(origLines);
  75.  
  76. const lines = code.split("\n");
  77.  
  78. // console.log(lines);
  79.  
  80. for (let i = 0; i < lines.length; i++)
  81. {
  82. // identify function
  83. if (lines[i].indexOf("{") > 0 && lines[i].indexOf("(") > 0 && lines[i].indexOf(")") > 0)
  84. {
  85. const words = lines[i].split(" ");
  86.  
  87. if (this.isTypeDef(words[0])) // function header start with return typedef
  88. {
  89. // merge all the remaining lines to be able to search for the end of the function ...
  90. let remainingcode = "";
  91. for (let j = i; j < lines.length; j++) remainingcode += lines[j];
  92.  
  93. // search for all {} and find the end of the function body...
  94. const startPos = remainingcode.indexOf("{");
  95. let count = 0;
  96. let cc = 0;
  97. for (cc = startPos; cc < remainingcode.length; cc++)
  98. {
  99. if (remainingcode.charAt(cc) == "{") count++;
  100. if (remainingcode.charAt(cc) == "}") count--;
  101. if (count == 0) break;
  102. }
  103.  
  104. // console.log("remainingcode", remainingcode);
  105. // parse the first and last line numbers
  106. let functioncode = remainingcode.substring(0, cc + 1);
  107. const linenums = functioncode.split("###line:");
  108.  
  109. // console.log("functioncode", functioncode);
  110. // console.log("linenums", linenums);
  111.  
  112. let lineNumStart = i, lineNumEnd = i - 1;
  113. if (linenums.length > 1)
  114. {
  115. lineNumStart = parseInt(linenums[1].split(":")[0]);
  116. lineNumEnd = parseInt(linenums[linenums.length - 1].split(":")[0]);
  117. }
  118.  
  119. functioncode = "";
  120.  
  121. // concat the whole function
  122. for (let j = lineNumStart; j <= lineNumEnd + 1; j++)
  123. if (origLines[j])functioncode += origLines[j] + "\n";
  124.  
  125. const infoFunc = { "name": words[1], "type": words[0], "params": [], "src": functioncode };
  126. infoFunc.uniqueName = words[0] + "_" + words[1];
  127.  
  128. // analyze function head and read all parameters
  129. words.length = words.indexOf(")") + 1;
  130. for (let j = 3; j < words.length - 2; j += 3)
  131. {
  132. infoFunc.params.push({ "name": words[j + 1], "type": words[j] });
  133. infoFunc.uniqueName += "_" + words[j + 0] + "_" + words[j + 1];
  134. }
  135.  
  136. info.functions.push(infoFunc);
  137. }
  138. }
  139.  
  140. if (lines[i].indexOf("UNI") == 0 || lines[i].indexOf("uniform") == 0)
  141. {
  142. const words = lines[i].split(" ");
  143. if (this.isTypeDef(words[1])) info.uniforms.push({ "name": words[2], "type": words[1] });
  144. }
  145. }
  146.  
  147. info.src = _code;
  148. // if (this._op.uiAttribs.comment)_code = "//" + this._op.uiAttribs.comment + "\n" + _code;
  149.  
  150. this.info = info;
  151. this.updatePorts(this.info);
  152.  
  153. return info;
  154. }
  155.  
  156. updatePorts(info)
  157. {
  158. const foundPortInNames = {};
  159. this._op.shaderSrc = info.src;
  160.  
  161. if (info.functions.length > 0)
  162. {
  163. const f = info.functions[info.functions.length - 1];
  164. this._op.setTitle(f.name);
  165. this._op.shaderFunc = f.name;
  166.  
  167. for (let p = 0; p < f.params.length; p++)
  168. {
  169. const port = this._op.getPort(f.params[p].name) || this._op.inObject(f.params[p].name);
  170.  
  171. // let changed = false;
  172. // if (port.uiAttribs.objType != f.params[p].type) changed = true;
  173. port.setUiAttribs({ "objType": "sg_" + f.params[p].type, "ignoreObjTypeErrors": true });
  174. // if (changed) port.setRef(port.get());
  175.  
  176. this._inPorts.push(port);
  177.  
  178. foundPortInNames[f.params[p].name] = true;
  179. }
  180.  
  181. let port = this._op.getPort("Result");
  182. if (!port)
  183. {
  184. port = this._op.outObject("Result");
  185. this._outPorts.push(port);
  186. }
  187.  
  188. // let changed = false;
  189. // if (port.uiAttribs.objType != f.type) changed = true;
  190. port.setUiAttribs({ "objType": "sg_" + f.type });
  191. // if (changed) port.setRef(port.get());
  192. }
  193.  
  194. for (let i = 0; i < this._inPorts.length; i++) if (!foundPortInNames[this._inPorts[i].name]) this._inPorts[i].remove();
  195.  
  196. this.addPortWatcher();
  197. this._op.refreshParams();
  198. }
  199.  
  200.  
  201. /**
  202. * add a define to a shader, e.g. #define DO_THIS_THAT 1
  203. * @function define
  204. * @memberof Shader
  205. * @instance
  206. * @param {String} name
  207. * @param {Any} value (can be empty)
  208. */
  209. define(name, value)
  210. {
  211. if (value === null || value === undefined) value = "";
  212.  
  213. if (typeof (value) == "object") // port
  214. {
  215. value.removeEventListener("change", value.onDefineChange);
  216. value.onDefineChange = (v) =>
  217. {
  218. this.define(name, v);
  219. };
  220. value.on("change", value.onDefineChange);
  221.  
  222. value = value.get();
  223. }
  224.  
  225.  
  226. for (let i = 0; i < this._defines.length; i++)
  227. {
  228. if (this._defines[i][0] == name && this._defines[i][1] == value) return;
  229. if (this._defines[i][0] == name)
  230. {
  231. this._defines[i][1] = value;
  232. // this.setWhyCompile("define " + name + " " + value);
  233.  
  234. // this._needsRecompile = true;
  235. return;
  236. }
  237. }
  238. // this.setWhyCompile("define " + name + " " + value);
  239. // this._needsRecompile = true;
  240. this._defines.push([name, value]);
  241. this.updateGraph();
  242. }
  243.  
  244. getDefines()
  245. {
  246. return this._defines;
  247. }
  248.  
  249. getDefine(name)
  250. {
  251. for (let i = 0; i < this._defines.length; i++)
  252. if (this._defines[i][0] == name) return this._defines[i][1];
  253. return null;
  254. }
  255.  
  256. /**
  257. * return true if shader has define
  258. * @function hasDefine
  259. * @memberof Shader
  260. * @instance
  261. * @param {String} name
  262. * @return {Boolean}
  263. */
  264. hasDefine(name)
  265. {
  266. for (let i = 0; i < this._defines.length; i++)
  267. if (this._defines[i][0] == name) return true;
  268. }
  269.  
  270. /**
  271. * remove a define from a shader
  272. * @param {name} name
  273. * @function removeDefine
  274. * @memberof Shader
  275. * @instance
  276. */
  277. removeDefine(name)
  278. {
  279. for (let i = 0; i < this._defines.length; i++)
  280. {
  281. if (this._defines[i][0] == name)
  282. {
  283. this._defines.splice(i, 1);
  284. // this._needsRecompile = true;
  285.  
  286. // this.setWhyCompile("define removed:" + name);
  287. this.updateGraph();
  288. return;
  289. }
  290. }
  291. }
  292.  
  293. toggleDefine(name, enabled)
  294. {
  295. if (enabled) this.define(name);
  296. else this.removeDefine(name);
  297. this.updateGraph();
  298. }
  299. }
  300.  
  301.  
  302. ShaderGraphOp.getMaxGenTypeFromPorts = (ports, portsSetType) =>
  303. {
  304. const types = ["sg_float", "sg_vec2", "sg_vec3", "sg_vec4"];
  305. let typeIdx = 0;
  306.  
  307. for (let j = 0; j < ports.length; j++)
  308. for (let i = 0; i < ports[j].links.length; i++)
  309. {
  310. const t = types.indexOf(ports[j].links[i].getOtherPort(ports[j]).uiAttribs.objType);
  311. typeIdx = Math.max(typeIdx, t);
  312. }
  313.  
  314. const t = types[typeIdx];
  315.  
  316. if (portsSetType)
  317. for (let i = 0; i < portsSetType.length; i++)
  318. portsSetType[i].setUiAttribs({ "objType": t });
  319.  
  320. return t;
  321. };
  322.  
  323. export { ShaderGraphOp };