cables_dev/cables/src/core/cgl/cgl_textureeffect.js
import { Logger } from "cables-shared-client";
import { Texture } from "./cgl_texture.js";
import { MESHES } from "./cgl_simplerect.js";
const TextureEffect = function (cgl, options)
{
this._cgl = cgl;
this._log = new Logger("cgl_TextureEffect");
if (!cgl.TextureEffectMesh) this.createMesh();
this._textureSource = null;
this._options = options;
this.name = options.name || "unknown";
// TODO: do we still need the options ?
// var opts=options ||
// {
// isFloatingPointTexture:false,
// filter:CGL.Texture.FILTER_LINEAR
// };
// if(options && options.fp)opts.isFloatingPointTexture=true;
this.imgCompVer = 0;
this.aspectRatio = 1;
this._textureTarget = null; // new CGL.Texture(this._cgl,opts);
this._frameBuf = this._cgl.gl.createFramebuffer();
this._frameBuf2 = this._cgl.gl.createFramebuffer();
this._renderbuffer = this._cgl.gl.createRenderbuffer();
this._renderbuffer2 = this._cgl.gl.createRenderbuffer();
this.switched = false;
this.depth = false;
};
TextureEffect.prototype.dispose = function ()
{
if (this._renderbuffer) this._cgl.gl.deleteRenderbuffer(this._renderbuffer);
if (this._frameBuf) this._cgl.gl.deleteFramebuffer(this._frameBuf);
if (this._renderbuffer2) this._cgl.gl.deleteRenderbuffer(this._renderbuffer2);
if (this._frameBuf2) this._cgl.gl.deleteFramebuffer(this._frameBuf2);
};
TextureEffect.prototype.getWidth = function ()
{
return this._textureSource.width;
};
TextureEffect.prototype.getHeight = function ()
{
return this._textureSource.height;
};
TextureEffect.prototype.setSourceTexture = function (tex)
{
if (tex === null)
{
this._textureSource = new Texture(this._cgl);
this._textureSource.setSize(16, 16);
}
else
{
this._textureSource = tex;
}
if (!this._textureSource.compareSettings(this._textureTarget))
{
if (this._textureTarget) this._textureTarget.delete();
this._textureTarget = this._textureSource.clone();
this._cgl.profileData.profileEffectBuffercreate++;
this._cgl.gl.bindFramebuffer(this._cgl.gl.FRAMEBUFFER, this._frameBuf);
this._cgl.gl.bindRenderbuffer(this._cgl.gl.RENDERBUFFER, this._renderbuffer);
// if(tex.textureType==CGL.Texture.TYPE_FLOAT) this._cgl.gl.renderbufferStorage(this._cgl.gl.RENDERBUFFER,this._cgl.gl.RGBA32F, this._textureSource.width,this._textureSource.height);
// else this._cgl.gl.renderbufferStorage(this._cgl.gl.RENDERBUFFER,this._cgl.gl.RGBA8, this._textureSource.width,this._textureSource.height);
if (this.depth) this._cgl.gl.renderbufferStorage(this._cgl.gl.RENDERBUFFER, this._cgl.gl.DEPTH_COMPONENT16, this._textureSource.width, this._textureSource.height);
this._cgl.gl.framebufferTexture2D(this._cgl.gl.FRAMEBUFFER, this._cgl.gl.COLOR_ATTACHMENT0, this._cgl.gl.TEXTURE_2D, this._textureTarget.tex, 0);
if (this.depth) this._cgl.gl.framebufferRenderbuffer(this._cgl.gl.FRAMEBUFFER, this._cgl.gl.DEPTH_ATTACHMENT, this._cgl.gl.RENDERBUFFER, this._renderbuffer);
// this._cgl.gl.framebufferTexture2D(this._cgl.gl.FRAMEBUFFER, this._cgl.gl.COLOR_ATTACHMENT0, this._cgl.gl.TEXTURE_2D, this._textureTarget.tex, 0);
this._cgl.gl.bindTexture(this._cgl.gl.TEXTURE_2D, null);
this._cgl.gl.bindRenderbuffer(this._cgl.gl.RENDERBUFFER, null);
this._cgl.gl.bindFramebuffer(this._cgl.gl.FRAMEBUFFER, null);
this._cgl.gl.bindFramebuffer(this._cgl.gl.FRAMEBUFFER, this._frameBuf2);
this._cgl.gl.bindRenderbuffer(this._cgl.gl.RENDERBUFFER, this._renderbuffer2);
// if(tex.textureType==CGL.Texture.TYPE_FLOAT) this._cgl.gl.renderbufferStorage(this._cgl.gl.RENDERBUFFER,this._cgl.gl.RGBA32F, this._textureSource.width,this._textureSource.height);
// else this._cgl.gl.renderbufferStorage(this._cgl.gl.RENDERBUFFER,this._cgl.gl.RGBA8, this._textureSource.width,this._textureSource.height);
if (this.depth) this._cgl.gl.renderbufferStorage(this._cgl.gl.RENDERBUFFER, this._cgl.gl.DEPTH_COMPONENT16, this._textureSource.width, this._textureSource.height);
this._cgl.gl.framebufferTexture2D(this._cgl.gl.FRAMEBUFFER, this._cgl.gl.COLOR_ATTACHMENT0, this._cgl.gl.TEXTURE_2D, this._textureSource.tex, 0);
if (this.depth) this._cgl.gl.framebufferRenderbuffer(this._cgl.gl.FRAMEBUFFER, this._cgl.gl.DEPTH_ATTACHMENT, this._cgl.gl.RENDERBUFFER, this._renderbuffer2);
// this._cgl.gl.framebufferTexture2D(this._cgl.gl.FRAMEBUFFER, this._cgl.gl.COLOR_ATTACHMENT0, this._cgl.gl.TEXTURE_2D, this._textureSource.tex, 0);
this._cgl.gl.bindTexture(this._cgl.gl.TEXTURE_2D, null);
this._cgl.gl.bindRenderbuffer(this._cgl.gl.RENDERBUFFER, null);
this._cgl.gl.bindFramebuffer(this._cgl.gl.FRAMEBUFFER, null);
}
this.aspectRatio = this._textureSource.width / this._textureSource.height;
};
TextureEffect.prototype.continueEffect = function ()
{
this._cgl.pushDepthTest(false);
this._cgl.pushModelMatrix();
this._cgl.pushPMatrix();
// todo why two pushs?
this._cgl.pushViewPort(0, 0, this.getCurrentTargetTexture().width, this.getCurrentTargetTexture().height);
mat4.perspective(this._cgl.pMatrix, 45, this.getCurrentTargetTexture().width / this.getCurrentTargetTexture().height, 0.1, 1100.0); // todo: why?
this._cgl.pushPMatrix();
mat4.identity(this._cgl.pMatrix);
this._cgl.pushViewMatrix();
mat4.identity(this._cgl.vMatrix);
this._cgl.pushModelMatrix();
mat4.identity(this._cgl.mMatrix);
};
TextureEffect.prototype.startEffect = function (bgTex)
{
if (!this._textureTarget)
{
this._log.warn("effect has no target");
return;
}
this.switched = false;
this.continueEffect();
if (bgTex)
{
this._bgTex = bgTex;
}
this._countEffects = 0;
};
TextureEffect.prototype.endEffect = function ()
{
this._cgl.popDepthTest();
this._cgl.popModelMatrix();
this._cgl.popPMatrix();
this._cgl.popModelMatrix();
this._cgl.popViewMatrix();
this._cgl.popPMatrix();
this._cgl.popViewPort();
};
TextureEffect.prototype.bind = function ()
{
if (this._textureSource === null)
{
this._log.warn("no base texture set!");
return;
}
if (!this.switched)
{
this._cgl.gl.bindFramebuffer(this._cgl.gl.FRAMEBUFFER, this._frameBuf);
this._cgl.pushGlFrameBuffer(this._frameBuf);
}
else
{
this._cgl.gl.bindFramebuffer(this._cgl.gl.FRAMEBUFFER, this._frameBuf2);
this._cgl.pushGlFrameBuffer(this._frameBuf2);
}
};
TextureEffect.prototype.finish = function ()
{
if (this._textureSource === null)
{
this._log.warn("no base texture set!");
return;
}
this._cgl.TextureEffectMesh.render(this._cgl.getShader());
this._cgl.gl.bindFramebuffer(this._cgl.gl.FRAMEBUFFER, this._cgl.popGlFrameBuffer());
this._cgl.profileData.profileTextureEffect++;
if (this._textureTarget.filter == Texture.FILTER_MIPMAP)
{
if (!this.switched)
{
this._cgl.gl.bindTexture(this._cgl.gl.TEXTURE_2D, this._textureTarget.tex);
this._textureTarget.updateMipMap();
}
else
{
this._cgl.gl.bindTexture(this._cgl.gl.TEXTURE_2D, this._textureSource.tex);
this._textureSource.updateMipMap();
}
this._cgl.gl.bindTexture(this._cgl.gl.TEXTURE_2D, null);
}
this.switched = !this.switched;
this._countEffects++;
};
TextureEffect.prototype.getCurrentTargetTexture = function ()
{
if (this.switched) return this._textureSource;
return this._textureTarget;
};
TextureEffect.prototype.getCurrentSourceTexture = function ()
{
if (this._countEffects == 0 && this._bgTex) return this._bgTex;
if (this.switched) return this._textureTarget;
return this._textureSource;
};
TextureEffect.prototype.delete = function ()
{
if (this._textureTarget) this._textureTarget.delete();
if (this._textureSource) this._textureSource.delete();
this._cgl.gl.deleteRenderbuffer(this._renderbuffer);
this._cgl.gl.deleteFramebuffer(this._frameBuf);
};
TextureEffect.prototype.createMesh = function ()
{
this._cgl.TextureEffectMesh = MESHES.getSimpleRect(this._cgl, "texEffectRect");
};
// ---------------------------------------------------------------------------------
TextureEffect.checkOpNotInTextureEffect = function (op)
{
if (!op.patch.cgl) return true;
if (op.uiAttribs.error && !op.patch.cgl.currentTextureEffect)
{
op.setUiError("textureeffect", null);
return true;
}
if (!op.patch.cgl.currentTextureEffect) return true;
if (op.patch.cgl.currentTextureEffect && !op.uiAttribs.error)
{
op.setUiError("textureeffect", "This op can not be a child of a ImageCompose/texture effect! imagecompose should only have textureeffect childs.", 0);
return false;
}
if (op.patch.cgl.currentTextureEffect) return false;
return true;
};
TextureEffect.checkOpInEffect = function (op, minver)
{
minver = minver || 0;
if (op.patch.cgl.currentTextureEffect)
{
if (op.uiAttribs.uierrors && op.patch.cgl.currentTextureEffect.imgCompVer >= minver)
{
op.setUiError("texeffect", null);
return true;
}
if (minver && op.patch.cgl.currentTextureEffect.imgCompVer < minver)
{
op.setUiError("texeffect", "This op must be a child of an ImageCompose op with version >=" + minver + " <span class=\"button-small\" onclick=\"gui.patchView.downGradeOp('" + op.id + "','" + op.name + "')\">Downgrade</span> to previous version", 1);
}
}
if (op.patch.cgl.currentTextureEffect) return true;
if (!op.patch.cgl.currentTextureEffect && (!op.uiAttribs.uierrors || op.uiAttribs.uierrors.length == 0))
{
op.setUiError("texeffect", "This op must be a child of an ImageCompose op! More infos <a href=\"https://docs.cables.gl/image_composition/image_composition.html\" target=\"_blank\">here</a>. ", 1);
return false;
}
if (!op.patch.cgl.currentTextureEffect) return false;
return true;
};
TextureEffect.getBlendCode = function (ver)
{
let src = "".endl()
+ "vec3 _blend(vec3 base,vec3 blend)".endl()
+ "{".endl()
+ " vec3 colNew=blend;".endl()
+ " #ifdef BM_MULTIPLY".endl()
+ " colNew=base*blend;".endl()
+ " #endif".endl()
+ " #ifdef BM_MULTIPLY_INV".endl()
+ " colNew=base* vec3(1.0)-blend;".endl()
+ " #endif".endl()
+ " #ifdef BM_AVERAGE".endl()
+ " colNew=((base + blend) / 2.0);".endl()
+ " #endif".endl()
+ " #ifdef BM_ADD".endl()
+ " colNew=min(base + blend, vec3(1.0));".endl()
+ " #endif".endl()
+ " #ifdef BM_SUBTRACT_ONE".endl()
+ " colNew=max(base + blend - vec3(1.0), vec3(0.0));".endl()
+ " #endif".endl()
+ " #ifdef BM_SUBTRACT".endl()
+ " colNew=base - blend;".endl()
+ " #endif".endl()
+ " #ifdef BM_DIFFERENCE".endl()
+ " colNew=abs(base - blend);".endl()
+ " #endif".endl()
+ " #ifdef BM_NEGATION".endl()
+ " colNew=(vec3(1.0) - abs(vec3(1.0) - base - blend));".endl()
+ " #endif".endl()
+ " #ifdef BM_EXCLUSION".endl()
+ " colNew=(base + blend - 2.0 * base * blend);".endl()
+ " #endif".endl()
+ " #ifdef BM_LIGHTEN".endl()
+ " colNew=max(blend, base);".endl()
+ " #endif".endl()
+ " #ifdef BM_DARKEN".endl()
+ " colNew=min(blend, base);".endl()
+ " #endif".endl()
+ " #ifdef BM_OVERLAY".endl()
+ " #define BlendOverlayf(base, blend) (base < 0.5 ? (2.0 * base * blend) : (1.0 - 2.0 * (1.0 - base) * (1.0 - blend)))"
// .endl()+' #define BlendOverlay(base, blend) Blend(base, blend, BlendOverlayf)'
// .endl()+' colNew=Blend(base, blend, BlendOverlayf);'
.endl()
+ " colNew=vec3(BlendOverlayf(base.r, blend.r),BlendOverlayf(base.g, blend.g),BlendOverlayf(base.b, blend.b));".endl()
+ " #endif".endl()
+ " #ifdef BM_SCREEN".endl()
+ " #define BlendScreenf(base, blend) (1.0 - ((1.0 - base) * (1.0 - blend)))"
// .endl()+' #define BlendScreen(base, blend) Blend(base, blend, BlendScreenf)'
// .endl()+' colNew=Blend(base, blend, BlendScreenf);'
.endl()
+ " colNew=vec3(BlendScreenf(base.r, blend.r),BlendScreenf(base.g, blend.g),BlendScreenf(base.b, blend.b));".endl()
+ " #endif".endl()
+ " #ifdef BM_SOFTLIGHT".endl()
+ " #define BlendSoftLightf(base, blend) ((blend < 0.5) ? (2.0 * base * blend + base * base * (1.0 - 2.0 * blend)) : (sqrt(base) * (2.0 * blend - 1.0) + 2.0 * base * (1.0 - blend)))"
// .endl()+' #define BlendSoftLight(base, blend) Blend(base, blend, BlendSoftLightf)'
// .endl()+' colNew=Blend(base, blend, BlendSoftLightf);'
.endl()
+ " colNew=vec3(BlendSoftLightf(base.r, blend.r),BlendSoftLightf(base.g, blend.g),BlendSoftLightf(base.b, blend.b));".endl()
+ " #endif".endl()
+ " #ifdef BM_HARDLIGHT".endl()
+ " #define BlendOverlayf(base, blend) (base < 0.5 ? (2.0 * base * blend) : (1.0 - 2.0 * (1.0 - base) * (1.0 - blend)))"
// .endl()+' #define BlendOverlay(base, blend) Blend(base, blend, BlendOverlayf)'
// .endl()+' colNew=Blend(blend, base, BlendOverlayf);'
.endl()
+ " colNew=vec3(BlendOverlayf(base.r, blend.r),BlendOverlayf(base.g, blend.g),BlendOverlayf(base.b, blend.b));".endl()
+ " #endif".endl()
+ " #ifdef BM_COLORDODGE".endl()
+ " #define BlendColorDodgef(base, blend) ((blend == 1.0) ? blend : min(base / (1.0 - blend), 1.0))"
// .endl()+' colNew=Blend(base, blend, BlendColorDodgef);'
.endl()
+ " colNew=vec3(BlendColorDodgef(base.r, blend.r),BlendColorDodgef(base.g, blend.g),BlendColorDodgef(base.b, blend.b));".endl()
+ " #endif".endl()
+ " #ifdef BM_COLORBURN".endl()
+ " #define BlendColorBurnf(base, blend) ((blend == 0.0) ? blend : max((1.0 - ((1.0 - base) / blend)), 0.0))"
// .endl()+' colNew=Blend(base, blend, BlendColorBurnf);'
.endl()
+ " colNew=vec3(BlendColorBurnf(base.r, blend.r),BlendColorBurnf(base.g, blend.g),BlendColorBurnf(base.b, blend.b));".endl()
+ " #endif".endl()
+ " return colNew;".endl()
+ "}".endl();
if (!ver)
src += "vec4 cgl_blend(vec4 oldColor,vec4 newColor,float amount)".endl()
+ "{".endl()
+ "vec4 col=vec4( _blend(oldColor.rgb,newColor.rgb) ,1.0);".endl()
+ "col=vec4( mix( col.rgb, oldColor.rgb ,1.0-oldColor.a*amount),1.0);".endl()
+ "return col;".endl()
+ "}".endl();
if (ver >= 3)
src += "vec4 cgl_blendPixel(vec4 base,vec4 col,float amount)".endl() +
"{".endl() +
"#ifdef BM_MATH_ADD".endl() +
" return vec4(base.rgb+col.rgb*amount,1.0);".endl() +
"#endif".endl() +
"#ifdef BM_MATH_SUB".endl() +
" return vec4(base.rgb-col.rgb*amount,1.0);".endl() +
"#endif".endl() +
"#ifdef BM_MATH_MUL".endl() +
" return vec4(base.rgb*col.rgb*amount,1.0);".endl() +
"#endif".endl() +
"#ifdef BM_MATH_DIV".endl() +
" return vec4(base.rgb/col.rgb*amount,1.0);".endl() +
"#endif".endl() +
"#ifndef BM_MATH".endl() +
"vec3 colNew=_blend(base.rgb,col.rgb);".endl() +
"float newA=clamp(base.a+(col.a*amount),0.,1.);".endl() +
"#ifdef BM_ALPHAMASKED".endl() +
"newA=base.a;".endl() +
"#endif".endl() +
"return vec4(".endl() +
"mix(colNew,base.rgb,1.0-(amount*col.a)),".endl() +
"newA);".endl() +
"#endif".endl() +
"}".endl();
return src;
};
TextureEffect.onChangeBlendSelect = function (shader, blendName, maskAlpha = false)
{
blendName = String(blendName);
shader.toggleDefine("BM_NORMAL", blendName == "normal");
shader.toggleDefine("BM_MULTIPLY", blendName == "multiply");
shader.toggleDefine("BM_MULTIPLY_INV", blendName == "multiply invert");
shader.toggleDefine("BM_AVERAGE", blendName == "average");
shader.toggleDefine("BM_ADD", blendName == "add");
shader.toggleDefine("BM_SUBTRACT_ONE", blendName == "subtract one");
shader.toggleDefine("BM_SUBTRACT", blendName == "subtract");
shader.toggleDefine("BM_DIFFERENCE", blendName == "difference");
shader.toggleDefine("BM_NEGATION", blendName == "negation");
shader.toggleDefine("BM_EXCLUSION", blendName == "exclusion");
shader.toggleDefine("BM_LIGHTEN", blendName == "lighten");
shader.toggleDefine("BM_DARKEN", blendName == "darken");
shader.toggleDefine("BM_OVERLAY", blendName == "overlay");
shader.toggleDefine("BM_SCREEN", blendName == "screen");
shader.toggleDefine("BM_SOFTLIGHT", blendName == "softlight");
shader.toggleDefine("BM_HARDLIGHT", blendName == "hardlight");
shader.toggleDefine("BM_COLORDODGE", blendName == "color dodge");
shader.toggleDefine("BM_COLORBURN", blendName == "color burn");
shader.toggleDefine("BM_MATH_ADD", blendName == "Math Add");
shader.toggleDefine("BM_MATH_SUB", blendName == "Math Subtract");
shader.toggleDefine("BM_MATH_MUL", blendName == "Math Multiply");
shader.toggleDefine("BM_MATH_DIV", blendName == "Math Divide");
shader.toggleDefine("BM_MATH", blendName.indexOf("Math ") == 0);
shader.toggleDefine("BM_ALPHAMASKED", maskAlpha);
};
TextureEffect.AddBlendSelect = function (op, name, defaultMode)
{
const p = op.inValueSelect(name || "Blend Mode", [
"normal", "lighten", "darken", "multiply", "multiply invert", "average", "add", "subtract", "difference", "negation", "exclusion", "overlay", "screen", "color dodge", "color burn", "softlight", "hardlight", "subtract one",
"Math Add",
"Math Subtract",
"Math Multiply",
"Math Divide",
], defaultMode || "normal");
return p;
};
TextureEffect.AddBlendAlphaMask = function (op, name, defaultMode)
{
const p = op.inSwitch(name || "Alpha Mask", ["Off", "On"], defaultMode || "Off");
return p;
};
TextureEffect.setupBlending = function (op, shader, blendPort, amountPort, alphaMaskPort)
{
const onChange = () =>
{
let maskAlpha = false;
if (alphaMaskPort) maskAlpha = alphaMaskPort.get() == "On";
TextureEffect.onChangeBlendSelect(shader, blendPort.get(), maskAlpha);
let str = blendPort.get();
if (str == "normal") str = null;
else if (str == "multiply") str = "mul";
else if (str == "multiply invert") str = "mulinv";
else if (str == "lighten") str = "light";
else if (str == "darken") str = "darken";
else if (str == "average") str = "avg";
else if (str == "subtract one") str = "sub one";
else if (str == "subtract") str = "sub";
else if (str == "difference") str = "diff";
else if (str == "negation") str = "neg";
else if (str == "exclusion") str = "exc";
else if (str == "overlay") str = "ovl";
else if (str == "color dodge") str = "dodge";
else if (str == "color burn") str = "burn";
else if (str == "softlight") str = "soft";
else if (str == "hardlight") str = "hard";
else if (str == "Math Add") str = "+";
else if (str == "Math Subtract") str = "-";
else if (str == "Math Multiply") str = "*";
else if (str == "Math Divide") str = "/";
op.setUiAttrib({ "extendTitle": str });
};
op.setPortGroup("Blending", [blendPort, amountPort, alphaMaskPort]);
let maskAlpha = false;
blendPort.onChange = onChange;
if (alphaMaskPort)
{
alphaMaskPort.onChange = onChange;
maskAlpha = alphaMaskPort.get() == "On";
}
TextureEffect.onChangeBlendSelect(shader, blendPort.get(), maskAlpha);
};
export { TextureEffect };