Home Reference Source

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

import { Events } from "cables-shared-client";
import { ShaderGraph } from "./cgl_shadergraph.js";

const ShaderGraphProgram = class extends Events
{
    constructor(op, port, type)
    {
        super();
        this._type = type;
        this._op = op;
        this._port = port;
        this.uniforms = [];

        this._opIdsHeadFuncSrc = {};
        this._opIdsFuncCallSrc = {};
        this._functionIdInHead = {};

        this._headFuncSrc = "";
        this._headUniSrc = "";
        this._callFuncStack = [];
        this.finalSrc = "";
    }

    setOpShaderId(op)
    {
        if (!op.shaderId) op.shaderId = CGL.ShaderGraph.getNewId();
    }

    replaceId(op, txt)
    {
        this.setOpShaderId(op);
        return txt.replaceAll("_ID", "_" + op.shaderId);
    }

    addOpShaderFuncCode(op)
    {
        if (!op.sgOp)
        {
            console.warn("HAS NO SGOP!", op);
            return;
        }
        // if (!op.sgOp.info) return;


        if (this._opIdsHeadFuncSrc[op.id])
        {
        // console.log("already exist",op.name,op.id);
            return;
        }
        this._opIdsHeadFuncSrc[op.id] = true;

        if (op.sgOp && op.sgOp._defines)
            for (let i = 0; i < op.sgOp._defines.length; i++)
                this._headFuncSrc += "#define " + op.sgOp._defines[i][0] + "\n";

        // if (op.shaderSrc)
        // {
        //     let src = op.shaderSrc.endl();// +"/* "+op.id+" */".endl();;
        //     src = this.replaceId(op, src);
        //     this._headFuncSrc += src;
        // }


        if (op.sgOp.info)
        {
            // console.log(op.sgOp.info.name, op.sgOp.info.functions);
            for (let i = 0; i < op.sgOp.info.functions.length; i++)
            {
                const f = op.sgOp.info.functions[i];
                // console.log("ADD FUNCTION CODE", f.name, f.uniqueName, this._functionIdInHead[f.uniqueName]);
                if (this._functionIdInHead[f.uniqueName]) continue;
                if (!f.name.contains("_ID")) this._functionIdInHead[f.uniqueName] = true;
                let src = f.src;
                // console.log("src", src);
                src = this.replaceId(op, src);
                this._headFuncSrc += src;
            }
        }

        if (op.shaderUniforms)
        {
            for (let i = 0; i < op.shaderUniforms.length; i++)
            {
                const uni = op.shaderUniforms[i];
                if (!uni.static)
                {
                    this._headUniSrc += "uniform " + CGL.Uniform.glslTypeString(uni.type) + " " + uni.name + ";".endl();
                    this.uniforms.push(uni);
                }
                else
                    this._headUniSrc += this.uniformAsStaticVar(uni);
            }
        }
    }

    uniformAsStaticVar(uni)
    {
        const typeStr = CGL.Uniform.glslTypeString(uni.type);
        let str = "";

        if (typeStr == "float")
        {
            let floatStr = String(uni.ports[0].get());
            if (!floatStr.contains("."))floatStr += ".";
            str = typeStr + " " + uni.name + " = " + floatStr + ";".endl();
        }
        else
        {
            str = typeStr + " " + uni.name + "=" + typeStr + "(";

            for (let i = 0; i < uni.ports.length; i++)
            {
                str += uni.ports[i].get();
                if (i != uni.ports.length - 1)str += ",";
            }

            str += ");".endl();
        }
        return str;
    }

    callFunc(op, convertTo)
    {
        this.setOpShaderId(op);
        let callstr = "  ";

        const varname = "var_" + op.getTitle() + "_" + op.shaderId;
        if (convertTo)callstr += ShaderGraph.typeConv(convertTo) + " " + varname + " = ";

        if (this._opIdsFuncCallSrc[op.shaderId])
        {
            if (varname) return varname;
            return;
        }
        this._opIdsFuncCallSrc[op.shaderId] = true;

        callstr += this.replaceId(op, op.shaderFunc || "") + "(";

        this.addOpShaderFuncCode(op);

        const numObjectPorts = this.countObjectInputPorts(op);
        let count = 0;
        for (let i = 0; i < op.portsIn.length; i++)
        {
            let paramStr = "";
            const p = op.portsIn[i];
            if (p.uiAttribs.objType == "sg_void") continue;
            if (p.type != CABLES.OP_PORT_TYPE_OBJECT) continue;

            // parameters...
            if (p.isLinked())
            {
                for (let j = 0; j < p.links.length; j++)
                {
                    const otherPort = p.links[j].getOtherPort(p);
                    paramStr = this._getPortParamStr(otherPort, p.uiAttribs.objType);

                    // console.log("objtype", p.uiAttribs.objType);
                    this.addOpShaderFuncCode(otherPort.op);
                }
            }
            else
            {
                this.addOpShaderFuncCode(p.op);
                // if (p.uiAttribs.objType == "sg_sampler2D")
                // {
                //     // callstr = "vec4(1.0)";
                //     // break;
                //     // paramStr = "null";
                //     // break;
                // }
                // else
                // {
                paramStr = ShaderGraph.getDefaultParameter(p.uiAttribs.objType);
                // }
            }

            if (p.op.shaderCodeOperator)
            {
                callstr += paramStr;
                if (count < numObjectPorts - 1) callstr += " " + p.op.shaderCodeOperator + " ";
            }
            else
            if (paramStr)
            {
                callstr += paramStr;
                if (count < numObjectPorts - 1) callstr += ", ";
            }
            count++;
        }

        callstr += ");";

        this._callFuncStack.push(callstr);

        return varname;
    }

    countObjectInputPorts(op)
    {
        let count = 0;
        for (let i = 0; i < op.portsIn.length; i++)
            if (op.portsIn[i].type == CABLES.OP_PORT_TYPE_OBJECT)
                count++;
        return count;
    }


    _getPortParamStr(p, convertTo)
    {
        let paramStr = "";

        if (p.op.shaderVar)
        {
            paramStr = p.op.shaderVar;
        }
        else
        if (p.direction == CABLES.PORT_DIR_OUT)
        {
            paramStr += this.callFunc(p.op, p.uiAttribs.objType);
        }

        if (convertTo && convertTo != p.uiAttribs.objType)
        {
            paramStr = ShaderGraph.convertTypes(convertTo, p.uiAttribs.objType, paramStr);
        }

        return paramStr;
    }

    compile()
    {
        const port = this._port;
        const l = port.links;

        this.uniforms = [];
        this._callFuncStack = [];
        this._functionIdInHead = {};
        this._opIdsFuncCallSrc = {};
        this._opIdsHeadFuncSrc = {};
        this._headFuncSrc = "";
        this._headUniSrc = "";
        let callSrc = "";

        for (let i = 0; i < l.length; i++)
        {
            const lnk = l[i];
            callSrc += this.callFunc(lnk.getOtherPort(port).op) + ";".endl();
        }

        callSrc = this._callFuncStack.join("\n");

        let src = "".endl() + "{{MODULES_HEAD}}".endl().endl();

        // console.log("COMPILE", this._type);
        // todo use shader attrib system...

        if (this._type == "frag") src += "IN vec2 texCoord;".endl().endl();
        if (this._type == "vert") src += "IN vec3 vPosition;".endl() +
                "IN vec2 attrTexCoord;".endl() +
                "OUT vec2 texCoord;".endl().endl();

        if (this._type == "vert")src += "".endl() +
                "UNI mat4 projMatrix;".endl().endl() +
                "UNI mat4 viewMatrix;".endl().endl() +
                "UNI mat4 modelMatrix;".endl().endl();

        src +=
            this._headUniSrc.endl().endl() +
            this._headFuncSrc.endl().endl() +
            "void main()".endl() +
            "{".endl();

        if (this._type == "frag")src += "  {{MODULE_BEGIN_FRAG}}".endl();
        if (this._type == "vert")src += "  {{MODULE_BEGIN_VERTEX}}".endl();

        src += callSrc.endl() +
            "}".endl();

        this.finalSrc = src;

        this.emitEvent("compiled");
    }
};

export { ShaderGraphProgram };