cables_dev/cables/src/core/cgl/cgl_shader.js
import { Logger } from "cables-shared-client";
import { ShaderLibMods } from "./cgl_shader_lib.js";
import { now } from "../timer.js";
import { MESH } from "./cgl_mesh.js";
import { CONSTANTS } from "./constants.js";
import { escapeHTML } from "./cgl_utils.js";
import { CgShader } from "../cg/cg_shader.js";
import defaultShaderSrcVert from "./cgl_shader_default_glsl.vert";
import { simpleId } from "../utils.js";
// ---------------------------------------------------------------------------
/*
proposal default shader variable names:
attrVertex - currently: vPosition
attrVertexIndex - currently: attrVertIndex
attrTexCoord
attrInstMat - currently: instMat
attrVertColor
attrTangent
attrBiTangent
uProjMatrix - currently: projMatrix
uModelMatrix - currently: modelMatrix
uNormalMatrix - currently: normalMatrix
uCamPosition - currently: camPos
*/
// ---------------------------------------------------------------------------
let materialIdCounter = 0;
function getDefaultVertexShader()
{
return defaultShaderSrcVert;
}
function getDefaultFragmentShader(r, g, b)
{
if (r == undefined)
{
r = 0.5;
g = 0.5;
b = 0.5;
}
return ""
.endl() + "IN vec2 texCoord;"
.endl() + "{{MODULES_HEAD}}"
.endl() + "void main()"
.endl() + "{"
.endl() + " vec4 col=vec4(" + r + "," + g + "," + b + ",1.0);"
.endl() + " {{MODULE_COLOR}}"
.endl() + " outColor = col;"
.endl() + "}";
};
/**
* @class
* @namespace external:CGL
* @hideconstructor
* @param _cgl
* @param _name
* @param _op
* @example
* var shader=new CGL.Shader(cgl,'MinimalMaterial');
* shader.setSource(attachments.shader_vert,attachments.shader_frag);
*/
class Shader extends CgShader
{
constructor (_cgl, _name, _op)
{
super();
if (!_cgl) throw new Error("shader constructed without cgl " + _name);
this._log = new Logger("cgl_shader");
this._cgl = _cgl;
if (!_name) this._log.stack("no shader name given");
this._name = _name || "unknown";
if (_op) this.opId = _op.id;
this.glslVersion = 0;
if (_cgl.glVersion > 1) this.glslVersion = 300;
this._materialId = ++materialIdCounter;
this._program = null;
this._uniforms = [];
this._drawBuffers = [true];
this._needsRecompile = true;
this._compileReason = "initial";
this.ignoreMissingUniforms = false;
this._projMatrixUniform = null;
this._mvMatrixUniform = null;
this._mMatrixUniform = null;
this._vMatrixUniform = null;
this._camPosUniform = null;
this._normalMatrixUniform = null;
this._inverseViewMatrixUniform = null;
this._fromUserInteraction = false;
this._attrVertexPos = -1;
this.precision = _cgl.patch.config.glslPrecision || "highp";
this._pMatrixState = -1;
this._vMatrixState = -1;
this._countMissingUniforms = 0;
this._modGroupCount = 0; // not needed anymore...
this._feedBackNames = [];
this._attributes = [];
this.glPrimitive = null;
this.offScreenPass = false;
this._extensions = [];
this.srcVert = getDefaultVertexShader();
this.srcFrag = getDefaultFragmentShader();
this.lastCompile = 0;
this._moduleNames = [];
this._modules = [];
this._moduleNumId = 0;
this._libs = [];
this._structNames = [];
this._structUniformNames = [];
this._textureStackUni = [];
this._textureStackTex = [];
this._textureStackType = [];
this._textureStackTexCgl = [];
this._tempNormalMatrix = mat4.create();
this._tempCamPosMatrix = mat4.create();
this._tempInverseViewMatrix = mat4.create();
this._tempInverseProjMatrix = mat4.create();
this.setModules(["MODULE_VERTEX_POSITION", "MODULE_COLOR", "MODULE_BEGIN_FRAG", "MODULE_VERTEX_MODELVIEW"]);
};
isValid()
{
return this._isValid;
};
getCgl()
{
return this._cgl;
};
getName()
{
return this._name;
};
/**
* enable an extension for the shader
* @function enableExtension
* @memberof Shader
* @instance
* @param name extension name
*/
enableExtension(name)
{
this.setWhyCompile("enable extension " + name);
this._needsRecompile = true;
this._extensions.push(name);
};
getAttrVertexPos()
{
return this._attrVertexPos;
};
hasTextureUniforms()
{
for (let i = 0; i < this._uniforms.length; i++)
if (this._uniforms[i].getType() == "t") return true;
return false;
};
setWhyCompile(why)
{
this._compileReason = why;
};
/**
* copy all uniform values from another shader
* @function copyUniforms
* @memberof Shader
* @instance
* @param origShader uniform values will be copied from this shader
*/
copyUniformValues(origShader)
{
// console.log(origShader._uniforms);
for (let i = 0; i < origShader._uniforms.length; i++)
{
if (!this._uniforms[i])
{
this._log.log("unknown uniform?!");
continue;
}
// this._log.log(origShader._uniforms[i].getName());
// this.getUniform(origShader._uniforms[i].)
// this._uniforms[i].set(origShader._uniforms[i].getValue());
// if (origShader._uniforms[i].getName().contains("pathPoints"))
// console.log("copyUniformValues", origShader._uniforms[i].getName(), origShader._uniforms[i].getValue());
this.getUniform(origShader._uniforms[i].getName()).set(origShader._uniforms[i].getValue());
}
this.popTextures();
for (let i = 0; i < origShader._textureStackUni.length; i++)
{
this._textureStackUni[i] = origShader._textureStackUni[i];
this._textureStackTex[i] = origShader._textureStackTex[i];
this._textureStackType[i] = origShader._textureStackType[i];
this._textureStackTexCgl[i] = origShader._textureStackTexCgl[i];
}
// this._textureStackUni = [];
// this._textureStackTex = [];
// this._textureStackType = [];
// this._textureStackTexCgl = [];
};
/**
* copy current shader
* @function copy
* @memberof Shader
* @instance
* @returns newShader
*/
copy()
{
const shader = new Shader(this._cgl, this._name + " copy");
shader.setSource(this.srcVert, this.srcFrag);
shader._modules = JSON.parse(JSON.stringify(this._modules));
shader._defines = JSON.parse(JSON.stringify(this._defines));
shader._modGroupCount = this._modGroupCount;
shader._moduleNames = this._moduleNames;
shader.glPrimitive = this.glPrimitive;
shader.offScreenPass = this.offScreenPass;
shader._extensions = this._extensions;
shader.wireframe = this.wireframe;
shader._attributes = this._attributes;
for (let i = 0; i < this._uniforms.length; i++)
{
const u = this._uniforms[i].copy(shader);
u.resetLoc();
}
this.setWhyCompile("copy");
shader._needsRecompile = true;
return shader;
};
/**
* set shader source code
* @function setSource
* @memberof Shader
* @instance
* @param {String} srcVert
* @param {String} srcFrag
* @param {Bool} fromUserInteraction
*/
setSource(srcVert, srcFrag, fromUserInteraction)
{
this._fromUserInteraction = fromUserInteraction;
this.srcVert = srcVert;
this.srcFrag = srcFrag;
this.setWhyCompile("Source changed");
this._needsRecompile = true;
this._isValid = true;
};
_addLibs(src)
{
for (const id in ShaderLibMods)
{
if (src.contains(id))
{
const lib = new ShaderLibMods[id]();
src = src.replace("{{" + id + "}}", lib.srcHeadFrag);
this._libs.push(lib);
if (lib.initUniforms)lib.initUniforms(this);
}
}
return src;
};
createStructUniforms()
{
// * create structs
let structStrFrag = "";
let structStrVert = ""; // TODO: not used yet
this._structNames = [];
// * reset the arrays holding the value each recompile so we don't skip structs
// * key value mapping so the same struct can be added twice (two times the same modifier)
this._injectedStringsFrag = {};
this._injectedStringsVert = {};
this._structUniformNamesIndicesFrag = [];
this._structUniformNamesIndicesVert = [];
for (let i = 0; i < this._uniforms.length; i++)
{
// * only add uniforms to struct that are a member of a struct
if (this._uniforms[i].isStructMember())
{
const injectionString = "{{INJECTION_POINT_STRUCT_" + this._uniforms[i]._structName + "}}";
// * check if struct is not already part of shader
if (!this._structNames.includes(this._uniforms[i]._structName))
{
// * create struct definition with placeholder string to inject
const structDefinition = "struct "
+ this._uniforms[i]._structName + " {".endl()
+ injectionString
+ "};".endl().endl();
if (this._uniforms[i].getShaderType() === "both" || this._uniforms[i].getShaderType() === "frag")
structStrFrag = structStrFrag.concat(structDefinition);
if (this._uniforms[i].getShaderType() === "both" || this._uniforms[i].getShaderType() === "vert")
structStrVert = structStrVert.concat(structDefinition);
this._structNames.push(this._uniforms[i]._structName);
this._injectedStringsFrag[this._uniforms[i]._structName] = [];
this._injectedStringsVert[this._uniforms[i]._structName] = [];
}
// * create member & comment
let comment = "";
if (this._uniforms[i].comment) comment = " // " + this._uniforms[i].comment;
let stringToInsert = "";
if (this._uniforms[i].getGlslTypeString() == undefined)stringToInsert += "//";
stringToInsert += " " + this._uniforms[i].getGlslTypeString()
+ " " + this._uniforms[i]._propertyName + ";"
+ comment;
if (this._uniforms[i].getShaderType() === "both")
{
// * inject member before {injectionString}
if (
!this._injectedStringsFrag[this._uniforms[i]._structName].contains(stringToInsert)
&& !this._injectedStringsVert[this._uniforms[i]._structName].contains(stringToInsert))
{
const insertionIndexFrag = structStrFrag.lastIndexOf(injectionString);
const insertionIndexVert = structStrVert.lastIndexOf(injectionString);
structStrFrag =
structStrFrag.slice(0, insertionIndexFrag)
+ stringToInsert + structStrFrag.slice(insertionIndexFrag - 1);
structStrVert =
structStrVert.slice(0, insertionIndexVert)
+ stringToInsert + structStrVert.slice(insertionIndexVert - 1);
this._injectedStringsFrag[this._uniforms[i]._structName].push(stringToInsert);
this._injectedStringsVert[this._uniforms[i]._structName].push(stringToInsert);
}
if (!this._structUniformNamesIndicesFrag.includes(i)) this._structUniformNamesIndicesFrag.push(i);
if (!this._structUniformNamesIndicesVert.includes(i)) this._structUniformNamesIndicesVert.push(i);
}
else if (this._uniforms[i].getShaderType() === "frag")
{
// * inject member before {injectionString}
if (!this._injectedStringsFrag[this._uniforms[i]._structName].includes(stringToInsert)) //
{
const insertionIndexFrag = structStrFrag.lastIndexOf(injectionString);
structStrFrag =
structStrFrag.slice(0, insertionIndexFrag)
+ stringToInsert + structStrFrag.slice(insertionIndexFrag - 1);
this._injectedStringsFrag[this._uniforms[i]._structName].push(stringToInsert);
}
if (!this._structUniformNamesIndicesFrag.includes(i)) this._structUniformNamesIndicesFrag.push(i);
}
else if (this._uniforms[i].getShaderType() === "vert")
{
// * inject member before {injectionString}
if (!this._injectedStringsVert[this._uniforms[i]._structName].includes(stringToInsert))
{
const insertionIndexVert = structStrVert.lastIndexOf(injectionString);
structStrVert =
structStrVert.slice(0, insertionIndexVert)
+ stringToInsert + structStrVert.slice(insertionIndexVert - 1);
this._injectedStringsVert[this._uniforms[i]._structName].push(stringToInsert);
}
if (!this._structUniformNamesIndicesVert.includes(i)) this._structUniformNamesIndicesVert.push(i);
}
}
}
// * dedupe injected uni declarations
this._uniDeclarationsFrag = [];
this._uniDeclarationsVert = [];
// * remove struct injection points and add uniform in fragment
for (let i = 0; i < this._structUniformNamesIndicesFrag.length; i += 1)
{
const index = this._structUniformNamesIndicesFrag[i];
const uniDeclarationString = "UNI " + this._uniforms[index]._structName + " " + this._uniforms[index]._structUniformName + ";".endl();
if (!this._uniDeclarationsFrag.includes(uniDeclarationString))
{
const injectionString = "{{INJECTION_POINT_STRUCT_" + this._uniforms[index]._structName + "}}";
structStrFrag = structStrFrag.replace(injectionString, "");
structStrFrag += uniDeclarationString;
this._uniDeclarationsFrag.push(uniDeclarationString);
}
}
// * remove struct injection points and add uniform in vertex
for (let i = 0; i < this._structUniformNamesIndicesVert.length; i += 1)
{
const index = this._structUniformNamesIndicesVert[i];
const uniDeclarationString = "UNI " + this._uniforms[index]._structName + " " + this._uniforms[index]._structUniformName + ";".endl();
if (!this._uniDeclarationsVert.includes(uniDeclarationString))
{
const injectionString = "{{INJECTION_POINT_STRUCT_" + this._uniforms[index]._structName + "}}";
structStrVert = structStrVert.replace(injectionString, "");
structStrVert += uniDeclarationString;
this._uniDeclarationsVert.push(uniDeclarationString);
}
}
return [structStrVert, structStrFrag];
};
_getAttrSrc(attr, firstLevel)
{
const r = {};
if (attr.name && attr.type)
{
r.srcHeadVert = "";
if (!firstLevel) r.srcHeadVert += "#ifndef ATTRIB_" + attr.name.endl();
r.srcHeadVert += "#define ATTRIB_" + attr.name.endl();
r.srcHeadVert += "IN " + attr.type + " " + attr.name + ";".endl();
if (!firstLevel) r.srcHeadVert += "#endif".endl();
if (attr.nameFrag)
{
r.srcHeadVert += "";
if (!firstLevel) r.srcHeadVert += "#ifndef ATTRIB_" + attr.nameFrag.endl();
r.srcHeadVert += "#define ATTRIB_" + attr.nameFrag.endl();
r.srcHeadVert += "OUT " + attr.type + " " + attr.nameFrag + ";".endl();
if (!firstLevel) r.srcHeadVert += "#endif".endl();
r.srcVert = "".endl() + attr.nameFrag + "=" + attr.name + ";";
r.srcHeadFrag = "";
if (!firstLevel) r.srcHeadFrag += "#ifndef ATTRIB_" + attr.nameFrag.endl();
r.srcHeadFrag += "#define ATTRIB_" + attr.nameFrag.endl();
r.srcHeadFrag += "IN " + attr.type + " " + attr.nameFrag + ";".endl();
if (!firstLevel) r.srcHeadFrag += "#endif".endl();
}
}
return r;
};
compile()
{
if (this._cgl.aborted) return;
const startTime = performance.now();
this._cgl.profileData.profileShaderCompiles++;
this._cgl.profileData.profileShaderCompileName = this._name + " [" + this._compileReason + "]";
let extensionString = "";
if (this._extensions)
for (let i = 0; i < this._extensions.length; i++)
extensionString += "#extension " + this._extensions[i] + " : enable".endl();
let definesStr = "";
if (this._defines.length) definesStr = "\n// cgl generated".endl();
for (let i = 0; i < this._defines.length; i++)
definesStr += "#define " + this._defines[i][0] + " " + this._defines[i][1] + "".endl();
const structStrings = this.createStructUniforms();
this._cgl.profileData.addHeavyEvent("shader compile", this._name + " [" + this._compileReason + "]");
this._compileReason = "";
if (this._uniforms)
{
// * we create an array of the uniform names to check our indices & an array to save them
const uniNames = this._uniforms.map((uni) => { return uni._name; });
const indicesToRemove = [];
// * we go through our uniforms and check if the same name is contained somewhere further in the array
// * if so, we add the current index to be removed later
for (let i = 0; i < this._uniforms.length; i++)
{
const uni = this._uniforms[i];
const nextIndex = uniNames.indexOf(uni._name, i + 1);
if (nextIndex > -1) indicesToRemove.push(i);
}
// * after that, we go through the uniforms backwards (so we keep the order) and remove the indices
// * also, we reset the locations of all the other valid uniforms
for (let j = this._uniforms.length - 1; j >= 0; j -= 1)
{
if (indicesToRemove.includes(j)) this._uniforms.splice(j, 1);
else this._uniforms[j].resetLoc();
}
}
this._cgl.printError("uniform resets");
if (this.hasTextureUniforms()) definesStr += "#define HAS_TEXTURES".endl();
let vs = "";
let fs = "";
if (!this.srcFrag)
{
this._log.warn("[cgl shader] has no fragment source!", this._name, this);
this.srcVert = getDefaultVertexShader();
this.srcFrag = getDefaultFragmentShader();
// return;
}
if (this.glslVersion == 300)
{
vs = "#version 300 es"
.endl() + "// "
.endl() + "// vertex shader " + this._name
.endl() + "// "
.endl() + "precision " + this.precision + " float;"
.endl() + "precision " + this.precision + " sampler2D;"
.endl() + ""
.endl() + "#define WEBGL2"
.endl() + "#define texture2D texture"
.endl() + "#define UNI uniform"
.endl() + "#define IN in"
.endl() + "#define OUT out"
.endl();
fs = "#version 300 es"
.endl() + "// "
.endl() + "// fragment shader " + this._name
.endl() + "// "
.endl() + "precision " + this.precision + " float;"
.endl() + "precision " + this.precision + " sampler2D;"
.endl() + ""
.endl() + "#define WEBGL2"
.endl() + "#define texture2D texture"
.endl() + "#define IN in"
.endl() + "#define OUT out"
.endl() + "#define UNI uniform"
.endl() + "{{DRAWBUFFER}}"
.endl();
}
else
{
fs = ""
.endl() + "// "
.endl() + "// fragment shader " + this._name
.endl() + "// "
.endl() + "#define WEBGL1"
.endl() + "#define texture texture2D"
.endl() + "#define outColor gl_FragColor"
.endl() + "#define IN varying"
.endl() + "#define UNI uniform"
.endl();
vs = ""
.endl() + "// "
.endl() + "// vertex shader " + this._name
.endl() + "// "
.endl() + "#define WEBGL1"
.endl() + "#define texture texture2D"
.endl() + "#define OUT varying"
.endl() + "#define IN attribute"
.endl() + "#define UNI uniform"
.endl();
}
let uniformsStrVert = "\n// cgl generated".endl();
let uniformsStrFrag = "\n// cgl generated".endl();
fs += "\n// active mods: --------------- ";
vs += "\n// active mods: --------------- ";
let foundModsFrag = false;
let foundModsVert = false;
for (let i = 0; i < this._moduleNames.length; i++)
{
for (let j = 0; j < this._modules.length; j++)
{
if (this._modules[j].name == this._moduleNames[i])
{
if (this._modules[j].srcBodyFrag || this._modules[j].srcHeadFrag)
{
foundModsFrag = true;
fs += "\n// " + i + "." + j + ". " + this._modules[j].title + " (" + this._modules[j].name + ")";
}
if (this._modules[j].srcBodyVert || this._modules[j].srcHeadVert)
{
vs += "\n// " + i + "." + j + ". " + this._modules[j].title + " (" + this._modules[j].name + ")";
foundModsVert = true;
}
}
}
}
if (!foundModsVert)fs += "\n// no mods used...";
if (!foundModsFrag)fs += "\n// no mods used...";
fs += "\n";
vs += "\n";
for (let i = 0; i < this._uniforms.length; i++)
{
if (this._uniforms[i].shaderType && !this._uniforms[i].isStructMember())
{
let uniStr = "";
if (!this._uniforms[i].getGlslTypeString())uniStr += "// ";
uniStr += "UNI " + this._uniforms[i].getGlslTypeString() + " " + this._uniforms[i].getName();
let comment = "";
if (this._uniforms[i].comment) comment = " // " + this._uniforms[i].comment;
if (this._uniforms[i].shaderType == "vert" || this._uniforms[i].shaderType == "both")
if (!this.srcVert.contains(uniStr) && !this.srcVert.contains("uniform " + this._uniforms[i].getGlslTypeString() + " " + this._uniforms[i].getName()))
uniformsStrVert += uniStr + ";" + comment.endl();
if (this._uniforms[i].shaderType == "frag" || this._uniforms[i].shaderType == "both")
if (!this.srcFrag.contains(uniStr) && !this.srcFrag.contains("uniform " + this._uniforms[i].getGlslTypeString() + " " + this._uniforms[i].getName()))
uniformsStrFrag += uniStr + ";" + comment.endl();
}
}
let countUniFrag = 0;
let countUniVert = 0;
for (let i = 0; i < this._uniforms.length; i++)
{
if (this._uniforms[i].shaderType && !this._uniforms[i].isStructMember())
{
if (this._uniforms[i].shaderType == "vert" || this._uniforms[i].shaderType == "both") countUniVert++;
if (this._uniforms[i].shaderType == "frag" || this._uniforms[i].shaderType == "both") countUniFrag++;
}
}
if (countUniFrag >= this._cgl.maxUniformsFrag) this._log.warn("[cgl_shader] num uniforms frag: " + countUniFrag + " / " + this._cgl.maxUniformsFrag);
if (countUniVert >= this._cgl.maxUniformsVert) this._log.warn("[cgl_shader] num uniforms vert: " + countUniVert + " / " + this._cgl.maxUniformsVert);
if (!fs.contains("precision")) fs = "precision " + this.precision + " float;".endl() + fs;
if (!vs.contains("precision")) vs = "precision " + this.precision + " float;".endl() + vs;
if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent))
{
fs += "#define MOBILE".endl();
vs += "#define MOBILE".endl();
}
vs = extensionString + vs + definesStr + structStrings[0] + uniformsStrVert + "\n// -- \n" + this.srcVert;
fs = extensionString + fs + definesStr + structStrings[1] + uniformsStrFrag + "\n// -- \n" + this.srcFrag;
let srcHeadVert = "";
let srcHeadFrag = "";
this._modules.sort(function (a, b)
{
return a.group - b.group;
});
this._modules.sort(function (a, b)
{
return a.priority || 0 - b.priority || 0;
});
let addedAttribs = false;
for (let i = 0; i < this._moduleNames.length; i++)
{
let srcVert = "";
let srcFrag = "";
if (!addedAttribs)
{
addedAttribs = true;
for (let k = 0; k < this._attributes.length; k++)
{
const r = this._getAttrSrc(this._attributes[k], true);
if (r.srcHeadVert)srcHeadVert += r.srcHeadVert;
if (r.srcVert)srcVert += r.srcVert;
if (r.srcHeadFrag)srcHeadFrag += r.srcHeadFrag;
}
}
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";
srcHeadFrag += "\n//---- MOD: group:" + mod.group + ": idx:" + j + " - prfx:" + mod.prefix + " - " + mod.title + " ------\n";
srcVert += "\n\n//---- MOD: " + mod.title + " / " + mod.priority + " ------\n";
srcFrag += "\n\n//---- MOD: " + mod.title + " / " + mod.priority + " ------\n";
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;
if (r.srcHeadFrag)srcHeadFrag += r.srcHeadFrag;
}
srcHeadVert += mod.srcHeadVert || "";
srcHeadFrag += mod.srcHeadFrag || "";
srcVert += mod.srcBodyVert || "";
srcFrag += mod.srcBodyFrag || "";
srcHeadVert += "\n//---- end mod ------\n";
srcHeadFrag += "\n//---- end mod ------\n";
srcVert += "\n//---- end mod ------\n";
srcFrag += "\n//---- end mod ------\n";
srcVert = srcVert.replace(/{{mod}}/g, mod.prefix);
srcFrag = srcFrag.replace(/{{mod}}/g, mod.prefix);
srcHeadVert = srcHeadVert.replace(/{{mod}}/g, mod.prefix);
srcHeadFrag = srcHeadFrag.replace(/{{mod}}/g, mod.prefix);
srcVert = srcVert.replace(/MOD_/g, mod.prefix);
srcFrag = srcFrag.replace(/MOD_/g, mod.prefix);
srcHeadVert = srcHeadVert.replace(/MOD_/g, mod.prefix);
srcHeadFrag = srcHeadFrag.replace(/MOD_/g, mod.prefix);
}
}
vs = vs.replace("{{" + this._moduleNames[i] + "}}", srcVert);
fs = fs.replace("{{" + this._moduleNames[i] + "}}", srcFrag);
}
vs = vs.replace("{{MODULES_HEAD}}", srcHeadVert);
fs = fs.replace("{{MODULES_HEAD}}", srcHeadFrag);
vs = this._addLibs(vs);
fs = this._addLibs(fs);
// SETUP draw buffers / multi texture render targets
let drawBufferStr = "";
for (let i = 0; i < 16; i++)
if (fs.contains("outColor" + i)) this._drawBuffers[i] = true;
if (this._drawBuffers.length == 1)
{
drawBufferStr = "out vec4 outColor;".endl();
drawBufferStr += "#define gl_FragColor outColor".endl();
}
else
{
drawBufferStr += "#define MULTI_COLORTARGETS".endl();
drawBufferStr += "vec4 outColor;".endl();
let count = 0;
for (let i = 0; i < this._drawBuffers.length; i++)
{
if (count == 0) drawBufferStr += "#define gl_FragColor outColor" + i + "".endl();
drawBufferStr += "layout(location = " + i + ") out vec4 outColor" + i + ";".endl();
count++;
}
}
fs = fs.replace("{{DRAWBUFFER}}", drawBufferStr);
// //////
if (!this._program)
{
this._program = this._createProgram(vs, fs);
}
else
{
// this.vshader=createShader(vs, gl.VERTEX_SHADER, this.vshader );
// this.fshader=createShader(fs, gl.FRAGMENT_SHADER, this.fshader );
// linkProgram(program);
this._program = this._createProgram(vs, fs);
this._projMatrixUniform = null;
for (let i = 0; i < this._uniforms.length; i++) this._uniforms[i].resetLoc();
}
this.finalShaderFrag = fs;
this.finalShaderVert = vs;
MESH.lastMesh = null;
MESH.lastShader = null;
this._countMissingUniforms = 0;
this._needsRecompile = false;
this.lastCompile = now();
// this._cgl.printError("shader compile");
this._cgl.profileData.shaderCompileTime += performance.now() - startTime;
};
hasChanged()
{
return this._needsRecompile;
}
bind()
{
if (!this._isValid || this._cgl.aborted) return;
MESH.lastShader = this;
if (!this._program || this._needsRecompile) this.compile();
if (!this._isValid) return;
if (!this._projMatrixUniform && !this.ignoreMissingUniforms)
{
this._countMissingUniforms++;
// if (this._countMissingUniforms == 10)console.log("stopping getlocation of missing uniforms...", this._name);
if (this._countMissingUniforms < 10)
{
this._projMatrixUniform = this._cgl.gl.getUniformLocation(this._program, CONSTANTS.SHADER.SHADERVAR_UNI_PROJMAT);
this._attrVertexPos = this._cgl.glGetAttribLocation(this._program, CONSTANTS.SHADER.SHADERVAR_VERTEX_POSITION);
this._mvMatrixUniform = this._cgl.gl.getUniformLocation(this._program, "mvMatrix");
this._vMatrixUniform = this._cgl.gl.getUniformLocation(this._program, CONSTANTS.SHADER.SHADERVAR_UNI_VIEWMAT);
this._mMatrixUniform = this._cgl.gl.getUniformLocation(this._program, CONSTANTS.SHADER.SHADERVAR_UNI_MODELMAT);
this._camPosUniform = this._cgl.gl.getUniformLocation(this._program, CONSTANTS.SHADER.SHADERVAR_UNI_VIEWPOS);
this._normalMatrixUniform = this._cgl.gl.getUniformLocation(this._program, CONSTANTS.SHADER.SHADERVAR_UNI_NORMALMAT);
this._inverseViewMatrixUniform = this._cgl.gl.getUniformLocation(this._program, CONSTANTS.SHADER.SHADERVAR_UNI_INVVIEWMAT);
this._inverseProjMatrixUniform = this._cgl.gl.getUniformLocation(this._program, CONSTANTS.SHADER.SHADERVAR_UNI_INVPROJMAT);
this._materialIdUniform = this._cgl.gl.getUniformLocation(this._program, CONSTANTS.SHADER.SHADERVAR_UNI_MATERIALID);
this._objectIdUniform = this._cgl.gl.getUniformLocation(this._program, CONSTANTS.SHADER.SHADERVAR_UNI_OBJECTID);
for (let i = 0; i < this._uniforms.length; i++) this._uniforms[i].needsUpdate = true;
}
}
if (this._cgl.currentProgram != this._program)
{
this._cgl.profileData.profileShaderBinds++;
this._cgl.gl.useProgram(this._program);
this._cgl.currentProgram = this._program;
}
for (let i = 0; i < this._uniforms.length; i++)
if (this._uniforms[i].needsUpdate) this._uniforms[i].updateValue();
if (this._pMatrixState != this._cgl.getProjectionMatrixStateCount())
{
this._pMatrixState = this._cgl.getProjectionMatrixStateCount();
this._cgl.gl.uniformMatrix4fv(this._projMatrixUniform, false, this._cgl.pMatrix);
this._cgl.profileData.profileMVPMatrixCount++;
}
if (this._objectIdUniform)
this._cgl.gl.uniform1f(this._objectIdUniform, ++this._cgl.frameStore.objectIdCounter);
if (this._materialIdUniform)
this._cgl.gl.uniform1f(this._materialIdUniform, this._materialId);
if (this._vMatrixUniform)
{
if (this._vMatrixState != this._cgl.getViewMatrixStateCount())
{
this._cgl.gl.uniformMatrix4fv(this._vMatrixUniform, false, this._cgl.vMatrix);
this._cgl.profileData.profileMVPMatrixCount++;
this._vMatrixState = this._cgl.getViewMatrixStateCount();
if (this._inverseViewMatrixUniform)
{
mat4.invert(this._tempInverseViewMatrix, this._cgl.vMatrix);
this._cgl.gl.uniformMatrix4fv(this._inverseViewMatrixUniform, false, this._tempInverseViewMatrix);
this._cgl.profileData.profileMVPMatrixCount++;
}
if (this._inverseProjMatrixUniform)
{
mat4.invert(this._tempInverseProjMatrix, this._cgl.pMatrix);
this._cgl.gl.uniformMatrix4fv(this._inverseProjMatrixUniform, false, this._tempInverseProjMatrix);
this._cgl.profileData.profileMVPMatrixCount++;
}
}
this._cgl.gl.uniformMatrix4fv(this._mMatrixUniform, false, this._cgl.mMatrix);
this._cgl.profileData.profileMVPMatrixCount++;
if (this._camPosUniform)
{
mat4.invert(this._tempCamPosMatrix, this._cgl.vMatrix);
this._cgl.gl.uniform3f(this._camPosUniform, this._tempCamPosMatrix[12], this._tempCamPosMatrix[13], this._tempCamPosMatrix[14]);
this._cgl.profileData.profileMVPMatrixCount++;
}
}
else
{
// mvmatrix deprecated....
const tempmv = mat4.create();
mat4.mul(tempmv, this._cgl.vMatrix, this._cgl.mMatrix);
this._cgl.gl.uniformMatrix4fv(this._mvMatrixUniform, false, tempmv);
this._cgl.profileData.profileMVPMatrixCount++;
}
if (this._normalMatrixUniform)
{
// mat4.mul(this._tempNormalMatrix, this._cgl.vMatrix, this._cgl.mMatrix);
mat4.invert(this._tempNormalMatrix, this._cgl.mMatrix);
mat4.transpose(this._tempNormalMatrix, this._tempNormalMatrix);
this._cgl.gl.uniformMatrix4fv(this._normalMatrixUniform, false, this._tempNormalMatrix);
this._cgl.profileData.profileMVPMatrixCount++;
}
for (let i = 0; i < this._libs.length; i++)
{
if (this._libs[i].onBind) this._libs[i].onBind.bind(this._libs[i])(this._cgl, this);
}
this._bindTextures();
return this._isValid;
};
unBind()
{
};
/**
* 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;
};
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;
};
dispose()
{
this._cgl.gl.deleteProgram(this._program);
};
needsRecompile()
{
return this._needsRecompile;
};
setDrawBuffers(arr)
{
console.log("useless drawbuffers...?!");
// if (this._drawBuffers.length !== arr.length)
// {
// this._drawBuffers = arr;
// this._needsRecompile = true;
// this.setWhyCompile("setDrawBuffers");
// return;
// }
// for (let i = 0; i < arr.length; i++)
// {
// if (arr[i] !== this._drawBuffers[i])
// {
// this._drawBuffers = arr;
// this._needsRecompile = true;
// this.setWhyCompile("setDrawBuffers");
// return;
// }
// }
};
getUniforms()
{
return this._uniforms;
};
getUniform(name)
{
for (let i = 0; i < this._uniforms.length; i++)
if (this._uniforms[i].getName() == name)
return this._uniforms[i];
return null;
};
removeAllUniforms()
{
this._uniforms = [];
// for (let i = 0; i < this._uniforms.length; i++)
// this.removeUniform(this._uniforms[i].name);
};
removeUniform(name)
{
for (let i = 0; i < this._uniforms.length; i++)
{
if (this._uniforms[i].getName() == name)
{
this._uniforms.splice(i, 1);
}
}
this._needsRecompile = true;
this.setWhyCompile("remove uniform " + name);
};
_addUniform(uni)
{
this._uniforms.push(uni);
this.setWhyCompile("add uniform " + name);
this._needsRecompile = true;
};
/**
* add a uniform to the fragment shader
* @param {String} type ['f','t', etc]
* @param {String} name
* @param {any} valueOrPort value or port
* @param p2
* @param p3
* @param p4
* @memberof Shader
* @instance
* @function addUniformFrag
* @returns {CGL.Uniform}
*/
addUniformFrag(type, name, valueOrPort, p2, p3, p4)
{
const uni = new CGL.Uniform(this, type, name, valueOrPort, p2, p3, p4);
uni.shaderType = "frag";
return uni;
};
/**
* add a uniform to the vertex shader
* @param {String} type ['f','t', etc]
* @param {String} name
* @param {any} valueOrPort value or port
* @param p2
* @param p3
* @param p4
* @memberof Shader
* @instance
* @function addUniformVert
* @returns {CGL.Uniform}
*/
addUniformVert(type, name, valueOrPort, p2, p3, p4)
{
const uni = new CGL.Uniform(this, type, name, valueOrPort, p2, p3, p4);
uni.shaderType = "vert";
return uni;
};
/**
* add a uniform to both shaders
* @param {String} type ['f','t', etc]
* @param {String} name
* @param {any} valueOrPort value or port
* @param p2
* @param p3
* @param p4
* @memberof Shader
* @instance
* @function addUniformBoth
* @returns {CGL.Uniform}
*/
addUniformBoth(type, name, valueOrPort, p2, p3, p4)
{
const uni = new CGL.Uniform(this, type, name, valueOrPort, p2, p3, p4);
uni.shaderType = "both";
return uni;
};
/**
* add a struct & its uniforms to the fragment shader
* @param {String} structName name of the struct, i.e.: LightStruct
* @param {String} uniformName name of the struct uniform in the shader, i.e.: lightUni
* @param {Array} members array of objects containing the struct members. see example for structure
* @memberof Shader
* @instance
* @function addUniformStructFrag
* @returns {Object}
* @example
* const shader = new CGL.Shader(cgl, 'MinimalMaterial');
* shader.setSource(attachments.shader_vert, attachments.shader_frag);
* shader.addUniformStructFrag("Light", "uniformLight", [
* { "type": "3f", "name": "position", "v1": null },
* { "type": "4f", "name": "color", "v1": inR, v2: inG, v3: inB, v4: inAlpha }
* ]);
*/
addUniformStructFrag(structName, uniformName, members)
{
const uniforms = {};
if (!members) return uniforms;
for (let i = 0; i < members.length; i += 1)
{
const member = members[i];
if (!this.hasUniform(uniformName + "." + member.name))
{
const uni = new CGL.Uniform(this, member.type, uniformName + "." + member.name, member.v1, member.v2, member.v3, member.v4, uniformName, structName, member.name);
uni.shaderType = "frag";
uniforms[uniformName + "." + member.name] = uni;
}
}
return uniforms;
};
/**
* add a struct & its uniforms to the vertex shader
* @param {String} structName name of the struct, i.e.: LightStruct
* @param {String} uniformName name of the struct uniform in the shader, i.e.: lightUni
* @param {Array} members array of objects containing the struct members. see example for structure
* @memberof Shader
* @instance
* @function addUniformStructVert
* @returns {CGL.Uniform}
* @example
* const shader = new CGL.Shader(cgl, 'MinimalMaterial');
* shader.setSource(attachments.shader_vert, attachments.shader_frag);
* shader.addUniformStructVert("Light", "uniformLight", [
* { "type": "3f", "name": "position", "v1": null },
* { "type": "4f", "name": "color", "v1": inR, v2: inG, v3: inB, v4: inAlpha }
* ]);
*/
addUniformStructVert(structName, uniformName, members)
{
const uniforms = {};
if (!members) return uniforms;
for (let i = 0; i < members.length; i += 1)
{
const member = members[i];
if (!this.hasUniform(uniformName + "." + member.name))
{
const uni = new CGL.Uniform(this, member.type, uniformName + "." + member.name, member.v1, member.v2, member.v3, member.v4, uniformName, structName, member.name);
uni.shaderType = "vert";
uniforms[uniformName + "." + member.name] = uni;
}
}
return uniforms;
};
/**
* add a struct & its uniforms to the both shaders. PLEASE NOTE: it is not possible to add the same struct to both shaders when it contains ANY integer members.
* @param {String} structName name of the struct, i.e.: LightStruct
* @param {String} uniformName name of the struct uniform in the shader, i.e.: lightUni
* @param {Array} members array of objects containing the struct members. see example for structure
* @memberof Shader
* @instance
* @function addUniformStructBoth
* @returns {Object}
* @example
* const shader = new CGL.Shader(cgl, 'MinimalMaterial');
* shader.setSource(attachments.shader_vert, attachments.shader_frag);
* shader.addUniformStructBoth("Light", "uniformLight", [
* { "type": "3f", "name": "position", "v1": null },
* { "type": "4f", "name": "color", "v1": inR, v2: inG, v3: inB, v4: inAlpha }
* ]);
*/
addUniformStructBoth(structName, uniformName, members)
{
const uniforms = {};
if (!members) return uniforms;
for (let i = 0; i < members.length; i += 1)
{
const member = members[i];
if ((member.type === "2i" || member.type === "i" || member.type === "3i"))
this._log.error("Adding an integer struct member to both shaders can potentially error. Please use different structs for each shader. Error occured in struct:", structName, " with member:", member.name, " of type:", member.type, ".");
if (!this.hasUniform(uniformName + "." + member.name))
{
const uni = new CGL.Uniform(this, member.type, uniformName + "." + member.name, member.v1, member.v2, member.v3, member.v4, uniformName, structName, member.name);
uni.shaderType = "both";
uniforms[uniformName + "." + member.name] = uni;
}
}
return uniforms;
};
hasUniform(name)
{
for (let i = 0; i < this._uniforms.length; i++)
{
if (this._uniforms[i].getName() == name) return true;
}
return false;
};
_createProgram(vstr, fstr)
{
this._cgl.printError("before _createprogram");
const program = this._cgl.gl.createProgram();
this.vshader = Shader.createShader(this._cgl, vstr, this._cgl.gl.VERTEX_SHADER, this);
this.fshader = Shader.createShader(this._cgl, fstr, this._cgl.gl.FRAGMENT_SHADER, this);
if (this.vshader && this.fshader)
{
this._cgl.gl.attachShader(program, this.vshader);
this._cgl.gl.attachShader(program, this.fshader);
this._linkProgram(program, vstr, fstr);
}
else
{
this._isValid = false;
this._cgl.printError("shader _createProgram");
this._log.error("could not link shaderprogram");
return null;
}
this._cgl.printError("shader _createProgram");
return program;
};
hasErrors()
{
return this._hasErrors;
};
_linkProgram(program, vstr, fstr)
{
this._cgl.printError("before _linkprogram");
if (this._feedBackNames.length > 0)
{
this._cgl.gl.transformFeedbackVaryings(program, this._feedBackNames, this._cgl.gl.SEPARATE_ATTRIBS);
// INTERLEAVED_ATTRIBS
// SEPARATE_ATTRIBS
}
this._cgl.gl.linkProgram(program);
this._cgl.printError("gl.linkprogram");
this._isValid = true;
this._hasErrors = false;
if (this._cgl.patch.config.glValidateShader !== false)
{
this._cgl.gl.validateProgram(program);
if (!this._cgl.gl.getProgramParameter(program, this._cgl.gl.VALIDATE_STATUS))
{
// validation failed
console.log("shaderprogram validation failed...");
console.log(this._name + " programinfo: ", this._cgl.gl.getProgramInfoLog(program));
}
if (!this._cgl.gl.getProgramParameter(program, this._cgl.gl.LINK_STATUS))
{
this._hasErrors = true;
const infoLogFrag = this._cgl.gl.getShaderInfoLog(this.fshader);
const infoLogVert = this._cgl.gl.getShaderInfoLog(this.vshader);
if (infoLogFrag) this._log.warn(this._cgl.gl.getShaderInfoLog(this.fshader));
if (infoLogVert) this._log.warn(this._cgl.gl.getShaderInfoLog(this.vshader));
this._log.error(this._name + " shader linking fail...");
console.log(this._name + " programinfo: ", this._cgl.gl.getProgramInfoLog(program));
console.log(this);
this._isValid = false;
this._name = "errorshader";
this.setSource(Shader.getDefaultVertexShader(), Shader.getErrorFragmentShader());
this._cgl.printError("shader link err");
}
}
};
getProgram()
{
return this._program;
};
setFeedbackNames(names)
{
this.setWhyCompile("setFeedbackNames");
this._needsRecompile = true;
this._feedBackNames = names;
};
// getDefaultVertexShader()
// {
// return defaultShaderSrcVert;
// }
// getDefaultFragmentShader()
// {
// return this.getDefaultFragmentShader()
// }
/**
* adds attribute definition to shader header without colliding with other shader modules...
* when attrFrag is defined, vertex shader will output this attribute to the fragment shader
* @function
* @memberof Shader
* @instance
* @param {Object} attr {type:x,name:x,[nameFrag:x]}
* @return {Object}
*/
addAttribute = function (attr)
{
for (let i = 0; i < this._attributes.length; i++)
{
if (this._attributes[i].name == attr.name && this._attributes[i].nameFrag == attr.nameFrag) return;
}
this._attributes.push(attr);
this._needsRecompile = true;
this.setWhyCompile("addAttribute");
};
bindTextures()
{
this._bindTextures();
}
_bindTextures()
{
if (this._textureStackTex.length > this._cgl.maxTextureUnits)
{
this._log.warn("[shader._bindTextures] too many textures bound", this._textureStackTex.length + "/" + this._cgl.maxTextureUnits);
}
// for (let i = this._textureStackTex.length + 1; i < this._cgl.maxTextureUnits; i++) this._cgl.setTexture(i, null);
for (let i = 0; i < this._textureStackTex.length; i++)
{
// console.log(this._textureStackTex.length, i);
if (!this._textureStackTex[i] && !this._textureStackTexCgl[i])
{
this._log.warn("no texture for pushtexture", this._name);
}
else
{
let t = this._textureStackTex[i];
if (this._textureStackTexCgl[i])
{
t = this._textureStackTexCgl[i].tex || CGL.Texture.getEmptyTexture(this._cgl).tex;
}
let bindOk = true;
if (!this._textureStackUni[i])
{
// throw(new Error('no uniform given to texturestack'));
this._log.warn("no uniform for pushtexture", this._name);
bindOk = this._cgl.setTexture(i, t, this._textureStackType[i]);
}
else
{
this._textureStackUni[i].setValue(i);
bindOk = this._cgl.setTexture(i, t, this._textureStackType[i]);
// console.log(bindOk, i, t, this._textureStackType[i]);
}
if (!bindOk) console.warn("tex bind failed", this.getName(), this._textureStackUni[i]);
}
}
};
setUniformTexture = function (uni, tex)
{
tex = tex || CGL.Texture.getTempTexture(this._cgl);
for (let i = 0; i < this._textureStackUni.length; i++)
if (this._textureStackUni[i] == uni)
{
const old = this._textureStackTex[i] || this._textureStackTexCgl[i];
if (tex.hasOwnProperty("tex"))
{
this._textureStackTexCgl[i] = tex;
this._textureStackTex[i] = null;
}
else
{
this._textureStackTexCgl[i] = null;
this._textureStackTex[i] = tex;
}
// this._textureStackTex[i] = tex;
// this._cgl.setTexture(i, tex, this._textureStackType[i]);
return old;
}
return null;
};
/**
* push a texture on the stack. those textures will be bound when binding the shader. texture slots are automatically set
* @param {uniform} uniform texture uniform
* @param {texture} t texture
* @param {type} type texture type, can be ignored when TEXTURE_2D
* @function pushTexture
* @memberof Shader
* @instance
*/
pushTexture = function (uniform, t, type)
{
if (!uniform)
{
console.log("no uniform given to texturestack", uniform);
return;
}
if (!t)
{
console.log("no tex...");
return;
}
if (!t.hasOwnProperty("tex") && !(t instanceof WebGLTexture))
{
this._log.warn(new Error("invalid texture").stack);
this._log.warn("[cgl_shader] invalid texture...", t);
return;
}
this._textureStackUni.push(uniform);
if (t.hasOwnProperty("tex"))
{
this._textureStackTexCgl.push(t);
this._textureStackTex.push(null);
}
else
{
this._textureStackTexCgl.push(null);
this._textureStackTex.push(t);
}
this._textureStackType.push(type);
};
/**
* pop last texture
* @function popTexture
* @memberof Shader
* @instance
*/
popTexture = function ()
{
this._textureStackUni.pop();
this._textureStackTex.pop();
this._textureStackTexCgl.pop();
this._textureStackType.pop();
};
/**
* pop all textures
* @function popTextures
* @memberof Shader
* @instance
*/
popTextures = function ()
{
this._textureStackTex.length =
this._textureStackTexCgl.length =
this._textureStackType.length =
this._textureStackUni.length = 0;
};
getMaterialId()
{
return this._materialId;
};
getInfo()
{
const info = {};
info.name = this._name;
// info.modules = JSON.parse(JSON.stringify(this._modules));
// info.defines = JSON.parse(JSON.stringify(this._defines));
info.defines = this.getDefines();
info.hasErrors = this.hasErrors();
return info;
};
getDefaultFragmentShader=function(r,g,b,a)
{
return getDefaultFragmentShader(r,g,b,a);
}
getDefaultVertexShader=function()
{
return getDefaultVertexShader();
}
}
// --------------------------
Shader.getDefaultVertexShader=getDefaultVertexShader;
Shader.getDefaultFragmentShader=getDefaultFragmentShader;
Shader.getErrorFragmentShader = function ()
{
return ""
.endl() + "void main()"
.endl() + "{"
.endl() + " float g=mod((gl_FragCoord.y+gl_FragCoord.x),50.0)/50.0;"
.endl() + " g= step(0.1,g);"
.endl() + " outColor = vec4( g+0.5, 0.0, 0.0, 1.0);"
.endl() + "}";
};
Shader.createShader = function (cgl, str, type, cglShader)
{
if (cgl.aborted) return;
// cgl.printError("[Shader.createShader] ", cglShader._name);
function getBadLines(infoLog)
{
const basLines = [];
const lines = infoLog.split("\n");
for (const i in lines)
{
const divide = lines[i].split(":");
if (parseInt(divide[2], 10)) basLines.push(parseInt(divide[2], 10));
}
return basLines;
}
const shader = cgl.gl.createShader(type);
cgl.gl.shaderSource(shader, str);
cgl.gl.compileShader(shader);
if (!cgl.gl.getShaderParameter(shader, cgl.gl.COMPILE_STATUS))
{
let infoLog = cgl.gl.getShaderInfoLog(shader);
if (!infoLog)
{
console.warn("empty shader info log", this._name);
return;
}
const badLines = getBadLines(infoLog);
let htmlWarning = "<pre style=\"margin-bottom:0px;\"><code class=\"shaderErrorCode language-glsl\" style=\"padding-bottom:0px;max-height: initial;max-width: initial;\">";
const lines = str.match(/^.*((\r\n|\n|\r)|$)/gm);
if (!cgl.aborted && infoLog)
{
if (type == cgl.gl.VERTEX_SHADER) console.log("VERTEX_SHADER");
if (type == cgl.gl.FRAGMENT_SHADER) console.log("FRAGMENT_SHADER");
for (const i in lines)
{
const j = parseInt(i, 10) + 1;
const line = j + ": " + lines[i];
let isBadLine = false;
for (const bj in badLines)
if (badLines[bj] == j) isBadLine = true;
if (isBadLine)
{
htmlWarning += "</code></pre>";
htmlWarning += "<pre style=\"margin:0\"><code class=\"language-glsl\" style=\"background-color:#660000;padding-top:0px;padding-bottom:0px\">";
cglShader._log.log("bad line: `" + line + "`");
}
htmlWarning += escapeHTML(line);
if (isBadLine)
{
htmlWarning += "</code></pre>";
htmlWarning += "<pre style=\"margin:0\"><code class=\"language-glsl\" style=\";padding-top:0px;padding-bottom:0px\">";
}
}
}
infoLog = infoLog.replace(/\n/g, "<br/>");
if (cgl.patch.isEditorMode())console.log("Shader error ", cglShader._name, infoLog, this);
htmlWarning = infoLog + "<br/>" + htmlWarning + "<br/><br/>";
htmlWarning += "</code></pre>";
if (this._fromUserInteraction)
{
// console.log("todo show modal?");
// cgl.patch.emitEvent("criticalError", { "title": "Shader error " + cglShader._name, "text": htmlWarning, "exception": { "message": infoLog } });
}
cglShader.setSource(Shader.getDefaultVertexShader(), Shader.getErrorFragmentShader());
}
else
{
// console.log(name+' shader compiled...');
}
// cgl.printError("shader create2");
return shader;
};
export { Shader };