diff --git a/build/litegraph.js b/build/litegraph.js
index 048fedcef0..4c125bf705 100644
--- a/build/litegraph.js
+++ b/build/litegraph.js
@@ -1382,6 +1382,9 @@
this.onNodeRemoved(node);
}
+ //close panels
+ this.sendActionToCanvas("checkPanels");
+
this.setDirtyCanvas(true, true);
this.change();
@@ -3090,9 +3093,11 @@
}
this.inputs.push(o);
this.setSize( this.computeSize() );
+
if (this.onInputAdded) {
this.onInputAdded(o);
}
+
this.setDirtyCanvas(true, true);
return o;
};
@@ -4710,6 +4715,7 @@ LGraphNode.prototype.executeAction = function(action)
}
graph.attachCanvas(this);
+ this.checkPanels();
this.setDirty(true, true);
};
@@ -4841,6 +4847,7 @@ LGraphNode.prototype.executeAction = function(action)
}
var canvas = this.canvas;
+
var ref_window = this.getCanvasWindow();
var document = ref_window.document; //hack used when moving canvas between windows
@@ -5243,6 +5250,7 @@ LGraphNode.prototype.executeAction = function(action)
//it wasn't clicked on the links boxes
if (!skip_action) {
var block_drag_node = false;
+ var pos = [e.canvasX - node.pos[0], e.canvasY - node.pos[1]];
//widgets
var widget = this.processNodeWidgets( node, this.canvas_mouse, e );
@@ -5255,18 +5263,31 @@ LGraphNode.prototype.executeAction = function(action)
if (is_double_click && this.selected_nodes[node.id]) {
//double click node
if (node.onDblClick) {
- node.onDblClick( e, [ e.canvasX - node.pos[0], e.canvasY - node.pos[1] ], this );
+ node.onDblClick( e, pos, this );
}
this.processNodeDblClicked(node);
block_drag_node = true;
}
//if do not capture mouse
- if ( node.onMouseDown && node.onMouseDown( e, [e.canvasX - node.pos[0], e.canvasY - node.pos[1]], this ) ) {
- block_drag_node = true;
- } else if (this.live_mode) {
- clicking_canvas_bg = true;
+ if ( node.onMouseDown && node.onMouseDown( e, pos, this ) ) {
block_drag_node = true;
+ } else {
+ //open subgraph button
+ if(node.subgraph && !node.skip_subgraph_button)
+ {
+ if ( !node.flags.collapsed && pos[0] > node.size[0] - LiteGraph.NODE_TITLE_HEIGHT && pos[1] < 0 ) {
+ var that = this;
+ setTimeout(function() {
+ that.openSubgraph(node.subgraph);
+ }, 10);
+ }
+ }
+
+ if (this.live_mode) {
+ clicking_canvas_bg = true;
+ block_drag_node = true;
+ }
}
if (!block_drag_node) {
@@ -5460,6 +5481,10 @@ LGraphNode.prototype.executeAction = function(action)
//mouse over a node
if (node) {
+
+ if(node.redraw_on_mouse)
+ this.dirty_canvas = true;
+
//this.canvas.style.cursor = "move";
if (!node.mouseOver) {
//mouse enter
@@ -6200,6 +6225,10 @@ LGraphNode.prototype.executeAction = function(action)
if (this.onShowNodePanel) {
this.onShowNodePanel(n);
}
+ else
+ {
+ this.showShowNodePanel(n);
+ }
if (this.onNodeDblClicked) {
this.onNodeDblClicked(n);
@@ -6350,6 +6379,10 @@ LGraphNode.prototype.executeAction = function(action)
LGraphCanvas.prototype.deleteSelectedNodes = function() {
for (var i in this.selected_nodes) {
var node = this.selected_nodes[i];
+
+ if(node.block_delete)
+ continue;
+
//autoconnect when possible (very basic, only takes into account first input-output)
if(node.inputs && node.inputs.length && node.outputs && node.outputs.length && LiteGraph.isValidConnection( node.inputs[0].type, node.outputs[0].type ) && node.inputs[0].link && node.outputs[0].links && node.outputs[0].links.length )
{
@@ -6972,14 +7005,8 @@ LGraphNode.prototype.executeAction = function(action)
var glow = false;
this.current_node = node;
- var color =
- node.color ||
- node.constructor.color ||
- LiteGraph.NODE_DEFAULT_COLOR;
- var bgcolor =
- node.bgcolor ||
- node.constructor.bgcolor ||
- LiteGraph.NODE_DEFAULT_BGCOLOR;
+ var color = node.color || node.constructor.color || LiteGraph.NODE_DEFAULT_COLOR;
+ var bgcolor = node.bgcolor || node.constructor.bgcolor || LiteGraph.NODE_DEFAULT_BGCOLOR;
//shadow and glow
if (node.mouseOver) {
@@ -7493,20 +7520,14 @@ LGraphNode.prototype.executeAction = function(action)
ctx.shadowColor = "transparent";
if (node.onDrawBackground) {
- node.onDrawBackground(ctx, this, this.canvas);
+ node.onDrawBackground(ctx, this, this.canvas, this.canvas_mouse );
}
//title bg (remember, it is rendered ABOVE the node)
if (render_title || title_mode == LiteGraph.TRANSPARENT_TITLE) {
//title bar
if (node.onDrawTitleBar) {
- node.onDrawTitleBar(
- ctx,
- title_height,
- size,
- this.ds.scale,
- fgcolor
- );
+ node.onDrawTitleBar( ctx, title_height, size, this.ds.scale, fgcolor );
} else if (
title_mode != LiteGraph.TRANSPARENT_TITLE &&
(node.constructor.title_color || this.render_title_colored)
@@ -7647,6 +7668,28 @@ LGraphNode.prototype.executeAction = function(action)
}
}
+ //subgraph box
+ if (!node.flags.collapsed && node.subgraph && !node.skip_subgraph_button) {
+ ctx.fillStyle = "#555";
+ var w = LiteGraph.NODE_TITLE_HEIGHT;
+ var x = node.size[0] - w;
+ if( shape == LiteGraph.BOX_SHAPE || low_quality)
+ ctx.fillRect(x+2, -w+2, w-4, w-4);
+ else
+ {
+ ctx.beginPath();
+ ctx.roundRect(x+2, -w+2, w-4, w-4,4);
+ ctx.fill();
+ }
+ ctx.fillStyle = "#333";
+ ctx.beginPath();
+ ctx.moveTo(x + w * 0.2, -w * 0.6);
+ ctx.lineTo(x + w * 0.8, -w * 0.6);
+ ctx.lineTo(x + w * 0.5, -w * 0.3);
+ ctx.fill();
+ }
+
+ //custom title render
if (node.onDrawTitle) {
node.onDrawTitle(ctx);
}
@@ -8885,6 +8928,7 @@ LGraphNode.prototype.executeAction = function(action)
}
if (!entries.length) {
+ console.log("no input entries");
return;
}
@@ -9754,6 +9798,341 @@ LGraphNode.prototype.executeAction = function(action)
return dialog;
};
+ LGraphCanvas.prototype.createPanel = function(title, options) {
+ options = options || {};
+
+ var ref_window = options.window || window;
+ var root = document.createElement("div");
+ root.className = "litegraph dialog";
+ root.innerHTML = "
";
+ root.header = root.querySelector(".dialog-header");
+
+ if(options.width)
+ root.style.width = options.width + (options.width.constructor === Number ? "px" : "");
+ if(options.height)
+ root.style.height = options.height + (options.height.constructor === Number ? "px" : "");
+ if(options.closable)
+ {
+ var close = document.createElement("span");
+ close.innerHTML = "✕";
+ close.classList.add("close");
+ close.addEventListener("click",function(){
+ root.close();
+ });
+ root.header.appendChild(close);
+ }
+ root.title_element = root.querySelector(".dialog-title");
+ root.title_element.innerText = title;
+ root.content = root.querySelector(".dialog-content");
+ root.footer = root.querySelector(".dialog-footer");
+
+ root.close = function()
+ {
+ this.parentNode.removeChild(this);
+ }
+
+ root.clear = function()
+ {
+ this.content.innerHTML = "";
+ }
+
+ root.addHTML = function(code, classname, on_footer)
+ {
+ var elem = document.createElement("div");
+ if(classname)
+ elem.className = classname;
+ elem.innerHTML = code;
+ if(on_footer)
+ root.footer.appendChild(elem);
+ else
+ root.content.appendChild(elem);
+ return elem;
+ }
+
+ root.addButton = function( name, callback, options )
+ {
+ var elem = document.createElement("button");
+ elem.innerText = name;
+ elem.options = options;
+ elem.classList.add("btn");
+ elem.addEventListener("click",callback);
+ root.footer.appendChild(elem);
+ return elem;
+ }
+
+ root.addSeparator = function()
+ {
+ var elem = document.createElement("div");
+ elem.className = "separator";
+ root.content.appendChild(elem);
+ }
+
+ root.addWidget = function( type, name, value, options, callback )
+ {
+ options = options || {};
+ var str_value = String(value);
+ if(type == "number")
+ str_value = value.toFixed(3);
+
+ var elem = document.createElement("div");
+ elem.className = "property";
+ elem.innerHTML = " ";
+ elem.querySelector(".property_name").innerText = name;
+ var value_element = elem.querySelector(".property_value");
+ value_element.innerText = str_value;
+ elem.dataset["property"] = name;
+ elem.dataset["type"] = options.type || type;
+ elem.options = options;
+ elem.value = value;
+
+ //if( type == "code" )
+ // elem.addEventListener("click", function(){ inner_showCodePad( node, this.dataset["property"] ); });
+ if (type == "boolean")
+ {
+ elem.classList.add("boolean");
+ if(value)
+ elem.classList.add("bool-on");
+ elem.addEventListener("click", function(){
+ //var v = node.properties[this.dataset["property"]];
+ //node.setProperty(this.dataset["property"],!v); this.innerText = v ? "true" : "false";
+ var propname = this.dataset["property"];
+ this.value = !this.value;
+ this.classList.toggle("bool-on");
+ this.querySelector(".property_value").innerText = this.value ? "true" : "false";
+ innerChange(propname, this.value );
+ });
+ }
+ else if (type == "string" || type == "number")
+ {
+ value_element.setAttribute("contenteditable",true);
+ value_element.addEventListener("keydown", function(e){
+ if(e.code == "Enter")
+ {
+ e.preventDefault();
+ this.blur();
+ }
+ });
+ value_element.addEventListener("blur", function(){
+ var v = this.innerText;
+ var propname = this.parentNode.dataset["property"];
+ var proptype = this.parentNode.dataset["type"];
+ if( proptype == "number")
+ v = Number(v);
+ innerChange(propname, v);
+ });
+ }
+ else if (type == "enum")
+ value_element.addEventListener("click", function(event){
+ var values = options.values || [];
+ var propname = this.parentNode.dataset["property"];
+ var elem_that = this;
+ var menu = new LiteGraph.ContextMenu(values,{
+ event: event,
+ className: "dark",
+ callback: inner_clicked
+ },
+ ref_window);
+ function inner_clicked(v, option, event) {
+ //node.setProperty(propname,v);
+ //graphcanvas.dirty_canvas = true;
+ elem_that.innerText = v;
+ innerChange(propname,v);
+ return false;
+ }
+ });
+
+ root.content.appendChild(elem);
+
+ function innerChange(name, value)
+ {
+ console.log("change",name,value);
+ //that.dirty_canvas = true;
+ if(options.callback)
+ options.callback(name,value);
+ if(callback)
+ callback(name,value);
+ }
+
+ return elem;
+ }
+
+ return root;
+ };
+
+ LGraphCanvas.prototype.showShowNodePanel = function( node )
+ {
+ window.SELECTED_NODE = node;
+ var panel = document.querySelector("#node-panel");
+ if(panel)
+ panel.close();
+ var ref_window = this.getCanvasWindow();
+ panel = this.createPanel(node.title || "",{closable: true, window: ref_window });
+ panel.id = "node-panel";
+ panel.node = node;
+ panel.classList.add("settings");
+ var that = this;
+ var graphcanvas = this;
+
+ function inner_refresh()
+ {
+ panel.content.innerHTML = ""; //clear
+ panel.addHTML(""+node.type+" "+(node.constructor.desc || "")+" ");
+
+ panel.addHTML("Properties ");
+
+ for(var i in node.properties)
+ {
+ var value = node.properties[i];
+ var info = node.getPropertyInfo(i);
+ var type = info.type || "string";
+
+ //in case the user wants control over the side panel widget
+ if( node.onAddPropertyToPanel && node.onAddPropertyToPanel(i,panel) )
+ continue;
+
+ panel.addWidget( info.widget || info.type, i, value, info, function(name,value){
+ node.setProperty(name,value);
+ graphcanvas.dirty_canvas = true;
+ });
+ }
+
+ panel.addSeparator();
+
+ if(node.onShowCustomPanelInfo)
+ node.onShowCustomPanelInfo(panel);
+
+ /*
+ panel.addHTML("Connections ");
+ var connection_containers = panel.addHTML("
","connections");
+ var inputs = connection_containers.querySelector(".inputs");
+ var outputs = connection_containers.querySelector(".outputs");
+ */
+
+ panel.addButton("Delete",function(){
+ if(node.block_delete)
+ return;
+ node.graph.remove(node);
+ panel.close();
+ }).classList.add("delete");
+ }
+
+ function inner_showCodePad( node, propname )
+ {
+ panel.style.top = "calc( 50% - 250px)";
+ panel.style.left = "calc( 50% - 400px)";
+ panel.style.width = "800px";
+ panel.style.height = "500px";
+
+ if(window.CodeFlask) //disabled for now
+ {
+ panel.content.innerHTML = "
";
+ var flask = new CodeFlask( "div.code", { language: 'js' });
+ flask.updateCode(node.properties[propname]);
+ flask.onUpdate( function(code) {
+ node.setProperty(propname, code);
+ });
+ }
+ else
+ {
+ panel.content.innerHTML = "";
+ var textarea = panel.content.querySelector("textarea");
+ textarea.value = node.properties[propname];
+ textarea.addEventListener("keydown", function(e){
+ //console.log(e);
+ if(e.code == "Enter" && e.ctrlKey )
+ {
+ console.log("Assigned");
+ node.setProperty(propname, textarea.value);
+ }
+ });
+ textarea.style.height = "calc(100% - 40px)";
+ }
+ var assign = that.createButton( "Assign", null, function(){
+ node.setProperty(propname, textarea.value);
+ });
+ panel.content.appendChild(assign);
+ var button = that.createButton( "Close", null, function(){
+ panel.style.height = "";
+ inner_refresh();
+ });
+ button.style.float = "right";
+ panel.content.appendChild(button);
+ }
+
+ inner_refresh();
+
+ this.canvas.parentNode.appendChild( panel );
+ }
+
+ LGraphCanvas.prototype.showSubgraphPropertiesDialog = function(node)
+ {
+ console.log("showing subgraph properties dialog");
+
+ var old_panel = this.canvas.parentNode.querySelector(".subgraph_dialog");
+ if(old_panel)
+ old_panel.close();
+
+ var panel = this.createPanel("Subgraph Inputs",{closable:true, width: 500});
+ panel.node = node;
+ panel.classList.add("subgraph_dialog");
+
+ function inner_refresh()
+ {
+ panel.clear();
+
+ //show currents
+ if(node.inputs)
+ for(var i = 0; i < node.inputs.length; ++i)
+ {
+ var input = node.inputs[i];
+ var html = "Name Type ✕ ";
+ var elem = panel.addHTML(html,"subgraph_property");
+ elem.dataset["name"] = input.name;
+ elem.dataset["slot"] = i;
+ elem.querySelector(".name").value = input.name;
+ elem.querySelector(".type").value = input.type;
+ elem.querySelector("button").addEventListener("click",function(e){
+ node.removeInput( this.parentNode.dataset["slot"] );
+ inner_refresh();
+ });
+ }
+ }
+
+ //add extra
+ var html = " + Name Type + ";
+ var elem = panel.addHTML(html,"subgraph_property extra", true);
+ elem.querySelector("button").addEventListener("click", function(e){
+ var elem = this.parentNode;
+ var name = elem.querySelector(".name").value;
+ var type = elem.querySelector(".type").value;
+ if(!name || node.findInputSlot(name) != -1)
+ return;
+ node.addInput(name,type);
+ elem.querySelector(".name").value = "";
+ elem.querySelector(".type").value = "";
+ inner_refresh();
+ });
+
+ inner_refresh();
+ this.canvas.parentNode.appendChild(panel);
+ return panel;
+ }
+
+ LGraphCanvas.prototype.checkPanels = function()
+ {
+ if(!this.canvas)
+ return;
+ var panels = this.canvas.parentNode.querySelectorAll(".litegraph.dialog");
+ for(var i = 0; i < panels.length; ++i)
+ {
+ var panel = panels[i];
+ if( !panel.node )
+ continue;
+ if( !panel.node.graph || panel.graph != this.graph )
+ panel.close();
+ }
+ }
+
LGraphCanvas.onMenuNodeCollapse = function(value, options, e, menu, node) {
node.collapse();
};
@@ -10058,12 +10437,11 @@ LGraphNode.prototype.executeAction = function(action)
callback: LGraphCanvas.onMenuNodeToSubgraph
});
- if (node.removable !== false) {
- options.push(null, {
- content: "Remove",
- callback: LGraphCanvas.onMenuNodeRemove
- });
- }
+ options.push(null, {
+ content: "Remove",
+ disabled: !(node.removable !== false && !node.block_delete ),
+ callback: LGraphCanvas.onMenuNodeRemove
+ });
if (node.graph && node.graph.onGetNodeMenuOptions) {
node.graph.onGetNodeMenuOptions(options, node);
@@ -11126,6 +11504,7 @@ if (typeof exports != "undefined") {
return [["enabled", "boolean"]];
};
+ /*
Subgraph.prototype.onDrawTitle = function(ctx) {
if (this.flags.collapsed) {
return;
@@ -11142,6 +11521,7 @@ if (typeof exports != "undefined") {
ctx.lineTo(x + w * 0.5, -w * 0.3);
ctx.fill();
};
+ */
Subgraph.prototype.onDblClick = function(e, pos, graphcanvas) {
var that = this;
@@ -11150,6 +11530,7 @@ if (typeof exports != "undefined") {
}, 10);
};
+ /*
Subgraph.prototype.onMouseDown = function(e, pos, graphcanvas) {
if (
!this.flags.collapsed &&
@@ -11162,6 +11543,7 @@ if (typeof exports != "undefined") {
}, 10);
}
};
+ */
Subgraph.prototype.onAction = function(action, param) {
this.subgraph.onAction(action, param);
@@ -17518,7 +17900,7 @@ if (typeof exports != "undefined") {
u_texture: 0,
u_textureB: 1,
value: value,
- texSize: [width, height],
+ texSize: [width, height,1/width,1/height],
time: time
})
.draw(mesh);
@@ -17533,7 +17915,7 @@ if (typeof exports != "undefined") {
uniform sampler2D u_texture;\n\
uniform sampler2D u_textureB;\n\
varying vec2 v_coord;\n\
- uniform vec2 texSize;\n\
+ uniform vec4 texSize;\n\
uniform float time;\n\
uniform float value;\n\
\n\
@@ -17570,6 +17952,20 @@ if (typeof exports != "undefined") {
LGraphTextureOperation.registerPreset("displace","texture2D(u_texture, uv + (colorB.xy - vec2(0.5)) * value).xyz");
LGraphTextureOperation.registerPreset("grayscale","vec3(color.x + color.y + color.z) * value / 3.0");
LGraphTextureOperation.registerPreset("saturation","mix( vec3(color.x + color.y + color.z) / 3.0, color, value )");
+ LGraphTextureOperation.registerPreset("normalmap","\n\
+ float z0 = texture2D(u_texture, uv + vec2(-texSize.z, -texSize.w) ).x;\n\
+ float z1 = texture2D(u_texture, uv + vec2(0.0, -texSize.w) ).x;\n\
+ float z2 = texture2D(u_texture, uv + vec2(texSize.z, -texSize.w) ).x;\n\
+ float z3 = texture2D(u_texture, uv + vec2(-texSize.z, 0.0) ).x;\n\
+ float z4 = color.x;\n\
+ float z5 = texture2D(u_texture, uv + vec2(texSize.z, 0.0) ).x;\n\
+ float z6 = texture2D(u_texture, uv + vec2(-texSize.z, texSize.w) ).x;\n\
+ float z7 = texture2D(u_texture, uv + vec2(0.0, texSize.w) ).x;\n\
+ float z8 = texture2D(u_texture, uv + vec2(texSize.z, texSize.w) ).x;\n\
+ vec3 normal = vec3( z2 + 2.0*z4 + z7 - z0 - 2.0*z3 - z5, z5 + 2.0*z6 + z7 -z0 - 2.0*z1 - z2, 1.0 );\n\
+ normal.xy *= value;\n\
+ result.xyz = normalize(normal) * 0.5 + vec3(0.5);\n\
+ ");
LGraphTextureOperation.registerPreset("threshold","vec3(color.x > colorB.x * value ? 1.0 : 0.0,color.y > colorB.y * value ? 1.0 : 0.0,color.z > colorB.z * value ? 1.0 : 0.0)");
//webglstudio stuff...
@@ -17602,7 +17998,7 @@ if (typeof exports != "undefined") {
};
this.properties.code = LGraphTextureShader.pixel_shader;
- this._uniforms = { u_value: 1, u_color: vec4.create(), in_texture: 0, texSize: vec2.create(), time: 0 };
+ this._uniforms = { u_value: 1, u_color: vec4.create(), in_texture: 0, texSize: vec4.create(), time: 0 };
}
LGraphTextureShader.title = "Shader";
@@ -17768,6 +18164,8 @@ if (typeof exports != "undefined") {
}
uniforms.texSize[0] = w;
uniforms.texSize[1] = h;
+ uniforms.texSize[2] = 1/w;
+ uniforms.texSize[3] = 1/h;
uniforms.time = this.graph.getTime();
uniforms.u_value = this.properties.u_value;
uniforms.u_color.set( this.properties.u_color );
@@ -17788,7 +18186,7 @@ if (typeof exports != "undefined") {
\n\
varying vec2 v_coord;\n\
uniform float time; //time in seconds\n\
-uniform vec2 texSize; //tex resolution\n\
+uniform vec4 texSize; //tex resolution\n\
uniform float u_value;\n\
uniform vec4 u_color;\n\n\
void main() {\n\
@@ -18438,6 +18836,69 @@ void main() {\n\
LGraphTextureDownsample
);
+
+
+ function LGraphTextureResize() {
+ this.addInput("Texture", "Texture");
+ this.addOutput("", "Texture");
+ this.properties = {
+ size: [512,512],
+ generate_mipmaps: false,
+ precision: LGraphTexture.DEFAULT
+ };
+ }
+
+ LGraphTextureResize.title = "Resize";
+ LGraphTextureResize.desc = "Resize Texture";
+ LGraphTextureResize.widgets_info = {
+ iterations: { type: "number", step: 1, precision: 0, min: 0 },
+ precision: { widget: "combo", values: LGraphTexture.MODE_VALUES }
+ };
+
+ LGraphTextureResize.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;
+ }
+
+ var width = this.properties.size[0] | 0;
+ var height = this.properties.size[1] | 0;
+ if(width == 0)
+ width = tex.width;
+ if(height == 0)
+ height = tex.height;
+ 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( !this._texture || this._texture.width != width || this._texture.height != height || this._texture.type != type )
+ this._texture = new GL.Texture( width, height, { type: type } );
+
+ tex.copyTo( this._texture );
+
+ if (this.properties.generate_mipmaps) {
+ this._texture.bind(0);
+ gl.generateMipmap(this._texture.texture_type);
+ this._texture.unbind(0);
+ }
+
+ this.setOutputData(0, this._texture);
+ };
+
+ LiteGraph.registerNodeType( "texture/resize", LGraphTextureResize );
+
// Texture Average *****************************************
function LGraphTextureAverage() {
this.addInput("Texture", "Texture");
@@ -19105,7 +19566,7 @@ void main() {\n\
this.addInput("Texture", "Texture");
this.addInput("Atlas", "Texture");
this.addOutput("", "Texture");
- this.properties = { enabled: true, num_row_symbols: 4, symbol_size: 16, brightness: 1, colorize: false, filter: false, invert: false, precision: LGraphTexture.DEFAULT, texture: null };
+ this.properties = { enabled: true, num_row_symbols: 4, symbol_size: 16, brightness: 1, colorize: false, filter: false, invert: false, precision: LGraphTexture.DEFAULT, generate_mipmaps: false, texture: null };
if (!LGraphTextureEncode._shader) {
LGraphTextureEncode._shader = new GL.Shader( Shader.SCREEN_VERTEX_SHADER, LGraphTextureEncode.pixel_shader );
@@ -19181,6 +19642,12 @@ void main() {\n\
tex.toViewport(LGraphTextureEncode._shader, uniforms);
});
+ if (this.properties.generate_mipmaps) {
+ this._tex.bind(0);
+ gl.generateMipmap(this._tex.texture_type);
+ this._tex.unbind(0);
+ }
+
this.setOutputData(0, this._tex);
};
diff --git a/css/litegraph-editor.css b/css/litegraph-editor.css
index acf575e1fd..d286949b68 100755
--- a/css/litegraph-editor.css
+++ b/css/litegraph-editor.css
@@ -93,7 +93,7 @@
/* BUTTONS **********************/
-.litegraph-editor button {
+.litegraph-editor .btn {
/*font-family: "Metro Light";*/
color: #ccc;
font-size: 20px;
@@ -193,157 +193,6 @@
background-image: url("../demo/imgs/load-progress-full.png");
}
-.litegraph-editor .dialog {
- position: absolute;
- top: 50%;
- left: 50%;
- margin-top: -150px;
- margin-left: -200px;
-
- background-color: #222222;
-
- min-width: 400px;
- min-height: 200px;
- box-shadow: 0 0 4px #111;
- border-radius: 6px;
-}
-
-.litegraph-editor .dialog.settings {
- left: 10px;
- top: 10px;
- height: calc( 100% - 20px );
- margin: auto;
-}
-
-.litegraph-editor .dialog .close {
- float: right;
- margin: 4px;
- margin-right: 10px;
- cursor: pointer;
- font-size: 1.4em;
-}
-
-.litegraph-editor .dialog .close:hover {
- color: white;
-}
-
-.litegraph-editor .dialog .dialog-header {
- color: #AAA;
- border-bottom: 1px solid #161616;
-}
-
-.litegraph-editor .dialog .dialog-header { height: 40px; }
-.litegraph-editor .dialog .dialog-footer { height: 50px; padding: 10px; border-top: 1px solid #1a1a1a;}
-
-.litegraph-editor .dialog .dialog-header .dialog-title {
- font: 20px "Arial";
- margin: 4px;
- padding: 4px 10px;
- display: inline-block;
-}
-
-.litegraph-editor .dialog .dialog-content {
- height: calc(100% - 90px);
- width: calc(100% - 10px);
- /*background-color: black;*/
- padding: 4px;
- display: inline-block;
- color: #AAA;
-}
-
-.litegraph-editor .dialog .dialog-content h3 {
- margin: 10px;
-}
-
-.litegraph-editor .dialog .dialog-content .connections {
- flex-direction: row;
-}
-
-.litegraph-editor .dialog .dialog-content .connections .connections_side {
- width: calc(50% - 5px);
- min-height: 100px;
- background-color: black;
- display: flex;
-}
-
-.litegraph-editor .dialog .node_type {
- font-size: 1.2em;
- display: block;
- margin: 10px;
-}
-
-.litegraph-editor .dialog .node_desc {
- opacity: 0.5;
- display: block;
- margin: 10px;
-}
-
-.litegraph-editor .dialog .separator {
- display: block;
- width: calc( 100% - 4px );
- height: 1px;
- border-top: 1px solid #000;
- border-bottom: 1px solid #333;
- margin: 10px 2px;
- padding: 0;
-}
-
-.litegraph-editor .dialog .property {
- margin-bottom: 2px;
- padding: 0;
-}
-
-.litegraph-editor .dialog .property_name {
- color: #737373;
- display: inline-block;
- text-align: left;
- vertical-align: top;
- width: 120px;
- padding-left: 4px;
- overflow: hidden;
-}
-
-.litegraph-editor .dialog .property_value {
- display: inline-block;
- text-align: right;
- color: #AAA;
- background-color: #1A1A1A;
- width: calc( 100% - 122px );
- max-height: 300px;
- padding: 4px;
- padding-right: 12px;
- overflow: hidden;
- cursor: pointer;
- border-radius: 3px;
-}
-
-.litegraph-editor .dialog .property_value:hover {
- color: white;
-}
-
-.litegraph-editor .dialog .property.boolean .property_value {
- padding-right: 30px;
-}
-
-.litegraph-editor .dialog button {
- border-radius: 4px;
- padding: 4px 20px;
- margin-left: 0px;
- background-color: #060606;
- color: #8e8e8e;
-}
-
-.litegraph-editor .dialog button:hover {
- background-color: #111;
- color: #FFF;
-}
-
-.litegraph-editor .dialog button.delete:hover {
- background-color: #F33;
- color: black;
-}
-
-
.litegraph-editor textarea.code, .litegraph-editor div.code {
height: 100%;
width: 100%;
diff --git a/css/litegraph.css b/css/litegraph.css
index 7b34e3e896..6f03567a4d 100755
--- a/css/litegraph.css
+++ b/css/litegraph.css
@@ -5,6 +5,7 @@
user-select: none;
-moz-user-select: none;
-webkit-user-select: none;
+ outline: none;
}
.litegraph.litecontextmenu {
@@ -207,6 +208,234 @@
color: black;
}
+/* DIALOGs ******/
+
+.litegraph .dialog {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ margin-top: -150px;
+ margin-left: -200px;
+
+ background-color: #2A2A2A;
+
+ min-width: 400px;
+ min-height: 200px;
+ box-shadow: 0 0 4px #111;
+ border-radius: 6px;
+}
+
+.litegraph .dialog.settings {
+ left: 10px;
+ top: 10px;
+ height: calc( 100% - 20px );
+ margin: auto;
+}
+
+.litegraph .dialog .close {
+ float: right;
+ margin: 4px;
+ margin-right: 10px;
+ cursor: pointer;
+ font-size: 1.4em;
+}
+
+.litegraph .dialog .close:hover {
+ color: white;
+}
+
+.litegraph .dialog .dialog-header {
+ color: #AAA;
+ border-bottom: 1px solid #161616;
+}
+
+.litegraph .dialog .dialog-header { height: 40px; }
+.litegraph .dialog .dialog-footer { height: 50px; padding: 10px; border-top: 1px solid #1a1a1a;}
+
+.litegraph .dialog .dialog-header .dialog-title {
+ font: 20px "Arial";
+ margin: 4px;
+ padding: 4px 10px;
+ display: inline-block;
+}
+
+.litegraph .dialog .dialog-content {
+ height: calc(100% - 90px);
+ width: calc(100% - 10px);
+ min-height: 100px;
+ /*background-color: black;*/
+ padding: 4px;
+ display: inline-block;
+ color: #AAA;
+}
+
+.litegraph .dialog .dialog-content h3 {
+ margin: 10px;
+}
+
+.litegraph .dialog .dialog-content .connections {
+ flex-direction: row;
+}
+
+.litegraph .dialog .dialog-content .connections .connections_side {
+ width: calc(50% - 5px);
+ min-height: 100px;
+ background-color: black;
+ display: flex;
+}
+
+.litegraph .dialog .node_type {
+ font-size: 1.2em;
+ display: block;
+ margin: 10px;
+}
+
+.litegraph .dialog .node_desc {
+ opacity: 0.5;
+ display: block;
+ margin: 10px;
+}
+
+.litegraph .dialog .separator {
+ display: block;
+ width: calc( 100% - 4px );
+ height: 1px;
+ border-top: 1px solid #000;
+ border-bottom: 1px solid #333;
+ margin: 10px 2px;
+ padding: 0;
+}
+
+.litegraph .dialog .property {
+ margin-bottom: 2px;
+ padding: 0;
+}
+
+.litegraph .dialog .property_name {
+ color: #737373;
+ display: inline-block;
+ text-align: left;
+ vertical-align: top;
+ width: 120px;
+ padding-left: 4px;
+ overflow: hidden;
+}
+
+.litegraph .dialog .property_value {
+ display: inline-block;
+ text-align: right;
+ color: #AAA;
+ background-color: #1A1A1A;
+ width: calc( 100% - 122px );
+ max-height: 300px;
+ padding: 4px;
+ padding-right: 12px;
+ overflow: hidden;
+ cursor: pointer;
+ border-radius: 3px;
+}
+
+.litegraph .dialog .property_value:hover {
+ color: white;
+}
+
+.litegraph .dialog .property.boolean .property_value {
+ padding-right: 30px;
+}
+
+.litegraph .dialog .btn {
+ border-radius: 4px;
+ padding: 4px 20px;
+ margin-left: 0px;
+ background-color: #060606;
+ color: #8e8e8e;
+}
+
+.litegraph .dialog .btn:hover {
+ background-color: #111;
+ color: #FFF;
+}
+
+.litegraph .dialog .btn.delete:hover {
+ background-color: #F33;
+ color: black;
+}
+
+.litegraph .subgraph_property {
+ padding-bottom: 4px;
+}
+
+.litegraph .subgraph_property:hover {
+ background-color: #333;
+}
+
+.litegraph .subgraph_property.extra {
+ margin-top: 8px;
+}
+
+.litegraph .subgraph_property span.name {
+ font-size: 1.3em;
+ padding-left: 4px;
+}
+
+.litegraph .subgraph_property span.type {
+ opacity: 0.5;
+ margin-right: 20px;
+ padding-left: 4px;
+}
+
+.litegraph .subgraph_property span.label {
+ display: inline-block;
+ width: 60px;
+ padding: 0px 10px;
+}
+
+.litegraph .subgraph_property input {
+ width: 140px;
+ color: #999;
+ background-color: #1A1A1A;
+ border-radius: 4px;
+ border: 0;
+ margin-right: 10px;
+ padding: 4px;
+ padding-left: 10px;
+}
+
+.litegraph .subgraph_property button {
+ background-color: #1c1c1c;
+ color: #aaa;
+ border: 0;
+ border-radius: 2px;
+ padding: 4px 10px;
+ cursor: pointer;
+}
+
+.litegraph .subgraph_property.extra {
+ color: #ccc;
+}
+
+.litegraph .subgraph_property.extra input {
+ background-color: #111;
+}
+
+.litegraph .bullet_icon {
+ margin-left: 10px;
+ border-radius: 10px;
+ width: 12px;
+ height: 12px;
+ background-color: #666;
+ display: inline-block;
+ margin-top: 2px;
+ margin-right: 4px;
+ transition: background-color 0.1s ease 0s;
+ -moz-transition: background-color 0.1s ease 0s;
+}
+
+.litegraph .bullet_icon:hover {
+ background-color: #698;
+ cursor: pointer;
+}
+
/* OLD */
.graphcontextmenu {
diff --git a/demo/js/code.js b/demo/js/code.js
index 7038567a6d..4f6ecb7123 100644
--- a/demo/js/code.js
+++ b/demo/js/code.js
@@ -1,7 +1,7 @@
var webgl_canvas = null;
LiteGraph.node_images_path = "../nodes_data/";
-var editor = new LiteGraph.Editor("main",{miniwindow:true});
+var editor = new LiteGraph.Editor("main",{miniwindow:false});
window.graphcanvas = editor.graphcanvas;
window.graph = editor.graph;
window.addEventListener("resize", function() { editor.graphcanvas.resize(); } );
@@ -17,7 +17,7 @@ LiteGraph.allow_scripts = true;
//create scene selector
var elem = document.createElement("span");
elem.className = "selector";
-elem.innerHTML = "Demo Empty Save Load Download | WebGL ";
+elem.innerHTML = "Demo Empty Save Load Download | WebGL ";
editor.tools.appendChild(elem);
var select = elem.querySelector("select");
select.addEventListener("change", function(e){
@@ -102,6 +102,7 @@ function enableWebGL()
"js/libs/litegl.js",
"../src/nodes/gltextures.js",
"../src/nodes/glfx.js",
+ "../src/nodes/shaders.js",
"../src/nodes/geometry.js"
];
diff --git a/demo/style.css b/demo/style.css
index ecf79c0e7b..02f642f798 100755
--- a/demo/style.css
+++ b/demo/style.css
@@ -123,7 +123,7 @@ label {
color: #AAF;
}
-input,textarea {
+.header input {
color: #EEE;
background-color: #555;
font-size: 1.2em;
diff --git a/src/litegraph-editor.js b/src/litegraph-editor.js
index 25a40f7bf2..e8ddb80b59 100755
--- a/src/litegraph-editor.js
+++ b/src/litegraph-editor.js
@@ -9,7 +9,7 @@ function Editor(container_id, options) {
var root = document.createElement("div");
this.root = root;
- root.className = "litegraph-editor";
+ root.className = "litegraph litegraph-editor";
root.innerHTML = html;
this.tools = root.querySelector(".tools");
@@ -27,7 +27,6 @@ function Editor(container_id, options) {
};
graphcanvas.onDropItem = this.onDropItem.bind(this);
- graphcanvas.onShowNodePanel = this.onShowNodePanel.bind(this);
//add stuff
//this.addToolsButton("loadsession_button","Load","imgs/icon-load.png", this.onLoadButton.bind(this), ".tools-left" );
@@ -115,157 +114,12 @@ Editor.prototype.addToolsButton = function( id, name, icon_url, callback, contai
this.root.querySelector(container).appendChild(button);
};
-Editor.prototype.createPanel = function(title, options) {
- options = options || {};
-
- var ref_window = options.window || window;
- var root = document.createElement("div");
- root.className = "dialog";
- root.innerHTML = "
";
- root.header = root.querySelector(".dialog-header");
- if(options.closable)
- {
- var close = document.createElement("span");
- close.innerHTML = "✕";
- close.classList.add("close");
- close.addEventListener("click",function(){
- root.close();
- });
- root.header.appendChild(close);
- }
- root.title_element = root.querySelector(".dialog-title");
- root.title_element.innerText = title;
- root.content = root.querySelector(".dialog-content");
- root.footer = root.querySelector(".dialog-footer");
- root.close = function()
- {
- this.parentNode.removeChild(this);
- }
-
- root.addHTML = function(code, classname)
- {
- var elem = document.createElement("div");
- if(classname)
- elem.className = classname;
- elem.innerHTML = code;
- root.content.appendChild(elem);
- return elem;
- }
-
- root.addButton = function( name, callback, options )
- {
- var elem = document.createElement("button");
- elem.innerText = name;
- elem.options = options;
- elem.addEventListener("click",callback);
- root.footer.appendChild(elem);
- return elem;
- }
-
- root.addSeparator = function()
- {
- var elem = document.createElement("div");
- elem.className = "separator";
- root.content.appendChild(elem);
- }
-
- root.addWidget = function( type, name, value, options, callback )
- {
- options = options || {};
- var str_value = String(value);
- if(type == "number")
- str_value = value.toFixed(3);
-
- var elem = document.createElement("div");
- elem.className = "property";
- elem.innerHTML = " ";
- elem.querySelector(".property_name").innerText = name;
- var value_element = elem.querySelector(".property_value");
- value_element.innerText = str_value;
- elem.dataset["property"] = name;
- elem.dataset["type"] = options.type || type;
- elem.options = options;
- elem.value = value;
-
- //if( type == "code" )
- // elem.addEventListener("click", function(){ inner_showCodePad( node, this.dataset["property"] ); });
- if (type == "boolean")
- {
- elem.classList.add("boolean");
- if(value)
- elem.classList.add("bool-on");
- elem.addEventListener("click", function(){
- //var v = node.properties[this.dataset["property"]];
- //node.setProperty(this.dataset["property"],!v); this.innerText = v ? "true" : "false";
- var propname = this.dataset["property"];
- this.value = !this.value;
- this.classList.toggle("bool-on");
- this.querySelector(".property_value").innerText = this.value ? "true" : "false";
- innerChange(propname, this.value );
- });
- }
- else if (type == "string" || type == "number")
- {
- value_element.setAttribute("contenteditable",true);
- value_element.addEventListener("keydown", function(e){
- if(e.code == "Enter")
- {
- e.preventDefault();
- this.blur();
- }
- });
- value_element.addEventListener("blur", function(){
- var v = this.innerText;
- var propname = this.parentNode.dataset["property"];
- var proptype = this.parentNode.dataset["type"];
- if( proptype == "number")
- v = Number(v);
- innerChange(propname, v);
- });
- }
- else if (type == "enum")
- value_element.addEventListener("click", function(event){
- var values = options.values || [];
- var propname = this.parentNode.dataset["property"];
- var elem_that = this;
- var menu = new LiteGraph.ContextMenu(values,{
- event: event,
- className: "dark",
- callback: inner_clicked
- },
- ref_window);
- function inner_clicked(v, option, event) {
- //node.setProperty(propname,v);
- //graphcanvas.dirty_canvas = true;
- elem_that.innerText = v;
- innerChange(propname,v);
- return false;
- }
- });
-
- root.content.appendChild(elem);
-
- function innerChange(name, value)
- {
- console.log("change",name,value);
- //that.dirty_canvas = true;
- if(options.callback)
- options.callback(name,value);
- if(callback)
- callback(name,value);
- }
-
- return elem;
- }
-
- return root;
-};
-
Editor.prototype.createButton = function(name, icon_url, callback) {
var button = document.createElement("button");
if (icon_url) {
button.innerHTML = " ";
}
+ button.classList.add("btn");
button.innerHTML += name;
if(callback)
button.addEventListener("click", callback );
@@ -273,7 +127,7 @@ Editor.prototype.createButton = function(name, icon_url, callback) {
};
Editor.prototype.onLoadButton = function() {
- var panel = this.createPanel("Load session",{closable:true});
+ var panel = this.graphcanvas.createPanel("Load session",{closable:true});
//TO DO
this.root.appendChild(panel);
@@ -332,107 +186,6 @@ Editor.prototype.onDropItem = function(e)
}
}
-//shows the left side panel with the node info
-Editor.prototype.onShowNodePanel = function(node)
-{
- window.SELECTED_NODE = node;
- var panel = document.querySelector("#node-panel");
- if(panel)
- panel.close();
- var ref_window = this.graphcanvas.getCanvasWindow();
- panel = this.createPanel(node.title || "",{closable: true, window: ref_window });
- panel.id = "node-panel";
- panel.classList.add("settings");
- var that = this;
- var graphcanvas = this.graphcanvas;
-
- function inner_refresh()
- {
- panel.content.innerHTML = ""; //clear
- panel.addHTML(""+node.type+" "+(node.constructor.desc || "")+" ");
-
- panel.addHTML("Properties ");
-
- for(var i in node.properties)
- {
- var value = node.properties[i];
- var info = node.getPropertyInfo(i);
- var type = info.type || "string";
-
- //in case the user wants control over the side panel widget
- if( node.onAddPropertyToPanel && node.onAddPropertyToPanel(i,panel) )
- continue;
-
- panel.addWidget( info.widget || info.type, i, value, info, function(name,value){
- node.setProperty(name,value);
- graphcanvas.dirty_canvas = true;
- });
- }
-
- panel.addSeparator();
-
- /*
- panel.addHTML("Connections ");
- var connection_containers = panel.addHTML("
","connections");
- var inputs = connection_containers.querySelector(".inputs");
- var outputs = connection_containers.querySelector(".outputs");
- */
-
-
- panel.addButton("Delete",function(){
- node.graph.remove(node);
- panel.close();
- }).classList.add("delete");
- }
-
- function inner_showCodePad( node, propname )
- {
- panel.style.top = "calc( 50% - 250px)";
- panel.style.left = "calc( 50% - 400px)";
- panel.style.width = "800px";
- panel.style.height = "500px";
-
- if(window.CodeFlask) //disabled for now
- {
- panel.content.innerHTML = "
";
- var flask = new CodeFlask( "div.code", { language: 'js' });
- flask.updateCode(node.properties[propname]);
- flask.onUpdate( function(code) {
- node.setProperty(propname, code);
- });
- }
- else
- {
- panel.content.innerHTML = "";
- var textarea = panel.content.querySelector("textarea");
- textarea.value = node.properties[propname];
- textarea.addEventListener("keydown", function(e){
- //console.log(e);
- if(e.code == "Enter" && e.ctrlKey )
- {
- console.log("Assigned");
- node.setProperty(propname, textarea.value);
- }
- });
- textarea.style.height = "calc(100% - 40px)";
- }
- var assign = that.createButton( "Assign", null, function(){
- node.setProperty(propname, textarea.value);
- });
- panel.content.appendChild(assign);
- var button = that.createButton( "Close", null, function(){
- panel.style.height = "";
- inner_refresh();
- });
- button.style.float = "right";
- panel.content.appendChild(button);
- }
-
- inner_refresh();
-
- this.content.appendChild( panel );
-}
-
Editor.prototype.goFullscreen = function() {
if (this.root.requestFullscreen) {
this.root.requestFullscreen(Element.ALLOW_KEYBOARD_INPUT);
@@ -466,7 +219,7 @@ Editor.prototype.addMiniWindow = function(w, h) {
var canvas = miniwindow.querySelector("canvas");
var that = this;
- var graphcanvas = new LGraphCanvas(canvas, this.graph);
+ var graphcanvas = new LGraphCanvas( canvas, this.graph );
graphcanvas.show_info = false;
graphcanvas.background_image = "imgs/grid.png";
graphcanvas.scale = 0.25;
diff --git a/src/litegraph.js b/src/litegraph.js
index b2fcda9fbb..6ca05a9994 100755
--- a/src/litegraph.js
+++ b/src/litegraph.js
@@ -1380,6 +1380,9 @@
this.onNodeRemoved(node);
}
+ //close panels
+ this.sendActionToCanvas("checkPanels");
+
this.setDirtyCanvas(true, true);
this.change();
@@ -2672,6 +2675,23 @@
return null;
};
+ /**
+ * Returns the link info in the connection of an input slot
+ * @method getInputLink
+ * @param {number} slot
+ * @return {LLink} object or null
+ */
+ LGraphNode.prototype.getInputLink = function(slot) {
+ if (!this.inputs) {
+ return null;
+ }
+ if (slot < this.inputs.length) {
+ var slot_info = this.inputs[slot];
+ return this.graph.links[ slot_info.link ];
+ }
+ return null;
+ };
+
/**
* returns the node connected in the input slot
* @method getInputNode
@@ -3088,9 +3108,11 @@
}
this.inputs.push(o);
this.setSize( this.computeSize() );
+
if (this.onInputAdded) {
this.onInputAdded(o);
}
+
this.setDirtyCanvas(true, true);
return o;
};
@@ -3130,7 +3152,7 @@
*/
LGraphNode.prototype.removeInput = function(slot) {
this.disconnectInput(slot);
- this.inputs.splice(slot, 1);
+ var slot_info = this.inputs.splice(slot, 1);
for (var i = slot; i < this.inputs.length; ++i) {
if (!this.inputs[i]) {
continue;
@@ -3143,7 +3165,7 @@
}
this.setSize( this.computeSize() );
if (this.onInputRemoved) {
- this.onInputRemoved(slot);
+ this.onInputRemoved(slot, slot_info[0] );
}
this.setDirtyCanvas(true, true);
};
@@ -4708,6 +4730,7 @@ LGraphNode.prototype.executeAction = function(action)
}
graph.attachCanvas(this);
+ this.checkPanels();
this.setDirty(true, true);
};
@@ -4839,6 +4862,7 @@ LGraphNode.prototype.executeAction = function(action)
}
var canvas = this.canvas;
+
var ref_window = this.getCanvasWindow();
var document = ref_window.document; //hack used when moving canvas between windows
@@ -5241,6 +5265,7 @@ LGraphNode.prototype.executeAction = function(action)
//it wasn't clicked on the links boxes
if (!skip_action) {
var block_drag_node = false;
+ var pos = [e.canvasX - node.pos[0], e.canvasY - node.pos[1]];
//widgets
var widget = this.processNodeWidgets( node, this.canvas_mouse, e );
@@ -5253,18 +5278,31 @@ LGraphNode.prototype.executeAction = function(action)
if (is_double_click && this.selected_nodes[node.id]) {
//double click node
if (node.onDblClick) {
- node.onDblClick( e, [ e.canvasX - node.pos[0], e.canvasY - node.pos[1] ], this );
+ node.onDblClick( e, pos, this );
}
this.processNodeDblClicked(node);
block_drag_node = true;
}
//if do not capture mouse
- if ( node.onMouseDown && node.onMouseDown( e, [e.canvasX - node.pos[0], e.canvasY - node.pos[1]], this ) ) {
- block_drag_node = true;
- } else if (this.live_mode) {
- clicking_canvas_bg = true;
+ if ( node.onMouseDown && node.onMouseDown( e, pos, this ) ) {
block_drag_node = true;
+ } else {
+ //open subgraph button
+ if(node.subgraph && !node.skip_subgraph_button)
+ {
+ if ( !node.flags.collapsed && pos[0] > node.size[0] - LiteGraph.NODE_TITLE_HEIGHT && pos[1] < 0 ) {
+ var that = this;
+ setTimeout(function() {
+ that.openSubgraph(node.subgraph);
+ }, 10);
+ }
+ }
+
+ if (this.live_mode) {
+ clicking_canvas_bg = true;
+ block_drag_node = true;
+ }
}
if (!block_drag_node) {
@@ -5458,6 +5496,10 @@ LGraphNode.prototype.executeAction = function(action)
//mouse over a node
if (node) {
+
+ if(node.redraw_on_mouse)
+ this.dirty_canvas = true;
+
//this.canvas.style.cursor = "move";
if (!node.mouseOver) {
//mouse enter
@@ -6198,6 +6240,10 @@ LGraphNode.prototype.executeAction = function(action)
if (this.onShowNodePanel) {
this.onShowNodePanel(n);
}
+ else
+ {
+ this.showShowNodePanel(n);
+ }
if (this.onNodeDblClicked) {
this.onNodeDblClicked(n);
@@ -6348,6 +6394,10 @@ LGraphNode.prototype.executeAction = function(action)
LGraphCanvas.prototype.deleteSelectedNodes = function() {
for (var i in this.selected_nodes) {
var node = this.selected_nodes[i];
+
+ if(node.block_delete)
+ continue;
+
//autoconnect when possible (very basic, only takes into account first input-output)
if(node.inputs && node.inputs.length && node.outputs && node.outputs.length && LiteGraph.isValidConnection( node.inputs[0].type, node.outputs[0].type ) && node.inputs[0].link && node.outputs[0].links && node.outputs[0].links.length )
{
@@ -6970,14 +7020,8 @@ LGraphNode.prototype.executeAction = function(action)
var glow = false;
this.current_node = node;
- var color =
- node.color ||
- node.constructor.color ||
- LiteGraph.NODE_DEFAULT_COLOR;
- var bgcolor =
- node.bgcolor ||
- node.constructor.bgcolor ||
- LiteGraph.NODE_DEFAULT_BGCOLOR;
+ var color = node.color || node.constructor.color || LiteGraph.NODE_DEFAULT_COLOR;
+ var bgcolor = node.bgcolor || node.constructor.bgcolor || LiteGraph.NODE_DEFAULT_BGCOLOR;
//shadow and glow
if (node.mouseOver) {
@@ -7491,20 +7535,14 @@ LGraphNode.prototype.executeAction = function(action)
ctx.shadowColor = "transparent";
if (node.onDrawBackground) {
- node.onDrawBackground(ctx, this, this.canvas);
+ node.onDrawBackground(ctx, this, this.canvas, this.canvas_mouse );
}
//title bg (remember, it is rendered ABOVE the node)
if (render_title || title_mode == LiteGraph.TRANSPARENT_TITLE) {
//title bar
if (node.onDrawTitleBar) {
- node.onDrawTitleBar(
- ctx,
- title_height,
- size,
- this.ds.scale,
- fgcolor
- );
+ node.onDrawTitleBar( ctx, title_height, size, this.ds.scale, fgcolor );
} else if (
title_mode != LiteGraph.TRANSPARENT_TITLE &&
(node.constructor.title_color || this.render_title_colored)
@@ -7645,6 +7683,28 @@ LGraphNode.prototype.executeAction = function(action)
}
}
+ //subgraph box
+ if (!node.flags.collapsed && node.subgraph && !node.skip_subgraph_button) {
+ ctx.fillStyle = "#555";
+ var w = LiteGraph.NODE_TITLE_HEIGHT;
+ var x = node.size[0] - w;
+ if( shape == LiteGraph.BOX_SHAPE || low_quality)
+ ctx.fillRect(x+2, -w+2, w-4, w-4);
+ else
+ {
+ ctx.beginPath();
+ ctx.roundRect(x+2, -w+2, w-4, w-4,4);
+ ctx.fill();
+ }
+ ctx.fillStyle = "#333";
+ ctx.beginPath();
+ ctx.moveTo(x + w * 0.2, -w * 0.6);
+ ctx.lineTo(x + w * 0.8, -w * 0.6);
+ ctx.lineTo(x + w * 0.5, -w * 0.3);
+ ctx.fill();
+ }
+
+ //custom title render
if (node.onDrawTitle) {
node.onDrawTitle(ctx);
}
@@ -8883,6 +8943,7 @@ LGraphNode.prototype.executeAction = function(action)
}
if (!entries.length) {
+ console.log("no input entries");
return;
}
@@ -9752,6 +9813,341 @@ LGraphNode.prototype.executeAction = function(action)
return dialog;
};
+ LGraphCanvas.prototype.createPanel = function(title, options) {
+ options = options || {};
+
+ var ref_window = options.window || window;
+ var root = document.createElement("div");
+ root.className = "litegraph dialog";
+ root.innerHTML = "
";
+ root.header = root.querySelector(".dialog-header");
+
+ if(options.width)
+ root.style.width = options.width + (options.width.constructor === Number ? "px" : "");
+ if(options.height)
+ root.style.height = options.height + (options.height.constructor === Number ? "px" : "");
+ if(options.closable)
+ {
+ var close = document.createElement("span");
+ close.innerHTML = "✕";
+ close.classList.add("close");
+ close.addEventListener("click",function(){
+ root.close();
+ });
+ root.header.appendChild(close);
+ }
+ root.title_element = root.querySelector(".dialog-title");
+ root.title_element.innerText = title;
+ root.content = root.querySelector(".dialog-content");
+ root.footer = root.querySelector(".dialog-footer");
+
+ root.close = function()
+ {
+ this.parentNode.removeChild(this);
+ }
+
+ root.clear = function()
+ {
+ this.content.innerHTML = "";
+ }
+
+ root.addHTML = function(code, classname, on_footer)
+ {
+ var elem = document.createElement("div");
+ if(classname)
+ elem.className = classname;
+ elem.innerHTML = code;
+ if(on_footer)
+ root.footer.appendChild(elem);
+ else
+ root.content.appendChild(elem);
+ return elem;
+ }
+
+ root.addButton = function( name, callback, options )
+ {
+ var elem = document.createElement("button");
+ elem.innerText = name;
+ elem.options = options;
+ elem.classList.add("btn");
+ elem.addEventListener("click",callback);
+ root.footer.appendChild(elem);
+ return elem;
+ }
+
+ root.addSeparator = function()
+ {
+ var elem = document.createElement("div");
+ elem.className = "separator";
+ root.content.appendChild(elem);
+ }
+
+ root.addWidget = function( type, name, value, options, callback )
+ {
+ options = options || {};
+ var str_value = String(value);
+ if(type == "number")
+ str_value = value.toFixed(3);
+
+ var elem = document.createElement("div");
+ elem.className = "property";
+ elem.innerHTML = " ";
+ elem.querySelector(".property_name").innerText = name;
+ var value_element = elem.querySelector(".property_value");
+ value_element.innerText = str_value;
+ elem.dataset["property"] = name;
+ elem.dataset["type"] = options.type || type;
+ elem.options = options;
+ elem.value = value;
+
+ //if( type == "code" )
+ // elem.addEventListener("click", function(){ inner_showCodePad( node, this.dataset["property"] ); });
+ if (type == "boolean")
+ {
+ elem.classList.add("boolean");
+ if(value)
+ elem.classList.add("bool-on");
+ elem.addEventListener("click", function(){
+ //var v = node.properties[this.dataset["property"]];
+ //node.setProperty(this.dataset["property"],!v); this.innerText = v ? "true" : "false";
+ var propname = this.dataset["property"];
+ this.value = !this.value;
+ this.classList.toggle("bool-on");
+ this.querySelector(".property_value").innerText = this.value ? "true" : "false";
+ innerChange(propname, this.value );
+ });
+ }
+ else if (type == "string" || type == "number")
+ {
+ value_element.setAttribute("contenteditable",true);
+ value_element.addEventListener("keydown", function(e){
+ if(e.code == "Enter")
+ {
+ e.preventDefault();
+ this.blur();
+ }
+ });
+ value_element.addEventListener("blur", function(){
+ var v = this.innerText;
+ var propname = this.parentNode.dataset["property"];
+ var proptype = this.parentNode.dataset["type"];
+ if( proptype == "number")
+ v = Number(v);
+ innerChange(propname, v);
+ });
+ }
+ else if (type == "enum")
+ value_element.addEventListener("click", function(event){
+ var values = options.values || [];
+ var propname = this.parentNode.dataset["property"];
+ var elem_that = this;
+ var menu = new LiteGraph.ContextMenu(values,{
+ event: event,
+ className: "dark",
+ callback: inner_clicked
+ },
+ ref_window);
+ function inner_clicked(v, option, event) {
+ //node.setProperty(propname,v);
+ //graphcanvas.dirty_canvas = true;
+ elem_that.innerText = v;
+ innerChange(propname,v);
+ return false;
+ }
+ });
+
+ root.content.appendChild(elem);
+
+ function innerChange(name, value)
+ {
+ console.log("change",name,value);
+ //that.dirty_canvas = true;
+ if(options.callback)
+ options.callback(name,value);
+ if(callback)
+ callback(name,value);
+ }
+
+ return elem;
+ }
+
+ return root;
+ };
+
+ LGraphCanvas.prototype.showShowNodePanel = function( node )
+ {
+ window.SELECTED_NODE = node;
+ var panel = document.querySelector("#node-panel");
+ if(panel)
+ panel.close();
+ var ref_window = this.getCanvasWindow();
+ panel = this.createPanel(node.title || "",{closable: true, window: ref_window });
+ panel.id = "node-panel";
+ panel.node = node;
+ panel.classList.add("settings");
+ var that = this;
+ var graphcanvas = this;
+
+ function inner_refresh()
+ {
+ panel.content.innerHTML = ""; //clear
+ panel.addHTML(""+node.type+" "+(node.constructor.desc || "")+" ");
+
+ panel.addHTML("Properties ");
+
+ for(var i in node.properties)
+ {
+ var value = node.properties[i];
+ var info = node.getPropertyInfo(i);
+ var type = info.type || "string";
+
+ //in case the user wants control over the side panel widget
+ if( node.onAddPropertyToPanel && node.onAddPropertyToPanel(i,panel) )
+ continue;
+
+ panel.addWidget( info.widget || info.type, i, value, info, function(name,value){
+ node.setProperty(name,value);
+ graphcanvas.dirty_canvas = true;
+ });
+ }
+
+ panel.addSeparator();
+
+ if(node.onShowCustomPanelInfo)
+ node.onShowCustomPanelInfo(panel);
+
+ /*
+ panel.addHTML("Connections ");
+ var connection_containers = panel.addHTML("
","connections");
+ var inputs = connection_containers.querySelector(".inputs");
+ var outputs = connection_containers.querySelector(".outputs");
+ */
+
+ panel.addButton("Delete",function(){
+ if(node.block_delete)
+ return;
+ node.graph.remove(node);
+ panel.close();
+ }).classList.add("delete");
+ }
+
+ function inner_showCodePad( node, propname )
+ {
+ panel.style.top = "calc( 50% - 250px)";
+ panel.style.left = "calc( 50% - 400px)";
+ panel.style.width = "800px";
+ panel.style.height = "500px";
+
+ if(window.CodeFlask) //disabled for now
+ {
+ panel.content.innerHTML = "
";
+ var flask = new CodeFlask( "div.code", { language: 'js' });
+ flask.updateCode(node.properties[propname]);
+ flask.onUpdate( function(code) {
+ node.setProperty(propname, code);
+ });
+ }
+ else
+ {
+ panel.content.innerHTML = "";
+ var textarea = panel.content.querySelector("textarea");
+ textarea.value = node.properties[propname];
+ textarea.addEventListener("keydown", function(e){
+ //console.log(e);
+ if(e.code == "Enter" && e.ctrlKey )
+ {
+ console.log("Assigned");
+ node.setProperty(propname, textarea.value);
+ }
+ });
+ textarea.style.height = "calc(100% - 40px)";
+ }
+ var assign = that.createButton( "Assign", null, function(){
+ node.setProperty(propname, textarea.value);
+ });
+ panel.content.appendChild(assign);
+ var button = that.createButton( "Close", null, function(){
+ panel.style.height = "";
+ inner_refresh();
+ });
+ button.style.float = "right";
+ panel.content.appendChild(button);
+ }
+
+ inner_refresh();
+
+ this.canvas.parentNode.appendChild( panel );
+ }
+
+ LGraphCanvas.prototype.showSubgraphPropertiesDialog = function(node)
+ {
+ console.log("showing subgraph properties dialog");
+
+ var old_panel = this.canvas.parentNode.querySelector(".subgraph_dialog");
+ if(old_panel)
+ old_panel.close();
+
+ var panel = this.createPanel("Subgraph Inputs",{closable:true, width: 500});
+ panel.node = node;
+ panel.classList.add("subgraph_dialog");
+
+ function inner_refresh()
+ {
+ panel.clear();
+
+ //show currents
+ if(node.inputs)
+ for(var i = 0; i < node.inputs.length; ++i)
+ {
+ var input = node.inputs[i];
+ var html = "✕ ";
+ var elem = panel.addHTML(html,"subgraph_property");
+ elem.dataset["name"] = input.name;
+ elem.dataset["slot"] = i;
+ elem.querySelector(".name").innerText = input.name;
+ elem.querySelector(".type").innerText = input.type;
+ elem.querySelector("button").addEventListener("click",function(e){
+ node.removeInput( Number( this.parentNode.dataset["slot"] ) );
+ inner_refresh();
+ });
+ }
+ }
+
+ //add extra
+ var html = " + Name Type + ";
+ var elem = panel.addHTML(html,"subgraph_property extra", true);
+ elem.querySelector("button").addEventListener("click", function(e){
+ var elem = this.parentNode;
+ var name = elem.querySelector(".name").value;
+ var type = elem.querySelector(".type").value;
+ if(!name || node.findInputSlot(name) != -1)
+ return;
+ node.addInput(name,type);
+ elem.querySelector(".name").value = "";
+ elem.querySelector(".type").value = "";
+ inner_refresh();
+ });
+
+ inner_refresh();
+ this.canvas.parentNode.appendChild(panel);
+ return panel;
+ }
+
+ LGraphCanvas.prototype.checkPanels = function()
+ {
+ if(!this.canvas)
+ return;
+ var panels = this.canvas.parentNode.querySelectorAll(".litegraph.dialog");
+ for(var i = 0; i < panels.length; ++i)
+ {
+ var panel = panels[i];
+ if( !panel.node )
+ continue;
+ if( !panel.node.graph || panel.graph != this.graph )
+ panel.close();
+ }
+ }
+
LGraphCanvas.onMenuNodeCollapse = function(value, options, e, menu, node) {
node.collapse();
};
@@ -10056,12 +10452,11 @@ LGraphNode.prototype.executeAction = function(action)
callback: LGraphCanvas.onMenuNodeToSubgraph
});
- if (node.removable !== false) {
- options.push(null, {
- content: "Remove",
- callback: LGraphCanvas.onMenuNodeRemove
- });
- }
+ options.push(null, {
+ content: "Remove",
+ disabled: !(node.removable !== false && !node.block_delete ),
+ callback: LGraphCanvas.onMenuNodeRemove
+ });
if (node.graph && node.graph.onGetNodeMenuOptions) {
node.graph.onGetNodeMenuOptions(options, node);
diff --git a/src/nodes/base.js b/src/nodes/base.js
index 5e4c429d4b..e0f4050cda 100755
--- a/src/nodes/base.js
+++ b/src/nodes/base.js
@@ -52,6 +52,7 @@
return [["enabled", "boolean"]];
};
+ /*
Subgraph.prototype.onDrawTitle = function(ctx) {
if (this.flags.collapsed) {
return;
@@ -68,6 +69,7 @@
ctx.lineTo(x + w * 0.5, -w * 0.3);
ctx.fill();
};
+ */
Subgraph.prototype.onDblClick = function(e, pos, graphcanvas) {
var that = this;
@@ -76,6 +78,7 @@
}, 10);
};
+ /*
Subgraph.prototype.onMouseDown = function(e, pos, graphcanvas) {
if (
!this.flags.collapsed &&
@@ -88,6 +91,7 @@
}, 10);
}
};
+ */
Subgraph.prototype.onAction = function(action, param) {
this.subgraph.onAction(action, param);
diff --git a/src/nodes/gltextures.js b/src/nodes/gltextures.js
index 725f9ad65b..3c24ddf69e 100755
--- a/src/nodes/gltextures.js
+++ b/src/nodes/gltextures.js
@@ -660,7 +660,7 @@
u_texture: 0,
u_textureB: 1,
value: value,
- texSize: [width, height],
+ texSize: [width, height,1/width,1/height],
time: time
})
.draw(mesh);
@@ -675,7 +675,7 @@
uniform sampler2D u_texture;\n\
uniform sampler2D u_textureB;\n\
varying vec2 v_coord;\n\
- uniform vec2 texSize;\n\
+ uniform vec4 texSize;\n\
uniform float time;\n\
uniform float value;\n\
\n\
@@ -712,6 +712,20 @@
LGraphTextureOperation.registerPreset("displace","texture2D(u_texture, uv + (colorB.xy - vec2(0.5)) * value).xyz");
LGraphTextureOperation.registerPreset("grayscale","vec3(color.x + color.y + color.z) * value / 3.0");
LGraphTextureOperation.registerPreset("saturation","mix( vec3(color.x + color.y + color.z) / 3.0, color, value )");
+ LGraphTextureOperation.registerPreset("normalmap","\n\
+ float z0 = texture2D(u_texture, uv + vec2(-texSize.z, -texSize.w) ).x;\n\
+ float z1 = texture2D(u_texture, uv + vec2(0.0, -texSize.w) ).x;\n\
+ float z2 = texture2D(u_texture, uv + vec2(texSize.z, -texSize.w) ).x;\n\
+ float z3 = texture2D(u_texture, uv + vec2(-texSize.z, 0.0) ).x;\n\
+ float z4 = color.x;\n\
+ float z5 = texture2D(u_texture, uv + vec2(texSize.z, 0.0) ).x;\n\
+ float z6 = texture2D(u_texture, uv + vec2(-texSize.z, texSize.w) ).x;\n\
+ float z7 = texture2D(u_texture, uv + vec2(0.0, texSize.w) ).x;\n\
+ float z8 = texture2D(u_texture, uv + vec2(texSize.z, texSize.w) ).x;\n\
+ vec3 normal = vec3( z2 + 2.0*z4 + z7 - z0 - 2.0*z3 - z5, z5 + 2.0*z6 + z7 -z0 - 2.0*z1 - z2, 1.0 );\n\
+ normal.xy *= value;\n\
+ result.xyz = normalize(normal) * 0.5 + vec3(0.5);\n\
+ ");
LGraphTextureOperation.registerPreset("threshold","vec3(color.x > colorB.x * value ? 1.0 : 0.0,color.y > colorB.y * value ? 1.0 : 0.0,color.z > colorB.z * value ? 1.0 : 0.0)");
//webglstudio stuff...
@@ -744,7 +758,7 @@
};
this.properties.code = LGraphTextureShader.pixel_shader;
- this._uniforms = { u_value: 1, u_color: vec4.create(), in_texture: 0, texSize: vec2.create(), time: 0 };
+ this._uniforms = { u_value: 1, u_color: vec4.create(), in_texture: 0, texSize: vec4.create(), time: 0 };
}
LGraphTextureShader.title = "Shader";
@@ -910,6 +924,8 @@
}
uniforms.texSize[0] = w;
uniforms.texSize[1] = h;
+ uniforms.texSize[2] = 1/w;
+ uniforms.texSize[3] = 1/h;
uniforms.time = this.graph.getTime();
uniforms.u_value = this.properties.u_value;
uniforms.u_color.set( this.properties.u_color );
@@ -930,7 +946,7 @@
\n\
varying vec2 v_coord;\n\
uniform float time; //time in seconds\n\
-uniform vec2 texSize; //tex resolution\n\
+uniform vec4 texSize; //tex resolution\n\
uniform float u_value;\n\
uniform vec4 u_color;\n\n\
void main() {\n\
@@ -1580,6 +1596,69 @@ void main() {\n\
LGraphTextureDownsample
);
+
+
+ function LGraphTextureResize() {
+ this.addInput("Texture", "Texture");
+ this.addOutput("", "Texture");
+ this.properties = {
+ size: [512,512],
+ generate_mipmaps: false,
+ precision: LGraphTexture.DEFAULT
+ };
+ }
+
+ LGraphTextureResize.title = "Resize";
+ LGraphTextureResize.desc = "Resize Texture";
+ LGraphTextureResize.widgets_info = {
+ iterations: { type: "number", step: 1, precision: 0, min: 0 },
+ precision: { widget: "combo", values: LGraphTexture.MODE_VALUES }
+ };
+
+ LGraphTextureResize.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;
+ }
+
+ var width = this.properties.size[0] | 0;
+ var height = this.properties.size[1] | 0;
+ if(width == 0)
+ width = tex.width;
+ if(height == 0)
+ height = tex.height;
+ 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( !this._texture || this._texture.width != width || this._texture.height != height || this._texture.type != type )
+ this._texture = new GL.Texture( width, height, { type: type } );
+
+ tex.copyTo( this._texture );
+
+ if (this.properties.generate_mipmaps) {
+ this._texture.bind(0);
+ gl.generateMipmap(this._texture.texture_type);
+ this._texture.unbind(0);
+ }
+
+ this.setOutputData(0, this._texture);
+ };
+
+ LiteGraph.registerNodeType( "texture/resize", LGraphTextureResize );
+
// Texture Average *****************************************
function LGraphTextureAverage() {
this.addInput("Texture", "Texture");
@@ -2247,7 +2326,7 @@ void main() {\n\
this.addInput("Texture", "Texture");
this.addInput("Atlas", "Texture");
this.addOutput("", "Texture");
- this.properties = { enabled: true, num_row_symbols: 4, symbol_size: 16, brightness: 1, colorize: false, filter: false, invert: false, precision: LGraphTexture.DEFAULT, texture: null };
+ this.properties = { enabled: true, num_row_symbols: 4, symbol_size: 16, brightness: 1, colorize: false, filter: false, invert: false, precision: LGraphTexture.DEFAULT, generate_mipmaps: false, texture: null };
if (!LGraphTextureEncode._shader) {
LGraphTextureEncode._shader = new GL.Shader( Shader.SCREEN_VERTEX_SHADER, LGraphTextureEncode.pixel_shader );
@@ -2323,6 +2402,12 @@ void main() {\n\
tex.toViewport(LGraphTextureEncode._shader, uniforms);
});
+ if (this.properties.generate_mipmaps) {
+ this._tex.bind(0);
+ gl.generateMipmap(this._tex.texture_type);
+ this._tex.unbind(0);
+ }
+
this.setOutputData(0, this._tex);
};
diff --git a/src/nodes/shaders.js b/src/nodes/shaders.js
new file mode 100644
index 0000000000..f5b22969cb
--- /dev/null
+++ b/src/nodes/shaders.js
@@ -0,0 +1,429 @@
+(function(global) {
+ var LiteGraph = global.LiteGraph;
+ var LGraphCanvas = global.LGraphCanvas;
+
+ if (typeof GL == "undefined")
+ return;
+
+
+ //common actions to all shader node classes
+ function setShaderNode( node_ctor )
+ {
+ node_ctor.color = "#345";
+ }
+
+ function getShaderInputLinkName( node, slot )
+ {
+ if(!node.inputs)
+ return null;
+ var link = node.getInputLink( slot );
+ if( !link )
+ return null;
+ var origin_node = node.graph.getNodeById( link.origin_id );
+ if( !origin_node )
+ return null;
+ if(origin_node.getOutputVarName)
+ return origin_node.getOutputVarName(link.origin_slot);
+ //generate
+ return "link_" + origin_node.id + "_" + link.origin_slot;
+ }
+
+ function getShaderOutputLinkName( node, slot )
+ {
+ return "link_" + node.id + "_" + slot;
+ }
+
+ //used to host a shader body *******************
+ function LGShaderContext()
+ {
+ this.vs_template = "";
+ this.fs_template = "";
+ this._uniforms = {};
+ this._codeparts = {};
+ }
+
+ LGShaderContext.valid_types = ["float","vec2","vec3","vec4","sampler2D","mat3","mat4","int","boolean"];
+
+ LGShaderContext.prototype.clear = function()
+ {
+ this._uniforms = {};
+ this._codeparts = {};
+ }
+
+ LGShaderContext.prototype.addUniform = function( name, type )
+ {
+ this._uniforms[ name ] = type;
+ }
+
+ LGShaderContext.prototype.addCode = function( hook, code )
+ {
+ if(!this._codeparts[ hook ])
+ this._codeparts[ hook ] = code + "\n";
+ else
+ this._codeparts[ hook ] += code + "\n";
+ }
+
+ //generates the shader code from the template and the
+ LGShaderContext.prototype.computeShader = function( shader )
+ {
+ var uniforms = "";
+ for(var i in this._uniforms)
+ uniforms += "uniform " + this._uniforms[i] + " " + i + ";\n";
+
+ var parts = this._codeparts;
+ parts.uniforms = uniforms;
+
+ var vs_code = GL.Shader.replaceCodeUsingContext( this.vs_template, parts );
+ var fs_code = GL.Shader.replaceCodeUsingContext( this.fs_template, parts );
+
+ try
+ {
+ if(shader)
+ shader.updateShader( vs_code, fs_code );
+ else
+ shader = new GL.Shader( vs_code, fs_code );
+ return shader;
+ }
+ catch (err)
+ {
+ return null;
+ }
+
+ return null;//never here
+ }
+
+ // LGraphShaderGraph *****************************
+ // applies a shader graph to texture, it can be uses as an example
+
+ function LGraphShaderGraph() {
+ this.addOutput("out", "texture");
+ this.properties = { width: 0, height: 0, alpha: false, precision: 0 };
+
+ this.subgraph = new LiteGraph.LGraph();
+ this.subgraph._subgraph_node = this;
+ this.subgraph._is_subgraph = true;
+
+ var subnode = LiteGraph.createNode("shader/fragcolor");
+ this.subgraph.pos = [300,100];
+ this.subgraph.add( subnode );
+
+ this.size = [180,60];
+ this.redraw_on_mouse = true; //force redraw
+
+ this._uniforms = {};
+ this._shader = null;
+ this._context = new LGShaderContext();
+ this._context.vs_template = GL.Shader.SCREEN_VERTEX_SHADER;
+ this._context.fs_template = LGraphShaderGraph.template;
+ }
+
+ LGraphShaderGraph.template = "\n\
+ precision highp float;\n\
+ varying vec2 v_coord;\n\
+ {{varying}}\n\
+ {{uniforms}}\n\
+ void main() {\n\
+ vec2 uv = v_coord;\n\
+ vec4 color = vec4(0.0);\n\
+ {{fs_code}}\n\
+ gl_FragColor = color;\n\
+ }\n\
+ ";
+
+ LGraphShaderGraph.widgets_info = {
+ precision: { widget: "combo", values: LGraphTexture.MODE_VALUES }
+ };
+
+ LGraphShaderGraph.title = "ShaderGraph";
+ LGraphShaderGraph.desc = "Builds a shader using a graph";
+
+ LGraphShaderGraph.prototype.onSerialize = function(o)
+ {
+ o.subgraph = this.subgraph.serialize();
+ }
+
+ LGraphShaderGraph.prototype.onConfigure = function(o)
+ {
+ this.subgraph.configure(o.subgraph);
+ }
+
+ LGraphShaderGraph.prototype.onExecute = function() {
+ if (!this.isOutputConnected(0))
+ return;
+
+ 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 texture = this._texture;
+ if ( !texture || texture.width != w || texture.height != h || texture.type != type ) {
+ texture = this._texture = new GL.Texture(w, h, {
+ type: type,
+ format: this.alpha ? gl.RGBA : gl.RGB,
+ filter: gl.LINEAR
+ });
+ }
+
+ var shader = this.getShader();
+ if(!shader)
+ return;
+
+ var uniforms = this._uniforms;
+
+ var tex_slot = 0;
+ if(this.inputs)
+ for(var i = 0; i < this.inputs.length; ++i)
+ {
+ var input = this.inputs[i];
+ var data = this.getInputData(i);
+ if(input.type == "texture")
+ {
+ if(!data)
+ data = GL.Texture.getWhiteTexture();
+ data = data.bind(tex_slot++);
+ }
+
+ if(data != null)
+ uniforms[ "u_" + input.name ] = data;
+ }
+
+ var mesh = GL.Mesh.getScreenQuad();
+
+ gl.disable( gl.DEPTH_TEST );
+ gl.disable( gl.BLEND );
+
+ texture.drawTo(function(){
+ shader.uniforms( uniforms );
+ shader.draw( mesh );
+ });
+
+ //use subgraph output
+ this.setOutputData(0, texture );
+ };
+
+ LGraphShaderGraph.prototype.onInputAdded = function( slot_info )
+ {
+ var subnode = LiteGraph.createNode("shader/uniform");
+ subnode.properties.name = slot_info.name;
+ subnode.properties.type = slot_info.type;
+ this.subgraph.add( subnode );
+ }
+
+ LGraphShaderGraph.prototype.onInputRemoved = function( slot, slot_info )
+ {
+ var nodes = this.subgraph.findNodesByType("shader/uniform");
+ for(var i = 0; i < nodes.length; ++i)
+ {
+ var node = nodes[i];
+ if(node.properties.name == slot_info.name )
+ this.subgraph.remove( node );
+ }
+ }
+
+ LGraphShaderGraph.prototype.computeSize = function()
+ {
+ var num_inputs = this.inputs ? this.inputs.length : 0;
+ var num_outputs = this.outputs ? this.outputs.length : 0;
+ return [ 200, Math.max(num_inputs,num_outputs) * LiteGraph.NODE_SLOT_HEIGHT + LiteGraph.NODE_TITLE_HEIGHT + 10];
+ }
+
+ LGraphShaderGraph.prototype.getShader = function()
+ {
+ //if subgraph not changed?
+ if(this._shader && this._shader._version == this.subgraph._version)
+ return this._shader;
+
+ //prepare context
+ this._context.clear();
+
+ //gets code from graph
+ this.subgraph.sendEventToAllNodes("onGetCode", this._context);
+
+ //compile shader
+ var shader = this._context.computeShader();
+ if(!shader)
+ {
+ this.boxcolor = "red";
+ return this._shader;
+ }
+ else
+ this.boxcolor = null;
+
+ this._shader = shader;
+ shader._version = this.subgraph._version;
+ return shader;
+ }
+
+ LGraphShaderGraph.prototype.onDrawBackground = function(ctx, graphcanvas, canvas, pos)
+ {
+ if(this.flags.collapsed)
+ return;
+
+ var y = this.size[1] - LiteGraph.NODE_TITLE_HEIGHT + 0.5;
+
+ //button
+ var over = LiteGraph.isInsideRectangle(pos[0],pos[1],this.pos[0],this.pos[1] + y,this.size[0],LiteGraph.NODE_TITLE_HEIGHT);
+ ctx.fillStyle = over ? "#555" : "#222";
+ ctx.beginPath();
+ ctx.roundRect( 0, y, this.size[0]+1, LiteGraph.NODE_TITLE_HEIGHT, 0, 8);
+ ctx.fill();
+
+ //button
+ ctx.textAlign = "center";
+ ctx.font = "24px Arial";
+ ctx.fillStyle = over ? "#DDD" : "#999";
+ ctx.fillText( "+", this.size[0] * 0.5, y + 24 );
+ }
+
+ LGraphShaderGraph.prototype.onMouseDown = function(e, localpos, graphcanvas)
+ {
+ var y = this.size[1] - LiteGraph.NODE_TITLE_HEIGHT + 0.5;
+ if(localpos[1] > y)
+ {
+ graphcanvas.showSubgraphPropertiesDialog(this);
+ }
+ }
+
+ LiteGraph.registerNodeType( "texture/shaderGraph", LGraphShaderGraph );
+
+ //Shader Nodes ***************************
+
+ //applies a shader graph to a code
+ function LGraphShaderUniform() {
+ this.addOutput("out", "");
+ this.properties = { name: "", type: "" };
+ }
+
+ LGraphShaderUniform.title = "Uniform";
+ LGraphShaderUniform.desc = "Input data for the shader";
+
+ LGraphShaderUniform.prototype.getTitle = function()
+ {
+ return "uniform " + this.properties.name;
+ }
+
+ LGraphShaderUniform.prototype.onGetCode = function( context )
+ {
+ var type = this.properties.type;
+ if( !type )
+ return;
+ if(type == "number")
+ type = "float";
+ else if(type == "texture")
+ type = "sampler2D";
+ if ( LGShaderContext.valid_types.indexOf(type) == -1 )
+ return;
+ context.addUniform( "u_" + this.properties.name, type );
+ }
+
+ LGraphShaderUniform.prototype.getOutputVarName = function(slot)
+ {
+ return "u_" + this.properties.name;
+ }
+
+ setShaderNode( LGraphShaderUniform );
+
+ LiteGraph.registerNodeType( "shader/uniform", LGraphShaderUniform );
+
+
+
+ function LGraphShaderAttribute() {
+ this.addOutput("out", "vec2");
+ this.properties = { name: "coord", type: "vec2" };
+ }
+
+ LGraphShaderAttribute.title = "Attribute";
+ LGraphShaderAttribute.desc = "Input data from mesh attribute";
+
+ LGraphShaderAttribute.prototype.getTitle = function()
+ {
+ return "att. " + this.properties.name;
+ }
+
+ LGraphShaderAttribute.prototype.onGetCode = function( context )
+ {
+ var type = this.properties.type;
+ if( !type || LGShaderContext.valid_types.indexOf(type) == -1 )
+ return;
+ if(type == "number")
+ type = "float";
+ if( this.properties.name != "coord")
+ context.addCode( "varying", " varying " + type +" v_" + this.properties.name );
+ }
+
+ LGraphShaderAttribute.prototype.getOutputVarName = function(slot)
+ {
+ return "v_" + this.properties.name;
+ }
+
+ setShaderNode( LGraphShaderAttribute );
+
+ LiteGraph.registerNodeType( "shader/attribute", LGraphShaderAttribute );
+
+
+ function LGraphShaderSampler2D() {
+ this.addInput("tex", "sampler2D");
+ this.addInput("uv", "vec2");
+ this.addOutput("rgba", "vec4");
+ }
+
+ LGraphShaderSampler2D.title = "Sampler2D";
+ LGraphShaderSampler2D.desc = "Reads a pixel from a texture";
+
+ LGraphShaderSampler2D.prototype.onGetCode = function( context )
+ {
+ var texname = getShaderInputLinkName( this, 0 );
+ var code;
+ if(texname)
+ {
+ var uvname = getShaderInputLinkName( this, 1 ) || "v_coord";
+ code = "vec4 " + getShaderOutputLinkName( this, 0 ) + " = texture2D("+texname+","+uvname+");";
+ }
+ else
+ code = "vec4 " + getShaderOutputLinkName( this, 0 ) + " = vec4(0.0);";
+ context.addCode( "fs_code", code );
+ }
+
+ setShaderNode( LGraphShaderSampler2D );
+
+ LiteGraph.registerNodeType( "shader/sampler2D", LGraphShaderSampler2D );
+
+ //*********************************
+
+ function LGraphShaderFragColor() {
+ this.addInput("color", "float,vec2,vec3,vec4");
+ this.block_delete = true;
+ }
+
+ LGraphShaderFragColor.title = "FragColor";
+ LGraphShaderFragColor.desc = "Pixel final color";
+
+ LGraphShaderFragColor.prototype.onGetCode = function( context )
+ {
+ var link_name = getShaderInputLinkName( this, 0 );
+ if(!link_name)
+ return;
+
+ var code = link_name;
+ var type = this.getInputDataType(0);
+ if(type == "float")
+ code = "vec4(" + code + ");";
+ else if(type == "vec2")
+ code = "vec4(" + code + ",0.0,1.0);";
+ else if(type == "vec3")
+ code = "vec4(" + code + ",1.0);";
+
+ context.addCode("fs_code", "color = " + code + ";\n");
+ }
+
+ setShaderNode( LGraphShaderFragColor );
+
+ LiteGraph.registerNodeType( "shader/fragcolor", LGraphShaderFragColor );
+
+})(this);
\ No newline at end of file