cables_dev/cables/src/libs/cgl/shadergraph/cgl_shadergraphop.js
- class ShaderGraphOp
- {
- constructor(op, src)
- {
- op.sgOp = this;
- this._op = op;
- this._inPorts = [];
- this._outPorts = [];
- this._defines = [];
- this.enabled = true;
- this.info = null;
-
- if (src)
- this.parseCode(src);
-
- this._op.on("onLinkChanged", this.updateGraph.bind(this));
- this.addPortWatcher();
- }
-
- addPortWatcher()
- {
- for (let i = 0; i < this._op.portsIn.length; i++)
- {
- if (this._op.portsIn[i].type != CABLES.OP_PORT_TYPE_OBJECT) continue;
-
-
- if (this._op.portsIn[i].uiAttribs.objType && this._op.portsIn[i].uiAttribs.objType.indexOf("sg_") == 0) this._op.portsIn[i].setUiAttribs({ "display": "sg_vec" });
-
- this._op.portsIn[i].on("change", this.updateGraph.bind(this));
- }
- }
-
- updateGraph()
- {
- for (let i = 0; i < this._op.portsOut.length; i++)
- {
- if (this._op.portsOut[i].type != CABLES.OP_PORT_TYPE_OBJECT) continue;
- // this._op.portsOut[i].setRef(null);
- this._op.portsOut[i].setRef({});
- }
- }
-
- isTypeDef(str)
- {
- return str == "void" || str == "float" || str == "sampler2D" || str == "vec2" || str == "vec3" || str == "vec4" || str == "void" || str == "mat4" || str == "mat3" || str == "mat2" || str == "out";
- }
-
- parseCode(_code)
- {
- let code = _code;
- let info = { "functions": [], "uniforms": [] };
-
- const origLines = code.split("\n");
- const prelines = code.split("\n");
-
- for (let i = 0; i < prelines.length; i++)
- prelines[i] += "###line:" + i + ":###";
-
- code = prelines.join("\n");
-
- code = code.replaceAll("{{", ""); // remove spaces before brackets
- code = code.replaceAll("}}", ""); // remove spaces before brackets
-
- // code = code.replaceAll(/\/\*[\s\S]*?\*\/|\/\/.*/g, ""); // remove comments
- // code = code.replaceAll(/{[^{}]*}/g, "{}"); // remove function content
- code = code.replaceAll("\n{", "{");
- code = code.replaceAll(";", " ;"); // add spaces for better splitting
- // code = code.replaceAll("{", "{"); // remove spaces before brackets
- code = code.replaceAll("(", " ( "); // add spaces for better splitting
- code = code.replaceAll(")", " ) "); // add spaces for better splitting
- code = code.replaceAll(",", " , "); // add spaces for better splitting
- code = code.replace(/ +(?= )/g, ""); // remove double whitespaces
-
- // console.log(origLines);
-
- const lines = code.split("\n");
-
- // console.log(lines);
-
- for (let i = 0; i < lines.length; i++)
- {
- // identify function
- if (lines[i].indexOf("{") > 0 && lines[i].indexOf("(") > 0 && lines[i].indexOf(")") > 0)
- {
- const words = lines[i].split(" ");
-
- if (this.isTypeDef(words[0])) // function header start with return typedef
- {
- // merge all the remaining lines to be able to search for the end of the function ...
- let remainingcode = "";
- for (let j = i; j < lines.length; j++) remainingcode += lines[j];
-
- // search for all {} and find the end of the function body...
- const startPos = remainingcode.indexOf("{");
- let count = 0;
- let cc = 0;
- for (cc = startPos; cc < remainingcode.length; cc++)
- {
- if (remainingcode.charAt(cc) == "{") count++;
- if (remainingcode.charAt(cc) == "}") count--;
- if (count == 0) break;
- }
-
- // console.log("remainingcode", remainingcode);
- // parse the first and last line numbers
- let functioncode = remainingcode.substring(0, cc + 1);
- const linenums = functioncode.split("###line:");
-
- // console.log("functioncode", functioncode);
- // console.log("linenums", linenums);
-
- let lineNumStart = i, lineNumEnd = i - 1;
- if (linenums.length > 1)
- {
- lineNumStart = parseInt(linenums[1].split(":")[0]);
- lineNumEnd = parseInt(linenums[linenums.length - 1].split(":")[0]);
- }
-
- functioncode = "";
-
- // concat the whole function
- for (let j = lineNumStart; j <= lineNumEnd + 1; j++)
- if (origLines[j])functioncode += origLines[j] + "\n";
-
- const infoFunc = { "name": words[1], "type": words[0], "params": [], "src": functioncode };
- infoFunc.uniqueName = words[0] + "_" + words[1];
-
- // analyze function head and read all parameters
- words.length = words.indexOf(")") + 1;
- for (let j = 3; j < words.length - 2; j += 3)
- {
- infoFunc.params.push({ "name": words[j + 1], "type": words[j] });
- infoFunc.uniqueName += "_" + words[j + 0] + "_" + words[j + 1];
- }
-
- info.functions.push(infoFunc);
- }
- }
-
- if (lines[i].indexOf("UNI") == 0 || lines[i].indexOf("uniform") == 0)
- {
- const words = lines[i].split(" ");
- if (this.isTypeDef(words[1])) info.uniforms.push({ "name": words[2], "type": words[1] });
- }
- }
-
- info.src = _code;
- // if (this._op.uiAttribs.comment)_code = "//" + this._op.uiAttribs.comment + "\n" + _code;
-
- this.info = info;
- this.updatePorts(this.info);
-
- return info;
- }
-
- updatePorts(info)
- {
- const foundPortInNames = {};
- this._op.shaderSrc = info.src;
-
- if (info.functions.length > 0)
- {
- const f = info.functions[info.functions.length - 1];
- this._op.setTitle(f.name);
- this._op.shaderFunc = f.name;
-
- for (let p = 0; p < f.params.length; p++)
- {
- const port = this._op.getPort(f.params[p].name) || this._op.inObject(f.params[p].name);
-
- // let changed = false;
- // if (port.uiAttribs.objType != f.params[p].type) changed = true;
- port.setUiAttribs({ "objType": "sg_" + f.params[p].type, "ignoreObjTypeErrors": true });
- // if (changed) port.setRef(port.get());
-
- this._inPorts.push(port);
-
- foundPortInNames[f.params[p].name] = true;
- }
-
- let port = this._op.getPort("Result");
- if (!port)
- {
- port = this._op.outObject("Result");
- this._outPorts.push(port);
- }
-
- // let changed = false;
- // if (port.uiAttribs.objType != f.type) changed = true;
- port.setUiAttribs({ "objType": "sg_" + f.type });
- // if (changed) port.setRef(port.get());
- }
-
- for (let i = 0; i < this._inPorts.length; i++) if (!foundPortInNames[this._inPorts[i].name]) this._inPorts[i].remove();
-
- this.addPortWatcher();
- this._op.refreshParams();
- }
-
-
- /**
- * add a define to a shader, e.g. #define DO_THIS_THAT 1
- * @function define
- * @memberof Shader
- * @instance
- * @param {String} name
- * @param {Any} value (can be empty)
- */
- define(name, value)
- {
- if (value === null || value === undefined) value = "";
-
- if (typeof (value) == "object") // port
- {
- value.removeEventListener("change", value.onDefineChange);
- value.onDefineChange = (v) =>
- {
- this.define(name, v);
- };
- value.on("change", value.onDefineChange);
-
- value = value.get();
- }
-
-
- for (let i = 0; i < this._defines.length; i++)
- {
- if (this._defines[i][0] == name && this._defines[i][1] == value) return;
- if (this._defines[i][0] == name)
- {
- this._defines[i][1] = value;
- // this.setWhyCompile("define " + name + " " + value);
-
- // this._needsRecompile = true;
- return;
- }
- }
- // this.setWhyCompile("define " + name + " " + value);
- // this._needsRecompile = true;
- this._defines.push([name, value]);
- this.updateGraph();
- }
-
- getDefines()
- {
- return this._defines;
- }
-
- getDefine(name)
- {
- for (let i = 0; i < this._defines.length; i++)
- if (this._defines[i][0] == name) return this._defines[i][1];
- return null;
- }
-
- /**
- * return true if shader has define
- * @function hasDefine
- * @memberof Shader
- * @instance
- * @param {String} name
- * @return {Boolean}
- */
- hasDefine(name)
- {
- for (let i = 0; i < this._defines.length; i++)
- if (this._defines[i][0] == name) return true;
- }
-
- /**
- * remove a define from a shader
- * @param {name} name
- * @function removeDefine
- * @memberof Shader
- * @instance
- */
- removeDefine(name)
- {
- for (let i = 0; i < this._defines.length; i++)
- {
- if (this._defines[i][0] == name)
- {
- this._defines.splice(i, 1);
- // this._needsRecompile = true;
-
- // this.setWhyCompile("define removed:" + name);
- this.updateGraph();
- return;
- }
- }
- }
-
- toggleDefine(name, enabled)
- {
- if (enabled) this.define(name);
- else this.removeDefine(name);
- this.updateGraph();
- }
- }
-
-
- ShaderGraphOp.getMaxGenTypeFromPorts = (ports, portsSetType) =>
- {
- const types = ["sg_float", "sg_vec2", "sg_vec3", "sg_vec4"];
- let typeIdx = 0;
-
- for (let j = 0; j < ports.length; j++)
- for (let i = 0; i < ports[j].links.length; i++)
- {
- const t = types.indexOf(ports[j].links[i].getOtherPort(ports[j]).uiAttribs.objType);
- typeIdx = Math.max(typeIdx, t);
- }
-
- const t = types[typeIdx];
-
- if (portsSetType)
- for (let i = 0; i < portsSetType.length; i++)
- portsSetType[i].setUiAttribs({ "objType": t });
-
- return t;
- };
-
- export { ShaderGraphOp };