Files
ComfyUI_frontend/src/nodes/gltextures.js
2019-05-10 19:49:33 +02:00

4529 lines
147 KiB
JavaScript
Executable File

(function(global) {
var LiteGraph = global.LiteGraph;
//Works with Litegl.js to create WebGL nodes
global.LGraphTexture = null;
if (typeof GL != "undefined") {
LGraphCanvas.link_type_colors["Texture"] = "#987";
function LGraphTexture() {
this.addOutput("Texture", "Texture");
this.properties = { name: "", filter: true };
this.size = [
LGraphTexture.image_preview_size,
LGraphTexture.image_preview_size
];
}
global.LGraphTexture = LGraphTexture;
LGraphTexture.title = "Texture";
LGraphTexture.desc = "Texture";
LGraphTexture.widgets_info = {
name: { widget: "texture" },
filter: { widget: "checkbox" }
};
//REPLACE THIS TO INTEGRATE WITH YOUR FRAMEWORK
LGraphTexture.loadTextureCallback = null; //function in charge of loading textures when not present in the container
LGraphTexture.image_preview_size = 256;
//flags to choose output texture type
LGraphTexture.PASS_THROUGH = 1; //do not apply FX
LGraphTexture.COPY = 2; //create new texture with the same properties as the origin texture
LGraphTexture.LOW = 3; //create new texture with low precision (byte)
LGraphTexture.HIGH = 4; //create new texture with high precision (half-float)
LGraphTexture.REUSE = 5; //reuse input texture
LGraphTexture.DEFAULT = 2;
LGraphTexture.MODE_VALUES = {
"pass through": LGraphTexture.PASS_THROUGH,
copy: LGraphTexture.COPY,
low: LGraphTexture.LOW,
high: LGraphTexture.HIGH,
reuse: LGraphTexture.REUSE,
default: LGraphTexture.DEFAULT
};
//returns the container where all the loaded textures are stored (overwrite if you have a Resources Manager)
LGraphTexture.getTexturesContainer = function() {
return gl.textures;
};
//process the loading of a texture (overwrite it if you have a Resources Manager)
LGraphTexture.loadTexture = function(name, options) {
options = options || {};
var url = name;
if (url.substr(0, 7) == "http://") {
if (LiteGraph.proxy) {
//proxy external files
url = LiteGraph.proxy + url.substr(7);
}
}
var container = LGraphTexture.getTexturesContainer();
var tex = (container[name] = GL.Texture.fromURL(url, options));
return tex;
};
LGraphTexture.getTexture = function(name) {
var container = this.getTexturesContainer();
if (!container) {
throw "Cannot load texture, container of textures not found";
}
var tex = container[name];
if (!tex && name && name[0] != ":") {
return this.loadTexture(name);
}
return tex;
};
//used to compute the appropiate output texture
LGraphTexture.getTargetTexture = function(origin, target, mode) {
if (!origin) {
throw "LGraphTexture.getTargetTexture expects a reference texture";
}
var tex_type = null;
switch (mode) {
case LGraphTexture.LOW:
tex_type = gl.UNSIGNED_BYTE;
break;
case LGraphTexture.HIGH:
tex_type = gl.HIGH_PRECISION_FORMAT;
break;
case LGraphTexture.REUSE:
return origin;
break;
case LGraphTexture.COPY:
default:
tex_type = origin ? origin.type : gl.UNSIGNED_BYTE;
break;
}
if (
!target ||
target.width != origin.width ||
target.height != origin.height ||
target.type != tex_type
) {
target = new GL.Texture(origin.width, origin.height, {
type: tex_type,
format: gl.RGBA,
filter: gl.LINEAR
});
}
return target;
};
LGraphTexture.getTextureType = function(precision, ref_texture) {
var type = ref_texture ? ref_texture.type : gl.UNSIGNED_BYTE;
switch (precision) {
case LGraphTexture.HIGH:
type = gl.HIGH_PRECISION_FORMAT;
break;
case LGraphTexture.LOW:
type = gl.UNSIGNED_BYTE;
break;
//no default
}
return type;
};
LGraphTexture.getWhiteTexture = function() {
if (this._white_texture) {
return this._white_texture;
}
var texture = (this._white_texture = GL.Texture.fromMemory(
1,
1,
[255, 255, 255, 255],
{ format: gl.RGBA, wrap: gl.REPEAT, filter: gl.NEAREST }
));
return texture;
};
LGraphTexture.getNoiseTexture = function() {
if (this._noise_texture) {
return this._noise_texture;
}
var noise = new Uint8Array(512 * 512 * 4);
for (var i = 0; i < 512 * 512 * 4; ++i) {
noise[i] = Math.random() * 255;
}
var texture = GL.Texture.fromMemory(512, 512, noise, {
format: gl.RGBA,
wrap: gl.REPEAT,
filter: gl.NEAREST
});
this._noise_texture = texture;
return texture;
};
LGraphTexture.prototype.onDropFile = function(data, filename, file) {
if (!data) {
this._drop_texture = null;
this.properties.name = "";
} else {
var texture = null;
if (typeof data == "string") {
texture = GL.Texture.fromURL(data);
} else if (filename.toLowerCase().indexOf(".dds") != -1) {
texture = GL.Texture.fromDDSInMemory(data);
} else {
var blob = new Blob([file]);
var url = URL.createObjectURL(blob);
texture = GL.Texture.fromURL(url);
}
this._drop_texture = texture;
this.properties.name = filename;
}
};
LGraphTexture.prototype.getExtraMenuOptions = function(graphcanvas) {
var that = this;
if (!this._drop_texture) {
return;
}
return [
{
content: "Clear",
callback: function() {
that._drop_texture = null;
that.properties.name = "";
}
}
];
};
LGraphTexture.prototype.onExecute = function() {
var tex = null;
if (this.isOutputConnected(1)) {
tex = this.getInputData(0);
}
if (!tex && this._drop_texture) {
tex = this._drop_texture;
}
if (!tex && this.properties.name) {
tex = LGraphTexture.getTexture(this.properties.name);
}
if (!tex) {
return;
}
this._last_tex = tex;
if (this.properties.filter === false) {
tex.setParameter(gl.TEXTURE_MAG_FILTER, gl.NEAREST);
} else {
tex.setParameter(gl.TEXTURE_MAG_FILTER, gl.LINEAR);
}
this.setOutputData(0, tex);
for (var i = 1; i < this.outputs.length; i++) {
var output = this.outputs[i];
if (!output) {
continue;
}
var v = null;
if (output.name == "width") {
v = tex.width;
} else if (output.name == "height") {
v = tex.height;
} else if (output.name == "aspect") {
v = tex.width / tex.height;
}
this.setOutputData(i, v);
}
};
LGraphTexture.prototype.onResourceRenamed = function(
old_name,
new_name
) {
if (this.properties.name == old_name) {
this.properties.name = new_name;
}
};
LGraphTexture.prototype.onDrawBackground = function(ctx) {
if (this.flags.collapsed || this.size[1] <= 20) {
return;
}
if (this._drop_texture && ctx.webgl) {
ctx.drawImage(
this._drop_texture,
0,
0,
this.size[0],
this.size[1]
);
//this._drop_texture.renderQuad(this.pos[0],this.pos[1],this.size[0],this.size[1]);
return;
}
//Different texture? then get it from the GPU
if (this._last_preview_tex != this._last_tex) {
if (ctx.webgl) {
this._canvas = this._last_tex;
} else {
var tex_canvas = LGraphTexture.generateLowResTexturePreview(
this._last_tex
);
if (!tex_canvas) {
return;
}
this._last_preview_tex = this._last_tex;
this._canvas = cloneCanvas(tex_canvas);
}
}
if (!this._canvas) {
return;
}
//render to graph canvas
ctx.save();
if (!ctx.webgl) {
//reverse image
ctx.translate(0, this.size[1]);
ctx.scale(1, -1);
}
ctx.drawImage(this._canvas, 0, 0, this.size[0], this.size[1]);
ctx.restore();
};
//very slow, used at your own risk
LGraphTexture.generateLowResTexturePreview = function(tex) {
if (!tex) {
return null;
}
var size = LGraphTexture.image_preview_size;
var temp_tex = tex;
if (tex.format == gl.DEPTH_COMPONENT) {
return null;
} //cannot generate from depth
//Generate low-level version in the GPU to speed up
if (tex.width > size || tex.height > size) {
temp_tex = this._preview_temp_tex;
if (!this._preview_temp_tex) {
temp_tex = new GL.Texture(size, size, {
minFilter: gl.NEAREST
});
this._preview_temp_tex = temp_tex;
}
//copy
tex.copyTo(temp_tex);
tex = temp_tex;
}
//create intermediate canvas with lowquality version
var tex_canvas = this._preview_canvas;
if (!tex_canvas) {
tex_canvas = createCanvas(size, size);
this._preview_canvas = tex_canvas;
}
if (temp_tex) {
temp_tex.toCanvas(tex_canvas);
}
return tex_canvas;
};
LGraphTexture.prototype.getResources = function(res) {
res[this.properties.name] = GL.Texture;
return res;
};
LGraphTexture.prototype.onGetInputs = function() {
return [["in", "Texture"]];
};
LGraphTexture.prototype.onGetOutputs = function() {
return [
["width", "number"],
["height", "number"],
["aspect", "number"]
];
};
//used to replace shader code
LGraphTexture.replaceCode = function( code, context )
{
return code.replace(/\{\{[a-zA-Z0-9_]*\}\}/g, function(v){
v = v.replace( /[\{\}]/g, "" );
return context[v] || "";
});
}
LiteGraph.registerNodeType("texture/texture", LGraphTexture);
//**************************
function LGraphTexturePreview() {
this.addInput("Texture", "Texture");
this.properties = { flipY: false };
this.size = [
LGraphTexture.image_preview_size,
LGraphTexture.image_preview_size
];
}
LGraphTexturePreview.title = "Preview";
LGraphTexturePreview.desc = "Show a texture in the graph canvas";
LGraphTexturePreview.allow_preview = false;
LGraphTexturePreview.prototype.onDrawBackground = function(ctx) {
if (this.flags.collapsed) {
return;
}
if (!ctx.webgl && !LGraphTexturePreview.allow_preview) {
return;
} //not working well
var tex = this.getInputData(0);
if (!tex) {
return;
}
var tex_canvas = null;
if (!tex.handle && ctx.webgl) {
tex_canvas = tex;
} else {
tex_canvas = LGraphTexture.generateLowResTexturePreview(tex);
}
//render to graph canvas
ctx.save();
if (this.properties.flipY) {
ctx.translate(0, this.size[1]);
ctx.scale(1, -1);
}
ctx.drawImage(tex_canvas, 0, 0, this.size[0], this.size[1]);
ctx.restore();
};
LiteGraph.registerNodeType("texture/preview", LGraphTexturePreview);
//**************************************
function LGraphTextureSave() {
this.addInput("Texture", "Texture");
this.addOutput("", "Texture");
this.properties = { name: "" };
}
LGraphTextureSave.title = "Save";
LGraphTextureSave.desc = "Save a texture in the repository";
LGraphTextureSave.prototype.onExecute = function() {
var tex = this.getInputData(0);
if (!tex) {
return;
}
if (this.properties.name) {
//for cases where we want to perform something when storing it
if (LGraphTexture.storeTexture) {
LGraphTexture.storeTexture(this.properties.name, tex);
} else {
var container = LGraphTexture.getTexturesContainer();
container[this.properties.name] = tex;
}
}
this.setOutputData(0, tex);
};
LiteGraph.registerNodeType("texture/save", LGraphTextureSave);
//****************************************************
function LGraphTextureOperation() {
this.addInput("Texture", "Texture");
this.addInput("TextureB", "Texture");
this.addInput("value", "number");
this.addOutput("Texture", "Texture");
this.help =
"<p>pixelcode must be vec3, uvcode must be vec2, is optional</p>\
<p><strong>uv:</strong> tex. coords</p><p><strong>color:</strong> texture <strong>colorB:</strong> textureB</p><p><strong>time:</strong> scene time <strong>value:</strong> input value</p><p>For multiline you must type: result = ...</p>";
this.properties = {
value: 1,
pixelcode: "color + colorB * value",
uvcode: "",
precision: LGraphTexture.DEFAULT
};
this.has_error = false;
}
LGraphTextureOperation.widgets_info = {
uvcode: { widget: "code" },
pixelcode: { widget: "code" },
precision: { widget: "combo", values: LGraphTexture.MODE_VALUES }
};
LGraphTextureOperation.title = "Operation";
LGraphTextureOperation.desc = "Texture shader operation";
LGraphTextureOperation.prototype.getExtraMenuOptions = function(
graphcanvas
) {
var that = this;
var txt = !that.properties.show ? "Show Texture" : "Hide Texture";
return [
{
content: txt,
callback: function() {
that.properties.show = !that.properties.show;
}
}
];
};
LGraphTextureOperation.prototype.onPropertyChanged = function()
{
this.has_error = false;
}
LGraphTextureOperation.prototype.onDrawBackground = function(ctx) {
if (
this.flags.collapsed ||
this.size[1] <= 20 ||
!this.properties.show
) {
return;
}
if (!this._tex) {
return;
}
//only works if using a webgl renderer
if (this._tex.gl != ctx) {
return;
}
//render to graph canvas
ctx.save();
ctx.drawImage(this._tex, 0, 0, this.size[0], this.size[1]);
ctx.restore();
};
LGraphTextureOperation.prototype.onExecute = function() {
var tex = this.getInputData(0);
if (!this.isOutputConnected(0)) {
return;
} //saves work
if (this.properties.precision === LGraphTexture.PASS_THROUGH) {
this.setOutputData(0, tex);
return;
}
var texB = this.getInputData(1);
if (!this.properties.uvcode && !this.properties.pixelcode) {
return;
}
var width = 512;
var height = 512;
if (tex) {
width = tex.width;
height = tex.height;
} else if (texB) {
width = texB.width;
height = texB.height;
}
var type = LGraphTexture.getTextureType(
this.properties.precision,
tex
);
if (!tex && !this._tex) {
this._tex = new GL.Texture(width, height, {
type: type,
format: gl.RGBA,
filter: gl.LINEAR
});
} else {
this._tex = LGraphTexture.getTargetTexture(
tex || this._tex,
this._tex,
this.properties.precision
);
}
var uvcode = "";
if (this.properties.uvcode) {
uvcode = "uv = " + this.properties.uvcode;
if (this.properties.uvcode.indexOf(";") != -1) {
//there are line breaks, means multiline code
uvcode = this.properties.uvcode;
}
}
var pixelcode = "";
if (this.properties.pixelcode) {
pixelcode = "result = " + this.properties.pixelcode;
if (this.properties.pixelcode.indexOf(";") != -1) {
//there are line breaks, means multiline code
pixelcode = this.properties.pixelcode;
}
}
var shader = this._shader;
if ( !this.has_error && (!shader || this._shader_code != uvcode + "|" + pixelcode) ) {
var final_pixel_code = LGraphTexture.replaceCode( LGraphTextureOperation.pixel_shader, { UV_CODE:uvcode, PIXEL_CODE:pixelcode });
try {
shader = new GL.Shader( Shader.SCREEN_VERTEX_SHADER, final_pixel_code );
this.boxcolor = "#00FF00";
} catch (err) {
console.log("Error compiling shader: ", err, final_pixel_code );
this.boxcolor = "#FF0000";
this.has_error = true;
return;
}
this._shader = shader;
this._shader_code = uvcode + "|" + pixelcode;
}
var value = this.getInputData(2);
if (value != null) {
this.properties.value = value;
} else {
value = parseFloat(this.properties.value);
}
var time = this.graph.getTime();
this._tex.drawTo(function() {
gl.disable(gl.DEPTH_TEST);
gl.disable(gl.CULL_FACE);
gl.disable(gl.BLEND);
if (tex) {
tex.bind(0);
}
if (texB) {
texB.bind(1);
}
var mesh = Mesh.getScreenQuad();
shader
.uniforms({
u_texture: 0,
u_textureB: 1,
value: value,
texSize: [width, height],
time: time
})
.draw(mesh);
});
this.setOutputData(0, this._tex);
};
LGraphTextureOperation.pixel_shader =
"precision highp float;\n\
\n\
uniform sampler2D u_texture;\n\
uniform sampler2D u_textureB;\n\
varying vec2 v_coord;\n\
uniform vec2 texSize;\n\
uniform float time;\n\
uniform float value;\n\
\n\
void main() {\n\
vec2 uv = v_coord;\n\
{{UV_CODE}};\n\
vec4 color4 = texture2D(u_texture, uv);\n\
vec3 color = color4.rgb;\n\
vec4 color4B = texture2D(u_textureB, uv);\n\
vec3 colorB = color4B.rgb;\n\
vec3 result = color;\n\
float alpha = 1.0;\n\
{{PIXEL_CODE}};\n\
gl_FragColor = vec4(result, alpha);\n\
}\n\
";
LiteGraph.registerNodeType("texture/operation", LGraphTextureOperation);
//****************************************************
function LGraphTextureShader() {
this.addOutput("out", "Texture");
this.properties = {
code: "",
width: 512,
height: 512,
precision: LGraphTexture.DEFAULT
};
this.properties.code =
"\nvoid main() {\n vec2 uv = v_coord;\n vec3 color = vec3(0.0);\n//your code here\n\ngl_FragColor = vec4(color, 1.0);\n}\n";
this._uniforms = { in_texture: 0, texSize: vec2.create(), time: 0 };
}
LGraphTextureShader.title = "Shader";
LGraphTextureShader.desc = "Texture shader";
LGraphTextureShader.widgets_info = {
code: { type: "code" },
precision: { widget: "combo", values: LGraphTexture.MODE_VALUES }
};
LGraphTextureShader.prototype.onPropertyChanged = function(
name,
value
) {
if (name != "code") {
return;
}
var shader = this.getShader();
if (!shader) {
return;
}
//update connections
var uniforms = shader.uniformInfo;
//remove deprecated slots
if (this.inputs) {
var already = {};
for (var i = 0; i < this.inputs.length; ++i) {
var info = this.getInputInfo(i);
if (!info) {
continue;
}
if (uniforms[info.name] && !already[info.name]) {
already[info.name] = true;
continue;
}
this.removeInput(i);
i--;
}
}
//update existing ones
for (var i in uniforms) {
var info = shader.uniformInfo[i];
if (info.loc === null) {
continue;
} //is an attribute, not a uniform
if (i == "time") {
//default one
continue;
}
var type = "number";
if (this._shader.samplers[i]) {
type = "texture";
} else {
switch (info.size) {
case 1:
type = "number";
break;
case 2:
type = "vec2";
break;
case 3:
type = "vec3";
break;
case 4:
type = "vec4";
break;
case 9:
type = "mat3";
break;
case 16:
type = "mat4";
break;
default:
continue;
}
}
var slot = this.findInputSlot(i);
if (slot == -1) {
this.addInput(i, type);
continue;
}
var input_info = this.getInputInfo(slot);
if (!input_info) {
this.addInput(i, type);
} else {
if (input_info.type == type) {
continue;
}
this.removeInput(slot, type);
this.addInput(i, type);
}
}
};
LGraphTextureShader.prototype.getShader = function() {
//replug
if (this._shader && this._shader_code == this.properties.code) {
return this._shader;
}
this._shader_code = this.properties.code;
this._shader = new GL.Shader(
Shader.SCREEN_VERTEX_SHADER,
LGraphTextureShader.pixel_shader + this.properties.code
);
if (!this._shader) {
this.boxcolor = "red";
return null;
} else {
this.boxcolor = "green";
}
return this._shader;
};
LGraphTextureShader.prototype.onExecute = function() {
if (!this.isOutputConnected(0)) {
return;
} //saves work
var shader = this.getShader();
if (!shader) {
return;
}
var tex_slot = 0;
var in_tex = null;
//set uniforms
for (var i = 0; i < this.inputs.length; ++i) {
var info = this.getInputInfo(i);
var data = this.getInputData(i);
if (data == null) {
continue;
}
if (data.constructor === GL.Texture) {
data.bind(tex_slot);
if (!in_tex) {
in_tex = data;
}
data = tex_slot;
tex_slot++;
}
shader.setUniform(info.name, data); //data is tex_slot
}
var uniforms = this._uniforms;
var type = LGraphTexture.getTextureType(
this.properties.precision,
in_tex
);
//render to texture
var w = this.properties.width | 0;
var h = this.properties.height | 0;
if (w == 0) {
w = in_tex ? in_tex.width : gl.canvas.width;
}
if (h == 0) {
h = in_tex ? in_tex.height : gl.canvas.height;
}
uniforms.texSize[0] = w;
uniforms.texSize[1] = h;
uniforms.time = this.graph.getTime();
if (
!this._tex ||
this._tex.type != type ||
this._tex.width != w ||
this._tex.height != h
) {
this._tex = new GL.Texture(w, h, {
type: type,
format: gl.RGBA,
filter: gl.LINEAR
});
}
var tex = this._tex;
tex.drawTo(function() {
shader.uniforms(uniforms).draw(GL.Mesh.getScreenQuad());
});
this.setOutputData(0, this._tex);
};
LGraphTextureShader.pixel_shader =
"precision highp float;\n\
\n\
varying vec2 v_coord;\n\
uniform float time;\n\
";
LiteGraph.registerNodeType("texture/shader", LGraphTextureShader);
// Texture Scale Offset
function LGraphTextureScaleOffset() {
this.addInput("in", "Texture");
this.addInput("scale", "vec2");
this.addInput("offset", "vec2");
this.addOutput("out", "Texture");
this.properties = {
offset: vec2.fromValues(0, 0),
scale: vec2.fromValues(1, 1),
precision: LGraphTexture.DEFAULT
};
}
LGraphTextureScaleOffset.widgets_info = {
precision: { widget: "combo", values: LGraphTexture.MODE_VALUES }
};
LGraphTextureScaleOffset.title = "Scale/Offset";
LGraphTextureScaleOffset.desc = "Applies an scaling and offseting";
LGraphTextureScaleOffset.prototype.onExecute = function() {
var tex = this.getInputData(0);
if (!this.isOutputConnected(0) || !tex) {
return;
} //saves work
if (this.properties.precision === LGraphTexture.PASS_THROUGH) {
this.setOutputData(0, tex);
return;
}
var width = tex.width;
var height = tex.height;
var type =
this.precision === LGraphTexture.LOW
? gl.UNSIGNED_BYTE
: gl.HIGH_PRECISION_FORMAT;
if (this.precision === LGraphTexture.DEFAULT) {
type = tex.type;
}
if (
!this._tex ||
this._tex.width != width ||
this._tex.height != height ||
this._tex.type != type
) {
this._tex = new GL.Texture(width, height, {
type: type,
format: gl.RGBA,
filter: gl.LINEAR
});
}
var shader = this._shader;
if (!shader) {
shader = new GL.Shader(
GL.Shader.SCREEN_VERTEX_SHADER,
LGraphTextureScaleOffset.pixel_shader
);
}
var scale = this.getInputData(1);
if (scale) {
this.properties.scale[0] = scale[0];
this.properties.scale[1] = scale[1];
} else {
scale = this.properties.scale;
}
var offset = this.getInputData(2);
if (offset) {
this.properties.offset[0] = offset[0];
this.properties.offset[1] = offset[1];
} else {
offset = this.properties.offset;
}
this._tex.drawTo(function() {
gl.disable(gl.DEPTH_TEST);
gl.disable(gl.CULL_FACE);
gl.disable(gl.BLEND);
tex.bind(0);
var mesh = Mesh.getScreenQuad();
shader
.uniforms({
u_texture: 0,
u_scale: scale,
u_offset: offset
})
.draw(mesh);
});
this.setOutputData(0, this._tex);
};
LGraphTextureScaleOffset.pixel_shader =
"precision highp float;\n\
\n\
uniform sampler2D u_texture;\n\
uniform sampler2D u_textureB;\n\
varying vec2 v_coord;\n\
uniform vec2 u_scale;\n\
uniform vec2 u_offset;\n\
\n\
void main() {\n\
vec2 uv = v_coord;\n\
uv = uv / u_scale - u_offset;\n\
gl_FragColor = texture2D(u_texture, uv);\n\
}\n\
";
LiteGraph.registerNodeType(
"texture/scaleOffset",
LGraphTextureScaleOffset
);
// Warp (distort a texture) *************************
function LGraphTextureWarp() {
this.addInput("in", "Texture");
this.addInput("warp", "Texture");
this.addInput("factor", "number");
this.addOutput("out", "Texture");
this.properties = {
factor: 0.01,
precision: LGraphTexture.DEFAULT
};
}
LGraphTextureWarp.widgets_info = {
precision: { widget: "combo", values: LGraphTexture.MODE_VALUES }
};
LGraphTextureWarp.title = "Warp";
LGraphTextureWarp.desc = "Texture warp operation";
LGraphTextureWarp.prototype.onExecute = function() {
var tex = this.getInputData(0);
if (!this.isOutputConnected(0)) {
return;
} //saves work
if (this.properties.precision === LGraphTexture.PASS_THROUGH) {
this.setOutputData(0, tex);
return;
}
var texB = this.getInputData(1);
var width = 512;
var height = 512;
var type = gl.UNSIGNED_BYTE;
if (tex) {
width = tex.width;
height = tex.height;
type = tex.type;
} else if (texB) {
width = texB.width;
height = texB.height;
type = texB.type;
}
if (!tex && !this._tex) {
this._tex = new GL.Texture(width, height, {
type:
this.precision === LGraphTexture.LOW
? gl.UNSIGNED_BYTE
: gl.HIGH_PRECISION_FORMAT,
format: gl.RGBA,
filter: gl.LINEAR
});
} else {
this._tex = LGraphTexture.getTargetTexture(
tex || this._tex,
this._tex,
this.properties.precision
);
}
var shader = this._shader;
if (!shader) {
shader = new GL.Shader(
GL.Shader.SCREEN_VERTEX_SHADER,
LGraphTextureWarp.pixel_shader
);
}
var factor = this.getInputData(2);
if (factor != null) {
this.properties.factor = factor;
} else {
factor = parseFloat(this.properties.factor);
}
this._tex.drawTo(function() {
gl.disable(gl.DEPTH_TEST);
gl.disable(gl.CULL_FACE);
gl.disable(gl.BLEND);
if (tex) {
tex.bind(0);
}
if (texB) {
texB.bind(1);
}
var mesh = Mesh.getScreenQuad();
shader
.uniforms({ u_texture: 0, u_textureB: 1, u_factor: factor })
.draw(mesh);
});
this.setOutputData(0, this._tex);
};
LGraphTextureWarp.pixel_shader =
"precision highp float;\n\
\n\
uniform sampler2D u_texture;\n\
uniform sampler2D u_textureB;\n\
varying vec2 v_coord;\n\
uniform float u_factor;\n\
\n\
void main() {\n\
vec2 uv = v_coord;\n\
uv += ( texture2D(u_textureB, uv).rg - vec2(0.5)) * u_factor;\n\
gl_FragColor = texture2D(u_texture, uv);\n\
}\n\
";
LiteGraph.registerNodeType("texture/warp", LGraphTextureWarp);
//****************************************************
// Texture to Viewport *****************************************
function LGraphTextureToViewport() {
this.addInput("Texture", "Texture");
this.properties = {
additive: false,
antialiasing: false,
filter: true,
disable_alpha: false,
gamma: 1.0
};
this.size[0] = 130;
}
LGraphTextureToViewport.title = "to Viewport";
LGraphTextureToViewport.desc = "Texture to viewport";
LGraphTextureToViewport.prototype.onExecute = function() {
var tex = this.getInputData(0);
if (!tex) {
return;
}
if (this.properties.disable_alpha) {
gl.disable(gl.BLEND);
} else {
gl.enable(gl.BLEND);
if (this.properties.additive) {
gl.blendFunc(gl.SRC_ALPHA, gl.ONE);
} else {
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
}
}
gl.disable(gl.DEPTH_TEST);
var gamma = this.properties.gamma || 1.0;
if (this.isInputConnected(1)) {
gamma = this.getInputData(1);
}
tex.setParameter(
gl.TEXTURE_MAG_FILTER,
this.properties.filter ? gl.LINEAR : gl.NEAREST
);
if (this.properties.antialiasing) {
if (!LGraphTextureToViewport._shader) {
LGraphTextureToViewport._shader = new GL.Shader(
GL.Shader.SCREEN_VERTEX_SHADER,
LGraphTextureToViewport.aa_pixel_shader
);
}
var viewport = gl.getViewport(); //gl.getParameter(gl.VIEWPORT);
var mesh = Mesh.getScreenQuad();
tex.bind(0);
LGraphTextureToViewport._shader
.uniforms({
u_texture: 0,
uViewportSize: [tex.width, tex.height],
u_igamma: 1 / gamma,
inverseVP: [1 / tex.width, 1 / tex.height]
})
.draw(mesh);
} else {
if (gamma != 1.0) {
if (!LGraphTextureToViewport._gamma_shader) {
LGraphTextureToViewport._gamma_shader = new GL.Shader(
Shader.SCREEN_VERTEX_SHADER,
LGraphTextureToViewport.gamma_pixel_shader
);
}
tex.toViewport(LGraphTextureToViewport._gamma_shader, {
u_texture: 0,
u_igamma: 1 / gamma
});
} else {
tex.toViewport();
}
}
};
LGraphTextureToViewport.prototype.onGetInputs = function() {
return [["gamma", "number"]];
};
LGraphTextureToViewport.aa_pixel_shader =
"precision highp float;\n\
precision highp float;\n\
varying vec2 v_coord;\n\
uniform sampler2D u_texture;\n\
uniform vec2 uViewportSize;\n\
uniform vec2 inverseVP;\n\
uniform float u_igamma;\n\
#define FXAA_REDUCE_MIN (1.0/ 128.0)\n\
#define FXAA_REDUCE_MUL (1.0 / 8.0)\n\
#define FXAA_SPAN_MAX 8.0\n\
\n\
/* from mitsuhiko/webgl-meincraft based on the code on geeks3d.com */\n\
vec4 applyFXAA(sampler2D tex, vec2 fragCoord)\n\
{\n\
vec4 color = vec4(0.0);\n\
/*vec2 inverseVP = vec2(1.0 / uViewportSize.x, 1.0 / uViewportSize.y);*/\n\
vec3 rgbNW = texture2D(tex, (fragCoord + vec2(-1.0, -1.0)) * inverseVP).xyz;\n\
vec3 rgbNE = texture2D(tex, (fragCoord + vec2(1.0, -1.0)) * inverseVP).xyz;\n\
vec3 rgbSW = texture2D(tex, (fragCoord + vec2(-1.0, 1.0)) * inverseVP).xyz;\n\
vec3 rgbSE = texture2D(tex, (fragCoord + vec2(1.0, 1.0)) * inverseVP).xyz;\n\
vec3 rgbM = texture2D(tex, fragCoord * inverseVP).xyz;\n\
vec3 luma = vec3(0.299, 0.587, 0.114);\n\
float lumaNW = dot(rgbNW, luma);\n\
float lumaNE = dot(rgbNE, luma);\n\
float lumaSW = dot(rgbSW, luma);\n\
float lumaSE = dot(rgbSE, luma);\n\
float lumaM = dot(rgbM, luma);\n\
float lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE)));\n\
float lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE)));\n\
\n\
vec2 dir;\n\
dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));\n\
dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE));\n\
\n\
float dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) * (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN);\n\
\n\
float rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce);\n\
dir = min(vec2(FXAA_SPAN_MAX, FXAA_SPAN_MAX), max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX), dir * rcpDirMin)) * inverseVP;\n\
\n\
vec3 rgbA = 0.5 * (texture2D(tex, fragCoord * inverseVP + dir * (1.0 / 3.0 - 0.5)).xyz + \n\
texture2D(tex, fragCoord * inverseVP + dir * (2.0 / 3.0 - 0.5)).xyz);\n\
vec3 rgbB = rgbA * 0.5 + 0.25 * (texture2D(tex, fragCoord * inverseVP + dir * -0.5).xyz + \n\
texture2D(tex, fragCoord * inverseVP + dir * 0.5).xyz);\n\
\n\
//return vec4(rgbA,1.0);\n\
float lumaB = dot(rgbB, luma);\n\
if ((lumaB < lumaMin) || (lumaB > lumaMax))\n\
color = vec4(rgbA, 1.0);\n\
else\n\
color = vec4(rgbB, 1.0);\n\
if(u_igamma != 1.0)\n\
color.xyz = pow( color.xyz, vec3(u_igamma) );\n\
return color;\n\
}\n\
\n\
void main() {\n\
gl_FragColor = applyFXAA( u_texture, v_coord * uViewportSize) ;\n\
}\n\
";
LGraphTextureToViewport.gamma_pixel_shader =
"precision highp float;\n\
precision highp float;\n\
varying vec2 v_coord;\n\
uniform sampler2D u_texture;\n\
uniform float u_igamma;\n\
void main() {\n\
vec4 color = texture2D( u_texture, v_coord);\n\
color.xyz = pow(color.xyz, vec3(u_igamma) );\n\
gl_FragColor = color;\n\
}\n\
";
LiteGraph.registerNodeType(
"texture/toviewport",
LGraphTextureToViewport
);
// Texture Copy *****************************************
function LGraphTextureCopy() {
this.addInput("Texture", "Texture");
this.addOutput("", "Texture");
this.properties = {
size: 0,
generate_mipmaps: false,
precision: LGraphTexture.DEFAULT
};
}
LGraphTextureCopy.title = "Copy";
LGraphTextureCopy.desc = "Copy Texture";
LGraphTextureCopy.widgets_info = {
size: {
widget: "combo",
values: [0, 32, 64, 128, 256, 512, 1024, 2048]
},
precision: { widget: "combo", values: LGraphTexture.MODE_VALUES }
};
LGraphTextureCopy.prototype.onExecute = function() {
var tex = this.getInputData(0);
if (!tex && !this._temp_texture) {
return;
}
if (!this.isOutputConnected(0)) {
return;
} //saves work
//copy the texture
if (tex) {
var width = tex.width;
var height = tex.height;
if (this.properties.size != 0) {
width = this.properties.size;
height = this.properties.size;
}
var temp = this._temp_texture;
var type = tex.type;
if (this.properties.precision === LGraphTexture.LOW) {
type = gl.UNSIGNED_BYTE;
} else if (this.properties.precision === LGraphTexture.HIGH) {
type = gl.HIGH_PRECISION_FORMAT;
}
if (
!temp ||
temp.width != width ||
temp.height != height ||
temp.type != type
) {
var minFilter = gl.LINEAR;
if (
this.properties.generate_mipmaps &&
isPowerOfTwo(width) &&
isPowerOfTwo(height)
) {
minFilter = gl.LINEAR_MIPMAP_LINEAR;
}
this._temp_texture = new GL.Texture(width, height, {
type: type,
format: gl.RGBA,
minFilter: minFilter,
magFilter: gl.LINEAR
});
}
tex.copyTo(this._temp_texture);
if (this.properties.generate_mipmaps) {
this._temp_texture.bind(0);
gl.generateMipmap(this._temp_texture.texture_type);
this._temp_texture.unbind(0);
}
}
this.setOutputData(0, this._temp_texture);
};
LiteGraph.registerNodeType("texture/copy", LGraphTextureCopy);
// Texture Downsample *****************************************
function LGraphTextureDownsample() {
this.addInput("Texture", "Texture");
this.addOutput("", "Texture");
this.properties = {
iterations: 1,
generate_mipmaps: false,
precision: LGraphTexture.DEFAULT
};
}
LGraphTextureDownsample.title = "Downsample";
LGraphTextureDownsample.desc = "Downsample Texture";
LGraphTextureDownsample.widgets_info = {
iterations: { type: "number", step: 1, precision: 0, min: 0 },
precision: { widget: "combo", values: LGraphTexture.MODE_VALUES }
};
LGraphTextureDownsample.prototype.onExecute = function() {
var tex = this.getInputData(0);
if (!tex && !this._temp_texture) {
return;
}
if (!this.isOutputConnected(0)) {
return;
} //saves work
//we do not allow any texture different than texture 2D
if (!tex || tex.texture_type !== GL.TEXTURE_2D) {
return;
}
if (this.properties.iterations < 1) {
this.setOutputData(0, tex);
return;
}
var shader = LGraphTextureDownsample._shader;
if (!shader) {
LGraphTextureDownsample._shader = shader = new GL.Shader(
GL.Shader.SCREEN_VERTEX_SHADER,
LGraphTextureDownsample.pixel_shader
);
}
var width = tex.width | 0;
var height = tex.height | 0;
var type = tex.type;
if (this.properties.precision === LGraphTexture.LOW) {
type = gl.UNSIGNED_BYTE;
} else if (this.properties.precision === LGraphTexture.HIGH) {
type = gl.HIGH_PRECISION_FORMAT;
}
var iterations = this.properties.iterations || 1;
var origin = tex;
var target = null;
var temp = [];
var options = {
type: type,
format: tex.format
};
var offset = vec2.create();
var uniforms = {
u_offset: offset
};
if (this._texture) {
GL.Texture.releaseTemporary(this._texture);
}
for (var i = 0; i < iterations; ++i) {
offset[0] = 1 / width;
offset[1] = 1 / height;
width = width >> 1 || 0;
height = height >> 1 || 0;
target = GL.Texture.getTemporary(width, height, options);
temp.push(target);
origin.setParameter(GL.TEXTURE_MAG_FILTER, GL.NEAREST);
origin.copyTo(target, shader, uniforms);
if (width == 1 && height == 1) {
break;
} //nothing else to do
origin = target;
}
//keep the last texture used
this._texture = temp.pop();
//free the rest
for (var i = 0; i < temp.length; ++i) {
GL.Texture.releaseTemporary(temp[i]);
}
if (this.properties.generate_mipmaps) {
this._texture.bind(0);
gl.generateMipmap(this._texture.texture_type);
this._texture.unbind(0);
}
this.setOutputData(0, this._texture);
};
LGraphTextureDownsample.pixel_shader =
"precision highp float;\n\
precision highp float;\n\
uniform sampler2D u_texture;\n\
uniform vec2 u_offset;\n\
varying vec2 v_coord;\n\
\n\
void main() {\n\
vec4 color = texture2D(u_texture, v_coord );\n\
color += texture2D(u_texture, v_coord + vec2( u_offset.x, 0.0 ) );\n\
color += texture2D(u_texture, v_coord + vec2( 0.0, u_offset.y ) );\n\
color += texture2D(u_texture, v_coord + vec2( u_offset.x, u_offset.y ) );\n\
gl_FragColor = color * 0.25;\n\
}\n\
";
LiteGraph.registerNodeType(
"texture/downsample",
LGraphTextureDownsample
);
// Texture Average *****************************************
function LGraphTextureAverage() {
this.addInput("Texture", "Texture");
this.addOutput("tex", "Texture");
this.addOutput("avg", "vec4");
this.addOutput("lum", "number");
this.properties = {
use_previous_frame: true, //to avoid stalls
high_quality: false //to use as much pixels as possible
};
this._uniforms = {
u_texture: 0,
u_mipmap_offset: 0
};
this._luminance = new Float32Array(4);
}
LGraphTextureAverage.title = "Average";
LGraphTextureAverage.desc =
"Compute a partial average (32 random samples) of a texture and stores it as a 1x1 pixel texture";
LGraphTextureAverage.prototype.onExecute = function() {
if (!this.properties.use_previous_frame) {
this.updateAverage();
}
var v = this._luminance;
this.setOutputData(0, this._temp_texture);
this.setOutputData(1, v);
this.setOutputData(2, (v[0] + v[1] + v[2]) / 3);
};
//executed before rendering the frame
LGraphTextureAverage.prototype.onPreRenderExecute = function() {
this.updateAverage();
};
LGraphTextureAverage.prototype.updateAverage = function() {
var tex = this.getInputData(0);
if (!tex) {
return;
}
if (
!this.isOutputConnected(0) &&
!this.isOutputConnected(1) &&
!this.isOutputConnected(2)
) {
return;
} //saves work
if (!LGraphTextureAverage._shader) {
LGraphTextureAverage._shader = new GL.Shader(
GL.Shader.SCREEN_VERTEX_SHADER,
LGraphTextureAverage.pixel_shader
);
//creates 256 random numbers and stores them in two mat4
var samples = new Float32Array(16);
for (var i = 0; i < samples.length; ++i) {
samples[i] = Math.random(); //poorly distributed samples
}
//upload only once
LGraphTextureAverage._shader.uniforms({
u_samples_a: samples.subarray(0, 16),
u_samples_b: samples.subarray(16, 32)
});
}
var temp = this._temp_texture;
var type = gl.UNSIGNED_BYTE;
if (tex.type != type) {
//force floats, half floats cannot be read with gl.readPixels
type = gl.FLOAT;
}
if (!temp || temp.type != type) {
this._temp_texture = new GL.Texture(1, 1, {
type: type,
format: gl.RGBA,
filter: gl.NEAREST
});
}
this._uniforms.u_mipmap_offset = 0;
if(this.properties.high_quality)
{
if( !this._temp_pot2_texture || this._temp_pot2_texture.type != type )
this._temp_pot2_texture = new GL.Texture(512, 512, {
type: type,
format: gl.RGBA,
minFilter: gl.LINEAR_MIPMAP_LINEAR,
magFilter: gl.LINEAR
});
tex.copyTo( this._temp_pot2_texture );
tex = this._temp_pot2_texture;
tex.bind(0);
gl.generateMipmap(GL_TEXTURE_2D);
this._uniforms.u_mipmap_offset = 9;
}
var shader = LGraphTextureAverage._shader;
var uniforms = this._uniforms;
uniforms.u_mipmap_offset = this.properties.mipmap_offset;
gl.disable(gl.DEPTH_TEST);
gl.disable(gl.BLEND);
this._temp_texture.drawTo(function() {
tex.toViewport(shader, uniforms);
});
if (this.isOutputConnected(1) || this.isOutputConnected(2)) {
var pixel = this._temp_texture.getPixels();
if (pixel) {
var v = this._luminance;
var type = this._temp_texture.type;
v.set(pixel);
if (type == gl.UNSIGNED_BYTE) {
vec4.scale(v, v, 1 / 255);
} else if (
type == GL.HALF_FLOAT ||
type == GL.HALF_FLOAT_OES
) {
//no half floats possible, hard to read back unless copyed to a FLOAT texture, so temp_texture is always forced to FLOAT
}
}
}
};
LGraphTextureAverage.pixel_shader =
"precision highp float;\n\
precision highp float;\n\
uniform mat4 u_samples_a;\n\
uniform mat4 u_samples_b;\n\
uniform sampler2D u_texture;\n\
uniform float u_mipmap_offset;\n\
varying vec2 v_coord;\n\
\n\
void main() {\n\
vec4 color = vec4(0.0);\n\
//random average\n\
for(int i = 0; i < 4; ++i)\n\
for(int j = 0; j < 4; ++j)\n\
{\n\
color += texture2D(u_texture, vec2( u_samples_a[i][j], u_samples_b[i][j] ), u_mipmap_offset );\n\
color += texture2D(u_texture, vec2( 1.0 - u_samples_a[i][j], 1.0 - u_samples_b[i][j] ), u_mipmap_offset );\n\
}\n\
gl_FragColor = color * 0.03125;\n\
}\n\
";
LiteGraph.registerNodeType("texture/average", LGraphTextureAverage);
function LGraphTextureTemporalSmooth() {
this.addInput("in", "Texture");
this.addInput("factor", "Number");
this.addOutput("out", "Texture");
this.properties = { factor: 0.5 };
this._uniforms = {
u_texture: 0,
u_textureB: 1,
u_factor: this.properties.factor
};
}
LGraphTextureTemporalSmooth.title = "Smooth";
LGraphTextureTemporalSmooth.desc = "Smooth texture over time";
LGraphTextureTemporalSmooth.prototype.onExecute = function() {
var tex = this.getInputData(0);
if (!tex || !this.isOutputConnected(0)) {
return;
}
if (!LGraphTextureTemporalSmooth._shader) {
LGraphTextureTemporalSmooth._shader = new GL.Shader(
GL.Shader.SCREEN_VERTEX_SHADER,
LGraphTextureTemporalSmooth.pixel_shader
);
}
var temp = this._temp_texture;
if (
!temp ||
temp.type != tex.type ||
temp.width != tex.width ||
temp.height != tex.height
) {
this._temp_texture = new GL.Texture(tex.width, tex.height, {
type: tex.type,
format: gl.RGBA,
filter: gl.NEAREST
});
this._temp_texture2 = new GL.Texture(tex.width, tex.height, {
type: tex.type,
format: gl.RGBA,
filter: gl.NEAREST
});
tex.copyTo(this._temp_texture2);
}
var tempA = this._temp_texture;
var tempB = this._temp_texture2;
var shader = LGraphTextureTemporalSmooth._shader;
var uniforms = this._uniforms;
uniforms.u_factor = 1.0 - this.getInputOrProperty("factor");
gl.disable(gl.BLEND);
gl.disable(gl.DEPTH_TEST);
tempA.drawTo(function() {
tempB.bind(1);
tex.toViewport(shader, uniforms);
});
this.setOutputData(0, tempA);
//swap
this._temp_texture = tempB;
this._temp_texture2 = tempA;
};
LGraphTextureTemporalSmooth.pixel_shader =
"precision highp float;\n\
precision highp float;\n\
uniform sampler2D u_texture;\n\
uniform sampler2D u_textureB;\n\
uniform float u_factor;\n\
varying vec2 v_coord;\n\
\n\
void main() {\n\
gl_FragColor = mix( texture2D( u_texture, v_coord ), texture2D( u_textureB, v_coord ), u_factor );\n\
}\n\
";
LiteGraph.registerNodeType(
"texture/temporal_smooth",
LGraphTextureTemporalSmooth
);
// Image To Texture *****************************************
function LGraphImageToTexture() {
this.addInput("Image", "image");
this.addOutput("", "Texture");
this.properties = {};
}
LGraphImageToTexture.title = "Image to Texture";
LGraphImageToTexture.desc = "Uploads an image to the GPU";
//LGraphImageToTexture.widgets_info = { size: { widget:"combo", values:[0,32,64,128,256,512,1024,2048]} };
LGraphImageToTexture.prototype.onExecute = function() {
var img = this.getInputData(0);
if (!img) {
return;
}
var width = img.videoWidth || img.width;
var height = img.videoHeight || img.height;
//this is in case we are using a webgl canvas already, no need to reupload it
if (img.gltexture) {
this.setOutputData(0, img.gltexture);
return;
}
var temp = this._temp_texture;
if (!temp || temp.width != width || temp.height != height) {
this._temp_texture = new GL.Texture(width, height, {
format: gl.RGBA,
filter: gl.LINEAR
});
}
try {
this._temp_texture.uploadImage(img);
} catch (err) {
console.error(
"image comes from an unsafe location, cannot be uploaded to webgl: " +
err
);
return;
}
this.setOutputData(0, this._temp_texture);
};
LiteGraph.registerNodeType(
"texture/imageToTexture",
LGraphImageToTexture
);
// Texture LUT *****************************************
function LGraphTextureLUT() {
this.addInput("Texture", "Texture");
this.addInput("LUT", "Texture");
this.addInput("Intensity", "number");
this.addOutput("", "Texture");
this.properties = {
intensity: 1,
precision: LGraphTexture.DEFAULT,
texture: null
};
if (!LGraphTextureLUT._shader) {
LGraphTextureLUT._shader = new GL.Shader(
Shader.SCREEN_VERTEX_SHADER,
LGraphTextureLUT.pixel_shader
);
}
}
LGraphTextureLUT.widgets_info = {
texture: { widget: "texture" },
precision: { widget: "combo", values: LGraphTexture.MODE_VALUES }
};
LGraphTextureLUT.title = "LUT";
LGraphTextureLUT.desc = "Apply LUT to Texture";
LGraphTextureLUT.prototype.onExecute = function() {
if (!this.isOutputConnected(0)) {
return;
} //saves work
var tex = this.getInputData(0);
if (this.properties.precision === LGraphTexture.PASS_THROUGH) {
this.setOutputData(0, tex);
return;
}
if (!tex) {
return;
}
var lut_tex = this.getInputData(1);
if (!lut_tex) {
lut_tex = LGraphTexture.getTexture(this.properties.texture);
}
if (!lut_tex) {
this.setOutputData(0, tex);
return;
}
lut_tex.bind(0);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(
gl.TEXTURE_2D,
gl.TEXTURE_WRAP_S,
gl.CLAMP_TO_EDGE
);
gl.texParameteri(
gl.TEXTURE_2D,
gl.TEXTURE_WRAP_T,
gl.CLAMP_TO_EDGE
);
gl.bindTexture(gl.TEXTURE_2D, null);
var intensity = this.properties.intensity;
if (this.isInputConnected(2)) {
this.properties.intensity = intensity = this.getInputData(2);
}
this._tex = LGraphTexture.getTargetTexture(
tex,
this._tex,
this.properties.precision
);
//var mesh = Mesh.getScreenQuad();
this._tex.drawTo(function() {
lut_tex.bind(1);
tex.toViewport(LGraphTextureLUT._shader, {
u_texture: 0,
u_textureB: 1,
u_amount: intensity
});
});
this.setOutputData(0, this._tex);
};
LGraphTextureLUT.pixel_shader =
"precision highp float;\n\
precision highp float;\n\
varying vec2 v_coord;\n\
uniform sampler2D u_texture;\n\
uniform sampler2D u_textureB;\n\
uniform float u_amount;\n\
\n\
void main() {\n\
lowp vec4 textureColor = clamp( texture2D(u_texture, v_coord), vec4(0.0), vec4(1.0) );\n\
mediump float blueColor = textureColor.b * 63.0;\n\
mediump vec2 quad1;\n\
quad1.y = floor(floor(blueColor) / 8.0);\n\
quad1.x = floor(blueColor) - (quad1.y * 8.0);\n\
mediump vec2 quad2;\n\
quad2.y = floor(ceil(blueColor) / 8.0);\n\
quad2.x = ceil(blueColor) - (quad2.y * 8.0);\n\
highp vec2 texPos1;\n\
texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\
texPos1.y = 1.0 - ((quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\
highp vec2 texPos2;\n\
texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\
texPos2.y = 1.0 - ((quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\
lowp vec4 newColor1 = texture2D(u_textureB, texPos1);\n\
lowp vec4 newColor2 = texture2D(u_textureB, texPos2);\n\
lowp vec4 newColor = mix(newColor1, newColor2, fract(blueColor));\n\
gl_FragColor = vec4( mix( textureColor.rgb, newColor.rgb, u_amount), textureColor.w);\n\
}\n\
";
LiteGraph.registerNodeType("texture/LUT", LGraphTextureLUT);
// Texture Channels *****************************************
function LGraphTextureChannels() {
this.addInput("Texture", "Texture");
this.addOutput("R", "Texture");
this.addOutput("G", "Texture");
this.addOutput("B", "Texture");
this.addOutput("A", "Texture");
this.properties = { use_luminance: true };
if (!LGraphTextureChannels._shader) {
LGraphTextureChannels._shader = new GL.Shader(
Shader.SCREEN_VERTEX_SHADER,
LGraphTextureChannels.pixel_shader
);
}
}
LGraphTextureChannels.title = "Texture to Channels";
LGraphTextureChannels.desc = "Split texture channels";
LGraphTextureChannels.prototype.onExecute = function() {
var texA = this.getInputData(0);
if (!texA) {
return;
}
if (!this._channels) {
this._channels = Array(4);
}
var format = this.properties.use_luminance ? gl.LUMINANCE : gl.RGBA;
var connections = 0;
for (var i = 0; i < 4; i++) {
if (this.isOutputConnected(i)) {
if (
!this._channels[i] ||
this._channels[i].width != texA.width ||
this._channels[i].height != texA.height ||
this._channels[i].type != texA.type ||
this._channels[i].format != format
) {
this._channels[i] = new GL.Texture(
texA.width,
texA.height,
{
type: texA.type,
format: format,
filter: gl.LINEAR
}
);
}
connections++;
} else {
this._channels[i] = null;
}
}
if (!connections) {
return;
}
gl.disable(gl.BLEND);
gl.disable(gl.DEPTH_TEST);
var mesh = Mesh.getScreenQuad();
var shader = LGraphTextureChannels._shader;
var masks = [
[1, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 1, 0],
[0, 0, 0, 1]
];
for (var i = 0; i < 4; i++) {
if (!this._channels[i]) {
continue;
}
this._channels[i].drawTo(function() {
texA.bind(0);
shader
.uniforms({ u_texture: 0, u_mask: masks[i] })
.draw(mesh);
});
this.setOutputData(i, this._channels[i]);
}
};
LGraphTextureChannels.pixel_shader =
"precision highp float;\n\
precision highp float;\n\
varying vec2 v_coord;\n\
uniform sampler2D u_texture;\n\
uniform vec4 u_mask;\n\
\n\
void main() {\n\
gl_FragColor = vec4( vec3( length( texture2D(u_texture, v_coord) * u_mask )), 1.0 );\n\
}\n\
";
LiteGraph.registerNodeType(
"texture/textureChannels",
LGraphTextureChannels
);
// Texture Channels to Texture *****************************************
function LGraphChannelsTexture() {
this.addInput("R", "Texture");
this.addInput("G", "Texture");
this.addInput("B", "Texture");
this.addInput("A", "Texture");
this.addOutput("Texture", "Texture");
this.properties = {
precision: LGraphTexture.DEFAULT,
R: 1,
G: 1,
B: 1,
A: 1
};
this._color = vec4.create();
this._uniforms = {
u_textureR: 0,
u_textureG: 1,
u_textureB: 2,
u_textureA: 3,
u_color: this._color
};
}
LGraphChannelsTexture.title = "Channels to Texture";
LGraphChannelsTexture.desc = "Split texture channels";
LGraphChannelsTexture.widgets_info = {
precision: { widget: "combo", values: LGraphTexture.MODE_VALUES }
};
LGraphChannelsTexture.prototype.onExecute = function() {
var white = LGraphTexture.getWhiteTexture();
var texR = this.getInputData(0) || white;
var texG = this.getInputData(1) || white;
var texB = this.getInputData(2) || white;
var texA = this.getInputData(3) || white;
gl.disable(gl.BLEND);
gl.disable(gl.DEPTH_TEST);
var mesh = Mesh.getScreenQuad();
if (!LGraphChannelsTexture._shader) {
LGraphChannelsTexture._shader = new GL.Shader(
Shader.SCREEN_VERTEX_SHADER,
LGraphChannelsTexture.pixel_shader
);
}
var shader = LGraphChannelsTexture._shader;
var w = Math.max(texR.width, texG.width, texB.width, texA.width);
var h = Math.max(
texR.height,
texG.height,
texB.height,
texA.height
);
var type =
this.properties.precision == LGraphTexture.HIGH
? LGraphTexture.HIGH_PRECISION_FORMAT
: gl.UNSIGNED_BYTE;
if (
!this._texture ||
this._texture.width != w ||
this._texture.height != h ||
this._texture.type != type
) {
this._texture = new GL.Texture(w, h, {
type: type,
format: gl.RGBA,
filter: gl.LINEAR
});
}
var color = this._color;
color[0] = this.properties.R;
color[1] = this.properties.G;
color[2] = this.properties.B;
color[3] = this.properties.A;
var uniforms = this._uniforms;
this._texture.drawTo(function() {
texR.bind(0);
texG.bind(1);
texB.bind(2);
texA.bind(3);
shader.uniforms(uniforms).draw(mesh);
});
this.setOutputData(0, this._texture);
};
LGraphChannelsTexture.pixel_shader =
"precision highp float;\n\
precision highp float;\n\
varying vec2 v_coord;\n\
uniform sampler2D u_textureR;\n\
uniform sampler2D u_textureG;\n\
uniform sampler2D u_textureB;\n\
uniform sampler2D u_textureA;\n\
uniform vec4 u_color;\n\
\n\
void main() {\n\
gl_FragColor = u_color * vec4( \
texture2D(u_textureR, v_coord).r,\
texture2D(u_textureG, v_coord).r,\
texture2D(u_textureB, v_coord).r,\
texture2D(u_textureA, v_coord).r);\n\
}\n\
";
LiteGraph.registerNodeType(
"texture/channelsTexture",
LGraphChannelsTexture
);
// Texture Color *****************************************
function LGraphTextureColor() {
this.addOutput("Texture", "Texture");
this._tex_color = vec4.create();
this.properties = {
color: vec4.create(),
precision: LGraphTexture.DEFAULT
};
}
LGraphTextureColor.title = "Color";
LGraphTextureColor.desc =
"Generates a 1x1 texture with a constant color";
LGraphTextureColor.widgets_info = {
precision: { widget: "combo", values: LGraphTexture.MODE_VALUES }
};
LGraphTextureColor.prototype.onDrawBackground = function(ctx) {
var c = this.properties.color;
ctx.fillStyle =
"rgb(" +
Math.floor(Math.clamp(c[0], 0, 1) * 255) +
"," +
Math.floor(Math.clamp(c[1], 0, 1) * 255) +
"," +
Math.floor(Math.clamp(c[2], 0, 1) * 255) +
")";
if (this.flags.collapsed) {
this.boxcolor = ctx.fillStyle;
} else {
ctx.fillRect(0, 0, this.size[0], this.size[1]);
}
};
LGraphTextureColor.prototype.onExecute = function() {
var type =
this.properties.precision == LGraphTexture.HIGH
? LGraphTexture.HIGH_PRECISION_FORMAT
: gl.UNSIGNED_BYTE;
if (!this._tex || this._tex.type != type) {
this._tex = new GL.Texture(1, 1, {
format: gl.RGBA,
type: type,
minFilter: gl.NEAREST
});
}
var color = this.properties.color;
if (this.inputs) {
for (var i = 0; i < this.inputs.length; i++) {
var input = this.inputs[i];
var v = this.getInputData(i);
if (v === undefined) {
continue;
}
switch (input.name) {
case "RGB":
case "RGBA":
color.set(v);
break;
case "R":
color[0] = v;
break;
case "G":
color[1] = v;
break;
case "B":
color[2] = v;
break;
case "A":
color[3] = v;
break;
}
}
}
if (vec4.sqrDist(this._tex_color, color) > 0.001) {
this._tex_color.set(color);
this._tex.fill(color);
}
this.setOutputData(0, this._tex);
};
LGraphTextureColor.prototype.onGetInputs = function() {
return [
["RGB", "vec3"],
["RGBA", "vec4"],
["R", "number"],
["G", "number"],
["B", "number"],
["A", "number"]
];
};
LiteGraph.registerNodeType("texture/color", LGraphTextureColor);
// Texture Channels to Texture *****************************************
function LGraphTextureGradient() {
this.addInput("A", "color");
this.addInput("B", "color");
this.addOutput("Texture", "Texture");
this.properties = {
angle: 0,
scale: 1,
A: [0, 0, 0],
B: [1, 1, 1],
texture_size: 32
};
if (!LGraphTextureGradient._shader) {
LGraphTextureGradient._shader = new GL.Shader(
Shader.SCREEN_VERTEX_SHADER,
LGraphTextureGradient.pixel_shader
);
}
this._uniforms = {
u_angle: 0,
u_colorA: vec3.create(),
u_colorB: vec3.create()
};
}
LGraphTextureGradient.title = "Gradient";
LGraphTextureGradient.desc = "Generates a gradient";
LGraphTextureGradient["@A"] = { type: "color" };
LGraphTextureGradient["@B"] = { type: "color" };
LGraphTextureGradient["@texture_size"] = {
type: "enum",
values: [32, 64, 128, 256, 512]
};
LGraphTextureGradient.prototype.onExecute = function() {
gl.disable(gl.BLEND);
gl.disable(gl.DEPTH_TEST);
var mesh = GL.Mesh.getScreenQuad();
var shader = LGraphTextureGradient._shader;
var A = this.getInputData(0);
if (!A) {
A = this.properties.A;
}
var B = this.getInputData(1);
if (!B) {
B = this.properties.B;
}
//angle and scale
for (var i = 2; i < this.inputs.length; i++) {
var input = this.inputs[i];
var v = this.getInputData(i);
if (v === undefined) {
continue;
}
this.properties[input.name] = v;
}
var uniforms = this._uniforms;
this._uniforms.u_angle = this.properties.angle * DEG2RAD;
this._uniforms.u_scale = this.properties.scale;
vec3.copy(uniforms.u_colorA, A);
vec3.copy(uniforms.u_colorB, B);
var size = parseInt(this.properties.texture_size);
if (!this._tex || this._tex.width != size) {
this._tex = new GL.Texture(size, size, {
format: gl.RGB,
filter: gl.LINEAR
});
}
this._tex.drawTo(function() {
shader.uniforms(uniforms).draw(mesh);
});
this.setOutputData(0, this._tex);
};
LGraphTextureGradient.prototype.onGetInputs = function() {
return [["angle", "number"], ["scale", "number"]];
};
LGraphTextureGradient.pixel_shader =
"precision highp float;\n\
precision highp float;\n\
varying vec2 v_coord;\n\
uniform float u_angle;\n\
uniform float u_scale;\n\
uniform vec3 u_colorA;\n\
uniform vec3 u_colorB;\n\
\n\
vec2 rotate(vec2 v, float angle)\n\
{\n\
vec2 result;\n\
float _cos = cos(angle);\n\
float _sin = sin(angle);\n\
result.x = v.x * _cos - v.y * _sin;\n\
result.y = v.x * _sin + v.y * _cos;\n\
return result;\n\
}\n\
void main() {\n\
float f = (rotate(u_scale * (v_coord - vec2(0.5)), u_angle) + vec2(0.5)).x;\n\
vec3 color = mix(u_colorA,u_colorB,clamp(f,0.0,1.0));\n\
gl_FragColor = vec4(color,1.0);\n\
}\n\
";
LiteGraph.registerNodeType("texture/gradient", LGraphTextureGradient);
// Texture Mix *****************************************
function LGraphTextureMix() {
this.addInput("A", "Texture");
this.addInput("B", "Texture");
this.addInput("Mixer", "Texture");
this.addOutput("Texture", "Texture");
this.properties = { factor: 0.5, precision: LGraphTexture.DEFAULT };
this._uniforms = {
u_textureA: 0,
u_textureB: 1,
u_textureMix: 2,
u_mix: vec4.create()
};
}
LGraphTextureMix.title = "Mix";
LGraphTextureMix.desc = "Generates a texture mixing two textures";
LGraphTextureMix.widgets_info = {
precision: { widget: "combo", values: LGraphTexture.MODE_VALUES }
};
LGraphTextureMix.prototype.onExecute = function() {
var texA = this.getInputData(0);
if (!this.isOutputConnected(0)) {
return;
} //saves work
if (this.properties.precision === LGraphTexture.PASS_THROUGH) {
this.setOutputData(0, texA);
return;
}
var texB = this.getInputData(1);
if (!texA || !texB) {
return;
}
var texMix = this.getInputData(2);
var factor = this.getInputData(3);
this._tex = LGraphTexture.getTargetTexture(
texA,
this._tex,
this.properties.precision
);
gl.disable(gl.BLEND);
gl.disable(gl.DEPTH_TEST);
var mesh = Mesh.getScreenQuad();
var shader = null;
var uniforms = this._uniforms;
if (texMix) {
shader = LGraphTextureMix._shader_tex;
if (!shader) {
shader = LGraphTextureMix._shader_tex = new GL.Shader(
Shader.SCREEN_VERTEX_SHADER,
LGraphTextureMix.pixel_shader,
{ MIX_TEX: "" }
);
}
} else {
shader = LGraphTextureMix._shader_factor;
if (!shader) {
shader = LGraphTextureMix._shader_factor = new GL.Shader(
Shader.SCREEN_VERTEX_SHADER,
LGraphTextureMix.pixel_shader
);
}
var f = factor == null ? this.properties.factor : factor;
uniforms.u_mix.set([f, f, f, f]);
}
this._tex.drawTo(function() {
texA.bind(0);
texB.bind(1);
if (texMix) {
texMix.bind(2);
}
shader.uniforms(uniforms).draw(mesh);
});
this.setOutputData(0, this._tex);
};
LGraphTextureMix.prototype.onGetInputs = function() {
return [["factor", "number"]];
};
LGraphTextureMix.pixel_shader =
"precision highp float;\n\
precision highp float;\n\
varying vec2 v_coord;\n\
uniform sampler2D u_textureA;\n\
uniform sampler2D u_textureB;\n\
#ifdef MIX_TEX\n\
uniform sampler2D u_textureMix;\n\
#else\n\
uniform vec4 u_mix;\n\
#endif\n\
\n\
void main() {\n\
#ifdef MIX_TEX\n\
vec4 f = texture2D(u_textureMix, v_coord);\n\
#else\n\
vec4 f = u_mix;\n\
#endif\n\
gl_FragColor = mix( texture2D(u_textureA, v_coord), texture2D(u_textureB, v_coord), f );\n\
}\n\
";
LiteGraph.registerNodeType("texture/mix", LGraphTextureMix);
// Texture Edges detection *****************************************
function LGraphTextureEdges() {
this.addInput("Tex.", "Texture");
this.addOutput("Edges", "Texture");
this.properties = {
invert: true,
threshold: false,
factor: 1,
precision: LGraphTexture.DEFAULT
};
if (!LGraphTextureEdges._shader) {
LGraphTextureEdges._shader = new GL.Shader(
Shader.SCREEN_VERTEX_SHADER,
LGraphTextureEdges.pixel_shader
);
}
}
LGraphTextureEdges.title = "Edges";
LGraphTextureEdges.desc = "Detects edges";
LGraphTextureEdges.widgets_info = {
precision: { widget: "combo", values: LGraphTexture.MODE_VALUES }
};
LGraphTextureEdges.prototype.onExecute = function() {
if (!this.isOutputConnected(0)) {
return;
} //saves work
var tex = this.getInputData(0);
if (this.properties.precision === LGraphTexture.PASS_THROUGH) {
this.setOutputData(0, tex);
return;
}
if (!tex) {
return;
}
this._tex = LGraphTexture.getTargetTexture(
tex,
this._tex,
this.properties.precision
);
gl.disable(gl.BLEND);
gl.disable(gl.DEPTH_TEST);
var mesh = Mesh.getScreenQuad();
var shader = LGraphTextureEdges._shader;
var invert = this.properties.invert;
var factor = this.properties.factor;
var threshold = this.properties.threshold ? 1 : 0;
this._tex.drawTo(function() {
tex.bind(0);
shader
.uniforms({
u_texture: 0,
u_isize: [1 / tex.width, 1 / tex.height],
u_factor: factor,
u_threshold: threshold,
u_invert: invert ? 1 : 0
})
.draw(mesh);
});
this.setOutputData(0, this._tex);
};
LGraphTextureEdges.pixel_shader =
"precision highp float;\n\
precision highp float;\n\
varying vec2 v_coord;\n\
uniform sampler2D u_texture;\n\
uniform vec2 u_isize;\n\
uniform int u_invert;\n\
uniform float u_factor;\n\
uniform float u_threshold;\n\
\n\
void main() {\n\
vec4 center = texture2D(u_texture, v_coord);\n\
vec4 up = texture2D(u_texture, v_coord + u_isize * vec2(0.0,1.0) );\n\
vec4 down = texture2D(u_texture, v_coord + u_isize * vec2(0.0,-1.0) );\n\
vec4 left = texture2D(u_texture, v_coord + u_isize * vec2(1.0,0.0) );\n\
vec4 right = texture2D(u_texture, v_coord + u_isize * vec2(-1.0,0.0) );\n\
vec4 diff = abs(center - up) + abs(center - down) + abs(center - left) + abs(center - right);\n\
diff *= u_factor;\n\
if(u_invert == 1)\n\
diff.xyz = vec3(1.0) - diff.xyz;\n\
if( u_threshold == 0.0 )\n\
gl_FragColor = vec4( diff.xyz, center.a );\n\
else\n\
gl_FragColor = vec4( diff.x > 0.5 ? 1.0 : 0.0, diff.y > 0.5 ? 1.0 : 0.0, diff.z > 0.5 ? 1.0 : 0.0, center.a );\n\
}\n\
";
LiteGraph.registerNodeType("texture/edges", LGraphTextureEdges);
// Texture Depth *****************************************
function LGraphTextureDepthRange() {
this.addInput("Texture", "Texture");
this.addInput("Distance", "number");
this.addInput("Range", "number");
this.addOutput("Texture", "Texture");
this.properties = {
distance: 100,
range: 50,
only_depth: false,
high_precision: false
};
this._uniforms = {
u_texture: 0,
u_distance: 100,
u_range: 50,
u_camera_planes: null
};
}
LGraphTextureDepthRange.title = "Depth Range";
LGraphTextureDepthRange.desc = "Generates a texture with a depth range";
LGraphTextureDepthRange.prototype.onExecute = function() {
if (!this.isOutputConnected(0)) {
return;
} //saves work
var tex = this.getInputData(0);
if (!tex) {
return;
}
var precision = gl.UNSIGNED_BYTE;
if (this.properties.high_precision) {
precision = gl.half_float_ext ? gl.HALF_FLOAT_OES : gl.FLOAT;
}
if (
!this._temp_texture ||
this._temp_texture.type != precision ||
this._temp_texture.width != tex.width ||
this._temp_texture.height != tex.height
) {
this._temp_texture = new GL.Texture(tex.width, tex.height, {
type: precision,
format: gl.RGBA,
filter: gl.LINEAR
});
}
var uniforms = this._uniforms;
//iterations
var distance = this.properties.distance;
if (this.isInputConnected(1)) {
distance = this.getInputData(1);
this.properties.distance = distance;
}
var range = this.properties.range;
if (this.isInputConnected(2)) {
range = this.getInputData(2);
this.properties.range = range;
}
uniforms.u_distance = distance;
uniforms.u_range = range;
gl.disable(gl.BLEND);
gl.disable(gl.DEPTH_TEST);
var mesh = Mesh.getScreenQuad();
if (!LGraphTextureDepthRange._shader) {
LGraphTextureDepthRange._shader = new GL.Shader(
Shader.SCREEN_VERTEX_SHADER,
LGraphTextureDepthRange.pixel_shader
);
LGraphTextureDepthRange._shader_onlydepth = new GL.Shader(
Shader.SCREEN_VERTEX_SHADER,
LGraphTextureDepthRange.pixel_shader,
{ ONLY_DEPTH: "" }
);
}
var shader = this.properties.only_depth
? LGraphTextureDepthRange._shader_onlydepth
: LGraphTextureDepthRange._shader;
//NEAR AND FAR PLANES
var planes = null;
if (tex.near_far_planes) {
planes = tex.near_far_planes;
} else if (window.LS && LS.Renderer._main_camera) {
planes = LS.Renderer._main_camera._uniforms.u_camera_planes;
} else {
planes = [0.1, 1000];
} //hardcoded
uniforms.u_camera_planes = planes;
this._temp_texture.drawTo(function() {
tex.bind(0);
shader.uniforms(uniforms).draw(mesh);
});
this._temp_texture.near_far_planes = planes;
this.setOutputData(0, this._temp_texture);
};
LGraphTextureDepthRange.pixel_shader =
"precision highp float;\n\
precision highp float;\n\
varying vec2 v_coord;\n\
uniform sampler2D u_texture;\n\
uniform vec2 u_camera_planes;\n\
uniform float u_distance;\n\
uniform float u_range;\n\
\n\
float LinearDepth()\n\
{\n\
float zNear = u_camera_planes.x;\n\
float zFar = u_camera_planes.y;\n\
float depth = texture2D(u_texture, v_coord).x;\n\
depth = depth * 2.0 - 1.0;\n\
return zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\
}\n\
\n\
void main() {\n\
float depth = LinearDepth();\n\
#ifdef ONLY_DEPTH\n\
gl_FragColor = vec4(depth);\n\
#else\n\
float diff = abs(depth * u_camera_planes.y - u_distance);\n\
float dof = 1.0;\n\
if(diff <= u_range)\n\
dof = diff / u_range;\n\
gl_FragColor = vec4(dof);\n\
#endif\n\
}\n\
";
LiteGraph.registerNodeType(
"texture/depth_range",
LGraphTextureDepthRange
);
// Texture Blur *****************************************
function LGraphTextureBlur() {
this.addInput("Texture", "Texture");
this.addInput("Iterations", "number");
this.addInput("Intensity", "number");
this.addOutput("Blurred", "Texture");
this.properties = {
intensity: 1,
iterations: 1,
preserve_aspect: false,
scale: [1, 1],
precision: LGraphTexture.DEFAULT
};
}
LGraphTextureBlur.title = "Blur";
LGraphTextureBlur.desc = "Blur a texture";
LGraphTextureBlur.widgets_info = {
precision: { widget: "combo", values: LGraphTexture.MODE_VALUES }
};
LGraphTextureBlur.max_iterations = 20;
LGraphTextureBlur.prototype.onExecute = function() {
var tex = this.getInputData(0);
if (!tex) {
return;
}
if (!this.isOutputConnected(0)) {
return;
} //saves work
var temp = this._final_texture;
if (
!temp ||
temp.width != tex.width ||
temp.height != tex.height ||
temp.type != tex.type
) {
//we need two textures to do the blurring
//this._temp_texture = new GL.Texture( tex.width, tex.height, { type: tex.type, format: gl.RGBA, filter: gl.LINEAR });
temp = this._final_texture = new GL.Texture(
tex.width,
tex.height,
{ type: tex.type, format: gl.RGBA, filter: gl.LINEAR }
);
}
//iterations
var iterations = this.properties.iterations;
if (this.isInputConnected(1)) {
iterations = this.getInputData(1);
this.properties.iterations = iterations;
}
iterations = Math.min(
Math.floor(iterations),
LGraphTextureBlur.max_iterations
);
if (iterations == 0) {
//skip blurring
this.setOutputData(0, tex);
return;
}
var intensity = this.properties.intensity;
if (this.isInputConnected(2)) {
intensity = this.getInputData(2);
this.properties.intensity = intensity;
}
//blur sometimes needs an aspect correction
var aspect = LiteGraph.camera_aspect;
if (!aspect && window.gl !== undefined) {
aspect = gl.canvas.height / gl.canvas.width;
}
if (!aspect) {
aspect = 1;
}
aspect = this.properties.preserve_aspect ? aspect : 1;
var scale = this.properties.scale || [1, 1];
tex.applyBlur(aspect * scale[0], scale[1], intensity, temp);
for (var i = 1; i < iterations; ++i) {
temp.applyBlur(
aspect * scale[0] * (i + 1),
scale[1] * (i + 1),
intensity
);
}
this.setOutputData(0, temp);
};
/*
LGraphTextureBlur.pixel_shader = "precision highp float;\n\
precision highp float;\n\
varying vec2 v_coord;\n\
uniform sampler2D u_texture;\n\
uniform vec2 u_offset;\n\
uniform float u_intensity;\n\
void main() {\n\
vec4 sum = vec4(0.0);\n\
vec4 center = texture2D(u_texture, v_coord);\n\
sum += texture2D(u_texture, v_coord + u_offset * -4.0) * 0.05/0.98;\n\
sum += texture2D(u_texture, v_coord + u_offset * -3.0) * 0.09/0.98;\n\
sum += texture2D(u_texture, v_coord + u_offset * -2.0) * 0.12/0.98;\n\
sum += texture2D(u_texture, v_coord + u_offset * -1.0) * 0.15/0.98;\n\
sum += center * 0.16/0.98;\n\
sum += texture2D(u_texture, v_coord + u_offset * 4.0) * 0.05/0.98;\n\
sum += texture2D(u_texture, v_coord + u_offset * 3.0) * 0.09/0.98;\n\
sum += texture2D(u_texture, v_coord + u_offset * 2.0) * 0.12/0.98;\n\
sum += texture2D(u_texture, v_coord + u_offset * 1.0) * 0.15/0.98;\n\
gl_FragColor = u_intensity * sum;\n\
}\n\
";
*/
LiteGraph.registerNodeType("texture/blur", LGraphTextureBlur);
// Texture Glow *****************************************
//based in https://catlikecoding.com/unity/tutorials/advanced-rendering/bloom/
function LGraphTextureGlow() {
this.addInput("in", "Texture");
this.addInput("dirt", "Texture");
this.addOutput("out", "Texture");
this.addOutput("glow", "Texture");
this.properties = {
enabled: true,
intensity: 1,
persistence: 0.99,
iterations: 16,
threshold: 0,
scale: 1,
dirt_factor: 0.5,
precision: LGraphTexture.DEFAULT
};
this._textures = [];
this._uniforms = {
u_intensity: 1,
u_texture: 0,
u_glow_texture: 1,
u_threshold: 0,
u_texel_size: vec2.create()
};
}
LGraphTextureGlow.title = "Glow";
LGraphTextureGlow.desc = "Filters a texture giving it a glow effect";
LGraphTextureGlow.weights = new Float32Array([0.5, 0.4, 0.3, 0.2]);
LGraphTextureGlow.widgets_info = {
iterations: {
type: "number",
min: 0,
max: 16,
step: 1,
precision: 0
},
threshold: {
type: "number",
min: 0,
max: 10,
step: 0.01,
precision: 2
},
precision: { widget: "combo", values: LGraphTexture.MODE_VALUES }
};
LGraphTextureGlow.prototype.onGetInputs = function() {
return [
["enabled", "boolean"],
["threshold", "number"],
["intensity", "number"],
["persistence", "number"],
["iterations", "number"],
["dirt_factor", "number"]
];
};
LGraphTextureGlow.prototype.onGetOutputs = function() {
return [["average", "Texture"]];
};
LGraphTextureGlow.prototype.onExecute = function() {
var tex = this.getInputData(0);
if (!tex) {
return;
}
if (!this.isAnyOutputConnected()) {
return;
} //saves work
if (
this.properties.precision === LGraphTexture.PASS_THROUGH ||
this.getInputOrProperty("enabled") === false
) {
this.setOutputData(0, tex);
return;
}
var width = tex.width;
var height = tex.height;
var texture_info = {
format: tex.format,
type: tex.type,
minFilter: GL.LINEAR,
magFilter: GL.LINEAR,
wrap: gl.CLAMP_TO_EDGE
};
var type = LGraphTexture.getTextureType(
this.properties.precision,
tex
);
var uniforms = this._uniforms;
var textures = this._textures;
//cut
var shader = LGraphTextureGlow._cut_shader;
if (!shader) {
shader = LGraphTextureGlow._cut_shader = new GL.Shader(
GL.Shader.SCREEN_VERTEX_SHADER,
LGraphTextureGlow.cut_pixel_shader
);
}
gl.disable(gl.DEPTH_TEST);
gl.disable(gl.BLEND);
uniforms.u_threshold = this.getInputOrProperty("threshold");
var currentDestination = (textures[0] = GL.Texture.getTemporary(
width,
height,
texture_info
));
tex.blit(currentDestination, shader.uniforms(uniforms));
var currentSource = currentDestination;
var iterations = this.getInputOrProperty("iterations");
iterations = Math.clamp(iterations, 1, 16) | 0;
var texel_size = uniforms.u_texel_size;
var intensity = this.getInputOrProperty("intensity");
uniforms.u_intensity = 1;
uniforms.u_delta = this.properties.scale; //1
//downscale/upscale shader
var shader = LGraphTextureGlow._shader;
if (!shader) {
shader = LGraphTextureGlow._shader = new GL.Shader(
GL.Shader.SCREEN_VERTEX_SHADER,
LGraphTextureGlow.scale_pixel_shader
);
}
var i = 1;
//downscale
for (; i < iterations; i++) {
width = width >> 1;
if ((height | 0) > 1) {
height = height >> 1;
}
if (width < 2) {
break;
}
currentDestination = textures[i] = GL.Texture.getTemporary(
width,
height,
texture_info
);
texel_size[0] = 1 / currentSource.width;
texel_size[1] = 1 / currentSource.height;
currentSource.blit(
currentDestination,
shader.uniforms(uniforms)
);
currentSource = currentDestination;
}
//average
if (this.isOutputConnected(2)) {
var average_texture = this._average_texture;
if (
!average_texture ||
average_texture.type != tex.type ||
average_texture.format != tex.format
) {
average_texture = this._average_texture = new GL.Texture(
1,
1,
{
type: tex.type,
format: tex.format,
filter: gl.LINEAR
}
);
}
texel_size[0] = 1 / currentSource.width;
texel_size[1] = 1 / currentSource.height;
uniforms.u_intensity = intensity;
uniforms.u_delta = 1;
currentSource.blit(average_texture, shader.uniforms(uniforms));
this.setOutputData(2, average_texture);
}
//upscale and blend
gl.enable(gl.BLEND);
gl.blendFunc(gl.ONE, gl.ONE);
uniforms.u_intensity = this.getInputOrProperty("persistence");
uniforms.u_delta = 0.5;
for (
i -= 2;
i >= 0;
i-- // i-=2 => -1 to point to last element in array, -1 to go to texture above
) {
currentDestination = textures[i];
textures[i] = null;
texel_size[0] = 1 / currentSource.width;
texel_size[1] = 1 / currentSource.height;
currentSource.blit(
currentDestination,
shader.uniforms(uniforms)
);
GL.Texture.releaseTemporary(currentSource);
currentSource = currentDestination;
}
gl.disable(gl.BLEND);
//glow
if (this.isOutputConnected(1)) {
var glow_texture = this._glow_texture;
if (
!glow_texture ||
glow_texture.width != tex.width ||
glow_texture.height != tex.height ||
glow_texture.type != type ||
glow_texture.format != tex.format
) {
glow_texture = this._glow_texture = new GL.Texture(
tex.width,
tex.height,
{ type: type, format: tex.format, filter: gl.LINEAR }
);
}
currentSource.blit(glow_texture);
this.setOutputData(1, glow_texture);
}
//final composition
if (this.isOutputConnected(0)) {
var final_texture = this._final_texture;
if (
!final_texture ||
final_texture.width != tex.width ||
final_texture.height != tex.height ||
final_texture.type != type ||
final_texture.format != tex.format
) {
final_texture = this._final_texture = new GL.Texture(
tex.width,
tex.height,
{ type: type, format: tex.format, filter: gl.LINEAR }
);
}
var dirt_texture = this.getInputData(1);
var dirt_factor = this.getInputOrProperty("dirt_factor");
uniforms.u_intensity = intensity;
shader = dirt_texture
? LGraphTextureGlow._dirt_final_shader
: LGraphTextureGlow._final_shader;
if (!shader) {
if (dirt_texture) {
shader = LGraphTextureGlow._dirt_final_shader = new GL.Shader(
GL.Shader.SCREEN_VERTEX_SHADER,
LGraphTextureGlow.final_pixel_shader,
{ USE_DIRT: "" }
);
} else {
shader = LGraphTextureGlow._final_shader = new GL.Shader(
GL.Shader.SCREEN_VERTEX_SHADER,
LGraphTextureGlow.final_pixel_shader
);
}
}
final_texture.drawTo(function() {
tex.bind(0);
currentSource.bind(1);
if (dirt_texture) {
shader.setUniform("u_dirt_factor", dirt_factor);
shader.setUniform(
"u_dirt_texture",
dirt_texture.bind(2)
);
}
shader.toViewport(uniforms);
});
this.setOutputData(0, final_texture);
}
GL.Texture.releaseTemporary(currentSource);
};
LGraphTextureGlow.cut_pixel_shader =
"precision highp float;\n\
varying vec2 v_coord;\n\
uniform sampler2D u_texture;\n\
uniform float u_threshold;\n\
void main() {\n\
gl_FragColor = max( texture2D( u_texture, v_coord ) - vec4( u_threshold ), vec4(0.0) );\n\
}";
LGraphTextureGlow.scale_pixel_shader =
"precision highp float;\n\
varying vec2 v_coord;\n\
uniform sampler2D u_texture;\n\
uniform vec2 u_texel_size;\n\
uniform float u_delta;\n\
uniform float u_intensity;\n\
\n\
vec4 sampleBox(vec2 uv) {\n\
vec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\
vec4 s = texture2D( u_texture, uv + o.xy ) + texture2D( u_texture, uv + o.zy) + texture2D( u_texture, uv + o.xw) + texture2D( u_texture, uv + o.zw);\n\
return s * 0.25;\n\
}\n\
void main() {\n\
gl_FragColor = u_intensity * sampleBox( v_coord );\n\
}";
LGraphTextureGlow.final_pixel_shader =
"precision highp float;\n\
varying vec2 v_coord;\n\
uniform sampler2D u_texture;\n\
uniform sampler2D u_glow_texture;\n\
#ifdef USE_DIRT\n\
uniform sampler2D u_dirt_texture;\n\
#endif\n\
uniform vec2 u_texel_size;\n\
uniform float u_delta;\n\
uniform float u_intensity;\n\
uniform float u_dirt_factor;\n\
\n\
vec4 sampleBox(vec2 uv) {\n\
vec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\
vec4 s = texture2D( u_glow_texture, uv + o.xy ) + texture2D( u_glow_texture, uv + o.zy) + texture2D( u_glow_texture, uv + o.xw) + texture2D( u_glow_texture, uv + o.zw);\n\
return s * 0.25;\n\
}\n\
void main() {\n\
vec4 glow = sampleBox( v_coord );\n\
#ifdef USE_DIRT\n\
glow = mix( glow, glow * texture2D( u_dirt_texture, v_coord ), u_dirt_factor );\n\
#endif\n\
gl_FragColor = texture2D( u_texture, v_coord ) + u_intensity * glow;\n\
}";
LiteGraph.registerNodeType("texture/glow", LGraphTextureGlow);
// Texture Filter *****************************************
function LGraphTextureKuwaharaFilter() {
this.addInput("Texture", "Texture");
this.addOutput("Filtered", "Texture");
this.properties = { intensity: 1, radius: 5 };
}
LGraphTextureKuwaharaFilter.title = "Kuwahara Filter";
LGraphTextureKuwaharaFilter.desc =
"Filters a texture giving an artistic oil canvas painting";
LGraphTextureKuwaharaFilter.max_radius = 10;
LGraphTextureKuwaharaFilter._shaders = [];
LGraphTextureKuwaharaFilter.prototype.onExecute = function() {
var tex = this.getInputData(0);
if (!tex) {
return;
}
if (!this.isOutputConnected(0)) {
return;
} //saves work
var temp = this._temp_texture;
if (
!temp ||
temp.width != tex.width ||
temp.height != tex.height ||
temp.type != tex.type
) {
this._temp_texture = new GL.Texture(tex.width, tex.height, {
type: tex.type,
format: gl.RGBA,
filter: gl.LINEAR
});
}
//iterations
var radius = this.properties.radius;
radius = Math.min(
Math.floor(radius),
LGraphTextureKuwaharaFilter.max_radius
);
if (radius == 0) {
//skip blurring
this.setOutputData(0, tex);
return;
}
var intensity = this.properties.intensity;
//blur sometimes needs an aspect correction
var aspect = LiteGraph.camera_aspect;
if (!aspect && window.gl !== undefined) {
aspect = gl.canvas.height / gl.canvas.width;
}
if (!aspect) {
aspect = 1;
}
aspect = this.properties.preserve_aspect ? aspect : 1;
if (!LGraphTextureKuwaharaFilter._shaders[radius]) {
LGraphTextureKuwaharaFilter._shaders[radius] = new GL.Shader(
Shader.SCREEN_VERTEX_SHADER,
LGraphTextureKuwaharaFilter.pixel_shader,
{ RADIUS: radius.toFixed(0) }
);
}
var shader = LGraphTextureKuwaharaFilter._shaders[radius];
var mesh = GL.Mesh.getScreenQuad();
tex.bind(0);
this._temp_texture.drawTo(function() {
shader
.uniforms({
u_texture: 0,
u_intensity: intensity,
u_resolution: [tex.width, tex.height],
u_iResolution: [1 / tex.width, 1 / tex.height]
})
.draw(mesh);
});
this.setOutputData(0, this._temp_texture);
};
//from https://www.shadertoy.com/view/MsXSz4
LGraphTextureKuwaharaFilter.pixel_shader =
"\n\
precision highp float;\n\
varying vec2 v_coord;\n\
uniform sampler2D u_texture;\n\
uniform float u_intensity;\n\
uniform vec2 u_resolution;\n\
uniform vec2 u_iResolution;\n\
#ifndef RADIUS\n\
#define RADIUS 7\n\
#endif\n\
void main() {\n\
\n\
const int radius = RADIUS;\n\
vec2 fragCoord = v_coord;\n\
vec2 src_size = u_iResolution;\n\
vec2 uv = v_coord;\n\
float n = float((radius + 1) * (radius + 1));\n\
int i;\n\
int j;\n\
vec3 m0 = vec3(0.0); vec3 m1 = vec3(0.0); vec3 m2 = vec3(0.0); vec3 m3 = vec3(0.0);\n\
vec3 s0 = vec3(0.0); vec3 s1 = vec3(0.0); vec3 s2 = vec3(0.0); vec3 s3 = vec3(0.0);\n\
vec3 c;\n\
\n\
for (int j = -radius; j <= 0; ++j) {\n\
for (int i = -radius; i <= 0; ++i) {\n\
c = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\
m0 += c;\n\
s0 += c * c;\n\
}\n\
}\n\
\n\
for (int j = -radius; j <= 0; ++j) {\n\
for (int i = 0; i <= radius; ++i) {\n\
c = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\
m1 += c;\n\
s1 += c * c;\n\
}\n\
}\n\
\n\
for (int j = 0; j <= radius; ++j) {\n\
for (int i = 0; i <= radius; ++i) {\n\
c = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\
m2 += c;\n\
s2 += c * c;\n\
}\n\
}\n\
\n\
for (int j = 0; j <= radius; ++j) {\n\
for (int i = -radius; i <= 0; ++i) {\n\
c = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\
m3 += c;\n\
s3 += c * c;\n\
}\n\
}\n\
\n\
float min_sigma2 = 1e+2;\n\
m0 /= n;\n\
s0 = abs(s0 / n - m0 * m0);\n\
\n\
float sigma2 = s0.r + s0.g + s0.b;\n\
if (sigma2 < min_sigma2) {\n\
min_sigma2 = sigma2;\n\
gl_FragColor = vec4(m0, 1.0);\n\
}\n\
\n\
m1 /= n;\n\
s1 = abs(s1 / n - m1 * m1);\n\
\n\
sigma2 = s1.r + s1.g + s1.b;\n\
if (sigma2 < min_sigma2) {\n\
min_sigma2 = sigma2;\n\
gl_FragColor = vec4(m1, 1.0);\n\
}\n\
\n\
m2 /= n;\n\
s2 = abs(s2 / n - m2 * m2);\n\
\n\
sigma2 = s2.r + s2.g + s2.b;\n\
if (sigma2 < min_sigma2) {\n\
min_sigma2 = sigma2;\n\
gl_FragColor = vec4(m2, 1.0);\n\
}\n\
\n\
m3 /= n;\n\
s3 = abs(s3 / n - m3 * m3);\n\
\n\
sigma2 = s3.r + s3.g + s3.b;\n\
if (sigma2 < min_sigma2) {\n\
min_sigma2 = sigma2;\n\
gl_FragColor = vec4(m3, 1.0);\n\
}\n\
}\n\
";
LiteGraph.registerNodeType(
"texture/kuwahara",
LGraphTextureKuwaharaFilter
);
// Texture *****************************************
function LGraphTextureXDoGFilter() {
this.addInput("Texture", "Texture");
this.addOutput("Filtered", "Texture");
this.properties = {
sigma: 1.4,
k: 1.6,
p: 21.7,
epsilon: 79,
phi: 0.017
};
}
LGraphTextureXDoGFilter.title = "XDoG Filter";
LGraphTextureXDoGFilter.desc =
"Filters a texture giving an artistic ink style";
LGraphTextureXDoGFilter.max_radius = 10;
LGraphTextureXDoGFilter._shaders = [];
LGraphTextureXDoGFilter.prototype.onExecute = function() {
var tex = this.getInputData(0);
if (!tex) {
return;
}
if (!this.isOutputConnected(0)) {
return;
} //saves work
var temp = this._temp_texture;
if (
!temp ||
temp.width != tex.width ||
temp.height != tex.height ||
temp.type != tex.type
) {
this._temp_texture = new GL.Texture(tex.width, tex.height, {
type: tex.type,
format: gl.RGBA,
filter: gl.LINEAR
});
}
if (!LGraphTextureXDoGFilter._xdog_shader) {
LGraphTextureXDoGFilter._xdog_shader = new GL.Shader(
Shader.SCREEN_VERTEX_SHADER,
LGraphTextureXDoGFilter.xdog_pixel_shader
);
}
var shader = LGraphTextureXDoGFilter._xdog_shader;
var mesh = GL.Mesh.getScreenQuad();
var sigma = this.properties.sigma;
var k = this.properties.k;
var p = this.properties.p;
var epsilon = this.properties.epsilon;
var phi = this.properties.phi;
tex.bind(0);
this._temp_texture.drawTo(function() {
shader
.uniforms({
src: 0,
sigma: sigma,
k: k,
p: p,
epsilon: epsilon,
phi: phi,
cvsWidth: tex.width,
cvsHeight: tex.height
})
.draw(mesh);
});
this.setOutputData(0, this._temp_texture);
};
//from https://github.com/RaymondMcGuire/GPU-Based-Image-Processing-Tools/blob/master/lib_webgl/scripts/main.js
LGraphTextureXDoGFilter.xdog_pixel_shader =
"\n\
precision highp float;\n\
uniform sampler2D src;\n\n\
uniform float cvsHeight;\n\
uniform float cvsWidth;\n\n\
uniform float sigma;\n\
uniform float k;\n\
uniform float p;\n\
uniform float epsilon;\n\
uniform float phi;\n\
varying vec2 v_coord;\n\n\
float cosh(float val)\n\
{\n\
float tmp = exp(val);\n\
float cosH = (tmp + 1.0 / tmp) / 2.0;\n\
return cosH;\n\
}\n\n\
float tanh(float val)\n\
{\n\
float tmp = exp(val);\n\
float tanH = (tmp - 1.0 / tmp) / (tmp + 1.0 / tmp);\n\
return tanH;\n\
}\n\n\
float sinh(float val)\n\
{\n\
float tmp = exp(val);\n\
float sinH = (tmp - 1.0 / tmp) / 2.0;\n\
return sinH;\n\
}\n\n\
void main(void){\n\
vec3 destColor = vec3(0.0);\n\
float tFrag = 1.0 / cvsHeight;\n\
float sFrag = 1.0 / cvsWidth;\n\
vec2 Frag = vec2(sFrag,tFrag);\n\
vec2 uv = gl_FragCoord.st;\n\
float twoSigmaESquared = 2.0 * sigma * sigma;\n\
float twoSigmaRSquared = twoSigmaESquared * k * k;\n\
int halfWidth = int(ceil( 1.0 * sigma * k ));\n\n\
const int MAX_NUM_ITERATION = 99999;\n\
vec2 sum = vec2(0.0);\n\
vec2 norm = vec2(0.0);\n\n\
for(int cnt=0;cnt<MAX_NUM_ITERATION;cnt++){\n\
if(cnt > (2*halfWidth+1)*(2*halfWidth+1)){break;}\n\
int i = int(cnt / (2*halfWidth+1)) - halfWidth;\n\
int j = cnt - halfWidth - int(cnt / (2*halfWidth+1)) * (2*halfWidth+1);\n\n\
float d = length(vec2(i,j));\n\
vec2 kernel = vec2( exp( -d * d / twoSigmaESquared ), \n\
exp( -d * d / twoSigmaRSquared ));\n\n\
vec2 L = texture2D(src, (uv + vec2(i,j)) * Frag).xx;\n\n\
norm += kernel;\n\
sum += kernel * L;\n\
}\n\n\
sum /= norm;\n\n\
float H = 100.0 * ((1.0 + p) * sum.x - p * sum.y);\n\
float edge = ( H > epsilon )? 1.0 : 1.0 + tanh( phi * (H - epsilon));\n\
destColor = vec3(edge);\n\
gl_FragColor = vec4(destColor, 1.0);\n\
}";
LiteGraph.registerNodeType("texture/xDoG", LGraphTextureXDoGFilter);
// Texture Webcam *****************************************
function LGraphTextureWebcam() {
this.addOutput("Webcam", "Texture");
this.properties = { texture_name: "", facingMode: "user" };
this.boxcolor = "black";
this.version = 0;
}
LGraphTextureWebcam.title = "Webcam";
LGraphTextureWebcam.desc = "Webcam texture";
LGraphTextureWebcam.is_webcam_open = false;
LGraphTextureWebcam.prototype.openStream = function() {
if (!navigator.getUserMedia) {
//console.log('getUserMedia() is not supported in your browser, use chrome and enable WebRTC from about://flags');
return;
}
this._waiting_confirmation = true;
// Not showing vendor prefixes.
var constraints = {
audio: false,
video: { facingMode: this.properties.facingMode }
};
navigator.mediaDevices
.getUserMedia(constraints)
.then(this.streamReady.bind(this))
.catch(onFailSoHard);
var that = this;
function onFailSoHard(e) {
LGraphTextureWebcam.is_webcam_open = false;
console.log("Webcam rejected", e);
that._webcam_stream = false;
that.boxcolor = "red";
that.trigger("stream_error");
}
};
LGraphTextureWebcam.prototype.closeStream = function() {
if (this._webcam_stream) {
var tracks = this._webcam_stream.getTracks();
if (tracks.length) {
for (var i = 0; i < tracks.length; ++i) {
tracks[i].stop();
}
}
LGraphTextureWebcam.is_webcam_open = false;
this._webcam_stream = null;
this._video = null;
this.boxcolor = "black";
this.trigger("stream_closed");
}
};
LGraphTextureWebcam.prototype.streamReady = function(localMediaStream) {
this._webcam_stream = localMediaStream;
//this._waiting_confirmation = false;
this.boxcolor = "green";
var video = this._video;
if (!video) {
video = document.createElement("video");
video.autoplay = true;
video.srcObject = localMediaStream;
this._video = video;
//document.body.appendChild( video ); //debug
//when video info is loaded (size and so)
video.onloadedmetadata = function(e) {
// Ready to go. Do some stuff.
LGraphTextureWebcam.is_webcam_open = true;
console.log(e);
};
}
this.trigger("stream_ready", video);
};
LGraphTextureWebcam.prototype.onPropertyChanged = function(
name,
value
) {
if (name == "facingMode") {
this.properties.facingMode = value;
this.closeStream();
this.openStream();
}
};
LGraphTextureWebcam.prototype.onRemoved = function() {
if (!this._webcam_stream) {
return;
}
var tracks = this._webcam_stream.getTracks();
if (tracks.length) {
for (var i = 0; i < tracks.length; ++i) {
tracks[i].stop();
}
}
this._webcam_stream = null;
this._video = null;
};
LGraphTextureWebcam.prototype.onDrawBackground = function(ctx) {
if (this.flags.collapsed || this.size[1] <= 20) {
return;
}
if (!this._video) {
return;
}
//render to graph canvas
ctx.save();
if (!ctx.webgl) {
//reverse image
ctx.drawImage(this._video, 0, 0, this.size[0], this.size[1]);
} else {
if (this._video_texture) {
ctx.drawImage(
this._video_texture,
0,
0,
this.size[0],
this.size[1]
);
}
}
ctx.restore();
};
LGraphTextureWebcam.prototype.onExecute = function() {
if (this._webcam_stream == null && !this._waiting_confirmation) {
this.openStream();
}
if (!this._video || !this._video.videoWidth) {
return;
}
var width = this._video.videoWidth;
var height = this._video.videoHeight;
var temp = this._video_texture;
if (!temp || temp.width != width || temp.height != height) {
this._video_texture = new GL.Texture(width, height, {
format: gl.RGB,
filter: gl.LINEAR
});
}
this._video_texture.uploadImage(this._video);
this._video_texture.version = ++this.version;
if (this.properties.texture_name) {
var container = LGraphTexture.getTexturesContainer();
container[this.properties.texture_name] = this._video_texture;
}
this.setOutputData(0, this._video_texture);
for (var i = 1; i < this.outputs.length; ++i) {
if (!this.outputs[i]) {
continue;
}
switch (this.outputs[i].name) {
case "width":
this.setOutputData(i, this._video.videoWidth);
break;
case "height":
this.setOutputData(i, this._video.videoHeight);
break;
}
}
};
LGraphTextureWebcam.prototype.onGetOutputs = function() {
return [
["width", "number"],
["height", "number"],
["stream_ready", LiteGraph.EVENT],
["stream_closed", LiteGraph.EVENT],
["stream_error", LiteGraph.EVENT]
];
};
LiteGraph.registerNodeType("texture/webcam", LGraphTextureWebcam);
//from https://github.com/spite/Wagner
function LGraphLensFX() {
this.addInput("in", "Texture");
this.addInput("f", "number");
this.addOutput("out", "Texture");
this.properties = {
enabled: true,
factor: 1,
precision: LGraphTexture.LOW
};
this._uniforms = { u_texture: 0, u_factor: 1 };
}
LGraphLensFX.title = "Lens FX";
LGraphLensFX.desc = "distortion and chromatic aberration";
LGraphLensFX.widgets_info = {
precision: { widget: "combo", values: LGraphTexture.MODE_VALUES }
};
LGraphLensFX.prototype.onGetInputs = function() {
return [["enabled", "boolean"]];
};
LGraphLensFX.prototype.onExecute = function() {
var tex = this.getInputData(0);
if (!tex) {
return;
}
if (!this.isOutputConnected(0)) {
return;
} //saves work
if (
this.properties.precision === LGraphTexture.PASS_THROUGH ||
this.getInputOrProperty("enabled") === false
) {
this.setOutputData(0, tex);
return;
}
var temp = this._temp_texture;
if (
!temp ||
temp.width != tex.width ||
temp.height != tex.height ||
temp.type != tex.type
) {
temp = this._temp_texture = new GL.Texture(
tex.width,
tex.height,
{ type: tex.type, format: gl.RGBA, filter: gl.LINEAR }
);
}
var shader = LGraphLensFX._shader;
if (!shader) {
shader = LGraphLensFX._shader = new GL.Shader(
GL.Shader.SCREEN_VERTEX_SHADER,
LGraphLensFX.pixel_shader
);
}
var factor = this.getInputData(1);
if (factor == null) {
factor = this.properties.factor;
}
var uniforms = this._uniforms;
uniforms.u_factor = factor;
//apply shader
gl.disable(gl.DEPTH_TEST);
temp.drawTo(function() {
tex.bind(0);
shader.uniforms(uniforms).draw(GL.Mesh.getScreenQuad());
});
this.setOutputData(0, temp);
};
LGraphLensFX.pixel_shader =
"precision highp float;\n\
varying vec2 v_coord;\n\
uniform sampler2D u_texture;\n\
uniform float u_factor;\n\
vec2 barrelDistortion(vec2 coord, float amt) {\n\
vec2 cc = coord - 0.5;\n\
float dist = dot(cc, cc);\n\
return coord + cc * dist * amt;\n\
}\n\
\n\
float sat( float t )\n\
{\n\
return clamp( t, 0.0, 1.0 );\n\
}\n\
\n\
float linterp( float t ) {\n\
return sat( 1.0 - abs( 2.0*t - 1.0 ) );\n\
}\n\
\n\
float remap( float t, float a, float b ) {\n\
return sat( (t - a) / (b - a) );\n\
}\n\
\n\
vec4 spectrum_offset( float t ) {\n\
vec4 ret;\n\
float lo = step(t,0.5);\n\
float hi = 1.0-lo;\n\
float w = linterp( remap( t, 1.0/6.0, 5.0/6.0 ) );\n\
ret = vec4(lo,1.0,hi, 1.) * vec4(1.0-w, w, 1.0-w, 1.);\n\
\n\
return pow( ret, vec4(1.0/2.2) );\n\
}\n\
\n\
const float max_distort = 2.2;\n\
const int num_iter = 12;\n\
const float reci_num_iter_f = 1.0 / float(num_iter);\n\
\n\
void main()\n\
{ \n\
vec2 uv=v_coord;\n\
vec4 sumcol = vec4(0.0);\n\
vec4 sumw = vec4(0.0); \n\
for ( int i=0; i<num_iter;++i )\n\
{\n\
float t = float(i) * reci_num_iter_f;\n\
vec4 w = spectrum_offset( t );\n\
sumw += w;\n\
sumcol += w * texture2D( u_texture, barrelDistortion(uv, .6 * max_distort*t * u_factor ) );\n\
}\n\
gl_FragColor = sumcol / sumw;\n\
}";
LiteGraph.registerNodeType("texture/lensfx", LGraphLensFX);
//simple exposition, but plan to expand it to support different gamma curves
function LGraphExposition() {
this.addInput("in", "Texture");
this.addInput("exp", "number");
this.addOutput("out", "Texture");
this.properties = { exposition: 1, precision: LGraphTexture.LOW };
this._uniforms = { u_texture: 0, u_exposition: 1 };
}
LGraphExposition.title = "Exposition";
LGraphExposition.desc = "Controls texture exposition";
LGraphExposition.widgets_info = {
exposition: { widget: "slider", min: 0, max: 3 },
precision: { widget: "combo", values: LGraphTexture.MODE_VALUES }
};
LGraphExposition.prototype.onExecute = function() {
var tex = this.getInputData(0);
if (!tex) {
return;
}
if (!this.isOutputConnected(0)) {
return;
} //saves work
var temp = this._temp_texture;
if (
!temp ||
temp.width != tex.width ||
temp.height != tex.height ||
temp.type != tex.type
) {
temp = this._temp_texture = new GL.Texture(
tex.width,
tex.height,
{ type: tex.type, format: gl.RGBA, filter: gl.LINEAR }
);
}
var shader = LGraphExposition._shader;
if (!shader) {
shader = LGraphExposition._shader = new GL.Shader(
GL.Shader.SCREEN_VERTEX_SHADER,
LGraphExposition.pixel_shader
);
}
var exp = this.properties.exposition;
var exp_input = this.getInputData(1);
if (exp_input != null) {
exp = this.properties.exposition = exp_input;
}
var uniforms = this._uniforms;
//apply shader
temp.drawTo(function() {
gl.disable(gl.DEPTH_TEST);
tex.bind(0);
shader.uniforms(uniforms).draw(GL.Mesh.getScreenQuad());
});
this.setOutputData(0, temp);
};
LGraphExposition.pixel_shader =
"precision highp float;\n\
varying vec2 v_coord;\n\
uniform sampler2D u_texture;\n\
uniform float u_exposition;\n\
\n\
void main() {\n\
vec4 color = texture2D( u_texture, v_coord );\n\
gl_FragColor = vec4( color.xyz * u_exposition, color.a );\n\
}";
LiteGraph.registerNodeType("texture/exposition", LGraphExposition);
function LGraphToneMapping() {
this.addInput("in", "Texture");
this.addInput("avg", "number,Texture");
this.addOutput("out", "Texture");
this.properties = {
enabled: true,
scale: 1,
gamma: 1,
average_lum: 1,
lum_white: 1,
precision: LGraphTexture.LOW
};
this._uniforms = {
u_texture: 0,
u_lumwhite2: 1,
u_igamma: 1,
u_scale: 1,
u_average_lum: 1
};
}
LGraphToneMapping.title = "Tone Mapping";
LGraphToneMapping.desc =
"Applies Tone Mapping to convert from high to low";
LGraphToneMapping.widgets_info = {
precision: { widget: "combo", values: LGraphTexture.MODE_VALUES }
};
LGraphToneMapping.prototype.onGetInputs = function() {
return [["enabled", "boolean"]];
};
LGraphToneMapping.prototype.onExecute = function() {
var tex = this.getInputData(0);
if (!tex) {
return;
}
if (!this.isOutputConnected(0)) {
return;
} //saves work
if (
this.properties.precision === LGraphTexture.PASS_THROUGH ||
this.getInputOrProperty("enabled") === false
) {
this.setOutputData(0, tex);
return;
}
var temp = this._temp_texture;
if (
!temp ||
temp.width != tex.width ||
temp.height != tex.height ||
temp.type != tex.type
) {
temp = this._temp_texture = new GL.Texture(
tex.width,
tex.height,
{ type: tex.type, format: gl.RGBA, filter: gl.LINEAR }
);
}
var avg = this.getInputData(1);
if (avg == null) {
avg = this.properties.average_lum;
}
var uniforms = this._uniforms;
var shader = null;
if (avg.constructor === Number) {
this.properties.average_lum = avg;
uniforms.u_average_lum = this.properties.average_lum;
shader = LGraphToneMapping._shader;
if (!shader) {
shader = LGraphToneMapping._shader = new GL.Shader(
GL.Shader.SCREEN_VERTEX_SHADER,
LGraphToneMapping.pixel_shader
);
}
} else if (avg.constructor === GL.Texture) {
uniforms.u_average_texture = avg.bind(1);
shader = LGraphToneMapping._shader_texture;
if (!shader) {
shader = LGraphToneMapping._shader_texture = new GL.Shader(
GL.Shader.SCREEN_VERTEX_SHADER,
LGraphToneMapping.pixel_shader,
{ AVG_TEXTURE: "" }
);
}
}
uniforms.u_lumwhite2 =
this.properties.lum_white * this.properties.lum_white;
uniforms.u_scale = this.properties.scale;
uniforms.u_igamma = 1 / this.properties.gamma;
//apply shader
gl.disable(gl.DEPTH_TEST);
temp.drawTo(function() {
tex.bind(0);
shader.uniforms(uniforms).draw(GL.Mesh.getScreenQuad());
});
this.setOutputData(0, this._temp_texture);
};
LGraphToneMapping.pixel_shader =
"precision highp float;\n\
varying vec2 v_coord;\n\
uniform sampler2D u_texture;\n\
uniform float u_scale;\n\
#ifdef AVG_TEXTURE\n\
uniform sampler2D u_average_texture;\n\
#else\n\
uniform float u_average_lum;\n\
#endif\n\
uniform float u_lumwhite2;\n\
uniform float u_igamma;\n\
vec3 RGB2xyY (vec3 rgb)\n\
{\n\
const mat3 RGB2XYZ = mat3(0.4124, 0.3576, 0.1805,\n\
0.2126, 0.7152, 0.0722,\n\
0.0193, 0.1192, 0.9505);\n\
vec3 XYZ = RGB2XYZ * rgb;\n\
\n\
float f = (XYZ.x + XYZ.y + XYZ.z);\n\
return vec3(XYZ.x / f,\n\
XYZ.y / f,\n\
XYZ.y);\n\
}\n\
\n\
void main() {\n\
vec4 color = texture2D( u_texture, v_coord );\n\
vec3 rgb = color.xyz;\n\
float average_lum = 0.0;\n\
#ifdef AVG_TEXTURE\n\
vec3 pixel = texture2D(u_average_texture,vec2(0.5)).xyz;\n\
average_lum = (pixel.x + pixel.y + pixel.z) / 3.0;\n\
#else\n\
average_lum = u_average_lum;\n\
#endif\n\
//Ld - this part of the code is the same for both versions\n\
float lum = dot(rgb, vec3(0.2126, 0.7152, 0.0722));\n\
float L = (u_scale / average_lum) * lum;\n\
float Ld = (L * (1.0 + L / u_lumwhite2)) / (1.0 + L);\n\
//first\n\
//vec3 xyY = RGB2xyY(rgb);\n\
//xyY.z *= Ld;\n\
//rgb = xyYtoRGB(xyY);\n\
//second\n\
rgb = (rgb / lum) * Ld;\n\
rgb = pow( rgb, vec3( u_igamma ) );\n\
gl_FragColor = vec4( rgb, color.a );\n\
}";
LiteGraph.registerNodeType("texture/tonemapping", LGraphToneMapping);
function LGraphTexturePerlin() {
this.addOutput("out", "Texture");
this.properties = {
width: 512,
height: 512,
seed: 0,
persistence: 0.1,
octaves: 8,
scale: 1,
offset: [0, 0],
amplitude: 1,
precision: LGraphTexture.DEFAULT
};
this._key = 0;
this._texture = null;
this._uniforms = {
u_persistence: 0.1,
u_seed: 0,
u_offset: vec2.create(),
u_scale: 1,
u_viewport: vec2.create()
};
}
LGraphTexturePerlin.title = "Perlin";
LGraphTexturePerlin.desc = "Generates a perlin noise texture";
LGraphTexturePerlin.widgets_info = {
precision: { widget: "combo", values: LGraphTexture.MODE_VALUES },
width: { type: "Number", precision: 0, step: 1 },
height: { type: "Number", precision: 0, step: 1 },
octaves: { type: "Number", precision: 0, step: 1, min: 1, max: 50 }
};
LGraphTexturePerlin.prototype.onGetInputs = function() {
return [
["seed", "Number"],
["persistence", "Number"],
["octaves", "Number"],
["scale", "Number"],
["amplitude", "Number"],
["offset", "vec2"]
];
};
LGraphTexturePerlin.prototype.onExecute = function() {
if (!this.isOutputConnected(0)) {
return;
} //saves work
var w = this.properties.width | 0;
var h = this.properties.height | 0;
if (w == 0) {
w = gl.viewport_data[2];
} //0 means default
if (h == 0) {
h = gl.viewport_data[3];
} //0 means default
var type = LGraphTexture.getTextureType(this.properties.precision);
var temp = this._texture;
if (
!temp ||
temp.width != w ||
temp.height != h ||
temp.type != type
) {
temp = this._texture = new GL.Texture(w, h, {
type: type,
format: gl.RGB,
filter: gl.LINEAR
});
}
var persistence = this.getInputOrProperty("persistence");
var octaves = this.getInputOrProperty("octaves");
var offset = this.getInputOrProperty("offset");
var scale = this.getInputOrProperty("scale");
var amplitude = this.getInputOrProperty("amplitude");
var seed = this.getInputOrProperty("seed");
//reusing old texture
var key =
"" +
w +
h +
type +
persistence +
octaves +
scale +
seed +
offset[0] +
offset[1] +
amplitude;
if (key == this._key) {
this.setOutputData(0, temp);
return;
}
this._key = key;
//gather uniforms
var uniforms = this._uniforms;
uniforms.u_persistence = persistence;
uniforms.u_octaves = octaves;
uniforms.u_offset.set(offset);
uniforms.u_scale = scale;
uniforms.u_amplitude = amplitude;
uniforms.u_seed = seed * 128;
uniforms.u_viewport[0] = w;
uniforms.u_viewport[1] = h;
//render
var shader = LGraphTexturePerlin._shader;
if (!shader) {
shader = LGraphTexturePerlin._shader = new GL.Shader(
GL.Shader.SCREEN_VERTEX_SHADER,
LGraphTexturePerlin.pixel_shader
);
}
gl.disable(gl.BLEND);
gl.disable(gl.DEPTH_TEST);
temp.drawTo(function() {
shader.uniforms(uniforms).draw(GL.Mesh.getScreenQuad());
});
this.setOutputData(0, temp);
};
LGraphTexturePerlin.pixel_shader =
"precision highp float;\n\
varying vec2 v_coord;\n\
uniform vec2 u_offset;\n\
uniform float u_scale;\n\
uniform float u_persistence;\n\
uniform int u_octaves;\n\
uniform float u_amplitude;\n\
uniform vec2 u_viewport;\n\
uniform float u_seed;\n\
#define M_PI 3.14159265358979323846\n\
\n\
float rand(vec2 c){ return fract(sin(dot(c.xy ,vec2( 12.9898 + u_seed,78.233 + u_seed))) * 43758.5453); }\n\
\n\
float noise(vec2 p, float freq ){\n\
float unit = u_viewport.x/freq;\n\
vec2 ij = floor(p/unit);\n\
vec2 xy = mod(p,unit)/unit;\n\
//xy = 3.*xy*xy-2.*xy*xy*xy;\n\
xy = .5*(1.-cos(M_PI*xy));\n\
float a = rand((ij+vec2(0.,0.)));\n\
float b = rand((ij+vec2(1.,0.)));\n\
float c = rand((ij+vec2(0.,1.)));\n\
float d = rand((ij+vec2(1.,1.)));\n\
float x1 = mix(a, b, xy.x);\n\
float x2 = mix(c, d, xy.x);\n\
return mix(x1, x2, xy.y);\n\
}\n\
\n\
float pNoise(vec2 p, int res){\n\
float persistance = u_persistence;\n\
float n = 0.;\n\
float normK = 0.;\n\
float f = 4.;\n\
float amp = 1.0;\n\
int iCount = 0;\n\
for (int i = 0; i<50; i++){\n\
n+=amp*noise(p, f);\n\
f*=2.;\n\
normK+=amp;\n\
amp*=persistance;\n\
if (iCount >= res)\n\
break;\n\
iCount++;\n\
}\n\
float nf = n/normK;\n\
return nf*nf*nf*nf;\n\
}\n\
void main() {\n\
vec2 uv = v_coord * u_scale * u_viewport + u_offset * u_scale;\n\
vec4 color = vec4( pNoise( uv, u_octaves ) * u_amplitude );\n\
gl_FragColor = color;\n\
}";
LiteGraph.registerNodeType("texture/perlin", LGraphTexturePerlin);
function LGraphTextureCanvas2D() {
this.addOutput("out", "Texture");
this.properties = {
code: "",
width: 512,
height: 512,
precision: LGraphTexture.DEFAULT
};
this._func = null;
this._temp_texture = null;
}
LGraphTextureCanvas2D.title = "Canvas2D";
LGraphTextureCanvas2D.desc =
"Executes Canvas2D code inside a texture or the viewport";
LGraphTextureCanvas2D.widgets_info = {
precision: { widget: "combo", values: LGraphTexture.MODE_VALUES },
code: { type: "code" },
width: { type: "Number", precision: 0, step: 1 },
height: { type: "Number", precision: 0, step: 1 }
};
LGraphTextureCanvas2D.prototype.onPropertyChanged = function(
name,
value
) {
if (name == "code" && LiteGraph.allow_scripts) {
this._func = null;
try {
this._func = new Function(
"canvas",
"ctx",
"time",
"script",
value
);
this.boxcolor = "#00FF00";
} catch (err) {
this.boxcolor = "#FF0000";
console.error("Error parsing script");
console.error(err);
}
}
};
LGraphTextureCanvas2D.prototype.onExecute = function() {
var func = this._func;
if (!func || !this.isOutputConnected(0)) {
return;
}
if (!global.enableWebGLCanvas) {
console.warn(
"cannot use LGraphTextureCanvas2D if Canvas2DtoWebGL is not included"
);
return;
}
var width = this.properties.width || gl.canvas.width;
var height = this.properties.height || gl.canvas.height;
var temp = this._temp_texture;
if (!temp || temp.width != width || temp.height != height) {
temp = this._temp_texture = new GL.Texture(width, height, {
format: gl.RGBA,
filter: gl.LINEAR
});
}
var that = this;
var time = this.graph.getTime();
temp.drawTo(function() {
gl.start2D();
try {
if (func.draw) {
func.draw.call(that, gl.canvas, gl, time, func);
} else {
func.call(that, gl.canvas, gl, time, func);
}
that.boxcolor = "#00FF00";
} catch (err) {
that.boxcolor = "#FF0000";
console.error("Error executing script");
console.error(err);
}
gl.finish2D();
});
this.setOutputData(0, temp);
};
LiteGraph.registerNodeType("texture/canvas2D", LGraphTextureCanvas2D);
function LGraphTextureMatte() {
this.addInput("in", "Texture");
this.addOutput("out", "Texture");
this.properties = {
key_color: vec3.fromValues(0, 1, 0),
threshold: 0.8,
slope: 0.2,
precision: LGraphTexture.DEFAULT
};
}
LGraphTextureMatte.title = "Matte";
LGraphTextureMatte.desc = "Extracts background";
LGraphTextureMatte.widgets_info = {
key_color: { widget: "color" },
precision: { widget: "combo", values: LGraphTexture.MODE_VALUES }
};
LGraphTextureMatte.prototype.onExecute = function() {
if (!this.isOutputConnected(0)) {
return;
} //saves work
var tex = this.getInputData(0);
if (this.properties.precision === LGraphTexture.PASS_THROUGH) {
this.setOutputData(0, tex);
return;
}
if (!tex) {
return;
}
this._tex = LGraphTexture.getTargetTexture(
tex,
this._tex,
this.properties.precision
);
gl.disable(gl.BLEND);
gl.disable(gl.DEPTH_TEST);
if (!this._uniforms) {
this._uniforms = {
u_texture: 0,
u_key_color: this.properties.key_color,
u_threshold: 1,
u_slope: 1
};
}
var uniforms = this._uniforms;
var mesh = Mesh.getScreenQuad();
var shader = LGraphTextureMatte._shader;
if (!shader) {
shader = LGraphTextureMatte._shader = new GL.Shader(
GL.Shader.SCREEN_VERTEX_SHADER,
LGraphTextureMatte.pixel_shader
);
}
uniforms.u_key_color = this.properties.key_color;
uniforms.u_threshold = this.properties.threshold;
uniforms.u_slope = this.properties.slope;
this._tex.drawTo(function() {
tex.bind(0);
shader.uniforms(uniforms).draw(mesh);
});
this.setOutputData(0, this._tex);
};
LGraphTextureMatte.pixel_shader =
"precision highp float;\n\
varying vec2 v_coord;\n\
uniform sampler2D u_texture;\n\
uniform vec3 u_key_color;\n\
uniform float u_threshold;\n\
uniform float u_slope;\n\
\n\
void main() {\n\
vec3 color = texture2D( u_texture, v_coord ).xyz;\n\
float diff = length( normalize(color) - normalize(u_key_color) );\n\
float edge = u_threshold * (1.0 - u_slope);\n\
float alpha = smoothstep( edge, u_threshold, diff);\n\
gl_FragColor = vec4( color, alpha );\n\
}";
LiteGraph.registerNodeType("texture/matte", LGraphTextureMatte);
//***********************************
//Cubemap reader (to pass a cubemap to a node that requires cubemaps and no images)
function LGraphCubemap() {
this.addOutput("Cubemap", "Cubemap");
this.properties = { name: "" };
this.size = [
LGraphTexture.image_preview_size,
LGraphTexture.image_preview_size
];
}
LGraphCubemap.title = "Cubemap";
LGraphCubemap.prototype.onDropFile = function(data, filename, file) {
if (!data) {
this._drop_texture = null;
this.properties.name = "";
} else {
if (typeof data == "string") {
this._drop_texture = GL.Texture.fromURL(data);
} else {
this._drop_texture = GL.Texture.fromDDSInMemory(data);
}
this.properties.name = filename;
}
};
LGraphCubemap.prototype.onExecute = function() {
if (this._drop_texture) {
this.setOutputData(0, this._drop_texture);
return;
}
if (!this.properties.name) {
return;
}
var tex = LGraphTexture.getTexture(this.properties.name);
if (!tex) {
return;
}
this._last_tex = tex;
this.setOutputData(0, tex);
};
LGraphCubemap.prototype.onDrawBackground = function(ctx) {
if (this.flags.collapsed || this.size[1] <= 20) {
return;
}
if (!ctx.webgl) {
return;
}
var cube_mesh = gl.meshes["cube"];
if (!cube_mesh) {
cube_mesh = gl.meshes["cube"] = GL.Mesh.cube({ size: 1 });
}
//var view = mat4.lookAt( mat4.create(), [0,0
};
LiteGraph.registerNodeType("texture/cubemap", LGraphCubemap);
} //litegl.js defined
})(this);