cables_dev/cables/src/core/cg/cg_shader.js
import { EventTarget } from "../eventtarget.js";
import { simpleId } from "../utils.js";
class CgShader extends EventTarget
{
constructor()
{
super();
this.id = simpleId();
this._isValid = true;
this._defines = [];
this._moduleNames = [];
this._modules = [];
this._moduleNumId = 0;
}
/**
* easily enable/disable a define without a value
* @function toggleDefine
* @memberof Shader
* @instance
* @param {name} name
* @param {any} enabled value or port
*/
toggleDefine(name, enabled)
{
if (enabled && typeof (enabled) == "object" && enabled.addEventListener) // port
{
if (enabled.changeListener)enabled.removeEventListener(enabled.changeListener);
enabled.onToggleDefine = (v) =>
{
this.toggleDefine(name, v);
};
enabled.changeListener = enabled.on("change", enabled.onToggleDefine);
enabled = enabled.get();
}
if (enabled) this.define(name);
else this.removeDefine(name);
}
/**
* 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]);
}
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);
return;
}
}
}
hasModule(modId)
{
for (let i = 0; i < this._modules.length; i++)
{
if (this._modules[i].id == modId) return true;
}
return false;
}
setModules(names)
{
this._moduleNames = names;
}
/**
* remove a module from shader
* @function removeModule
* @memberof Shader
* @instance
* @param {shaderModule} mod the module to be removed
*/
removeModule(mod)
{
for (let i = 0; i < this._modules.length; i++)
{
if (mod && mod.id)
{
if (this._modules[i].id == mod.id || !this._modules[i])
{
let found = true;
while (found)
{
found = false;
for (let j = 0; j < this._uniforms.length; j++)
{
if (this._uniforms[j].getName().startsWith(mod.prefix))
{
this._uniforms.splice(j, 1);
found = true;
continue;
}
}
}
this._needsRecompile = true;
this.setWhyCompile("remove module " + mod.title);
this._modules.splice(i, 1);
break;
}
}
}
}
getNumModules()
{
return this._modules.length;
}
getCurrentModules() { return this._modules; }
/**
* add a module
* @function addModule
* @memberof Shader
* @instance
* @param {shaderModule} mod the module to be added
* @param {shaderModule} [sibling] sibling module, new module will share the same group
*/
addModule(mod, sibling)
{
if (this.hasModule(mod.id)) return;
if (!mod.id) mod.id = CABLES.simpleId();
if (!mod.numId) mod.numId = this._moduleNumId;
if (!mod.num)mod.num = this._modules.length;
if (sibling && !sibling.group) sibling.group = simpleId();
if (!mod.group)
if (sibling) mod.group = sibling.group;
else mod.group = simpleId();
mod.prefix = "mod" + mod.group + "_";
this._modules.push(mod);
this._needsRecompile = true;
this.setWhyCompile("add module " + mod.title);
this._moduleNumId++;
return mod;
}
getAttributeSrc(mod, srcHeadVert, srcVert)
{
if (mod.attributes)
for (let k = 0; k < mod.attributes.length; k++)
{
const r = this._getAttrSrc(mod.attributes[k], false);
if (r.srcHeadVert)srcHeadVert += r.srcHeadVert;
if (r.srcVert)srcVert += r.srcVert;
}
return { "srcHeadVert": srcHeadVert, "srcVert": srcVert };
}
replaceModuleSrc()
{
let srcHeadVert = "";
for (let j = 0; j < this._modules.length; j++)
{
const mod = this._modules[j];
if (mod.name == this._moduleNames[i])
{
srcHeadVert += "\n//---- MOD: group:" + mod.group + ": idx:" + j + " - prfx:" + mod.prefix + " - " + mod.title + " ------\n";
srcVert += "\n\n//---- MOD: " + mod.title + " / " + mod.priority + " ------\n";
if (mod.getAttributeSrc)
{
const r = getAttributeSrc(mod, srcHeadVert, srcVert);
if (r.srcHeadVert)srcHeadVert += r.srcHeadVert;
if (r.srcVert)srcVert += r.srcVert;
}
srcHeadVert += mod.srcHeadVert || "";
srcVert += mod.srcBodyVert || "";
srcHeadVert += "\n//---- end mod ------\n";
srcVert += "\n//---- end mod ------\n";
srcVert = srcVert.replace(/{{mod}}/g, mod.prefix);
srcHeadVert = srcHeadVert.replace(/{{mod}}/g, mod.prefix);
srcVert = srcVert.replace(/MOD_/g, mod.prefix);
srcHeadVert = srcHeadVert.replace(/MOD_/g, mod.prefix);
}
}
vs = vs.replace("{{" + this._moduleNames[i] + "}}", srcVert);
}
}
export { CgShader };