diff --git a/build/litegraph.js b/build/litegraph.js index 19d1a72ac..083569e56 100644 --- a/build/litegraph.js +++ b/build/litegraph.js @@ -322,6 +322,7 @@ LGraph.prototype.clear = function() //nodes this._nodes = []; this._nodes_by_id = {}; + this._nodes_in_order = null; //nodes that are executable sorted in execution order //links this.last_link_id = 0; @@ -398,7 +399,8 @@ LGraph.prototype.detachCanvas = function(graphcanvas) LGraph.prototype.start = function(interval) { - if(this.status == LGraph.STATUS_RUNNING) return; + if( this.status == LGraph.STATUS_RUNNING ) + return; this.status = LGraph.STATUS_RUNNING; if(this.onPlayEvent) @@ -424,7 +426,7 @@ LGraph.prototype.start = function(interval) LGraph.prototype.stop = function() { - if(this.status == LGraph.STATUS_STOPPED) + if( this.status == LGraph.STATUS_STOPPED ) return; this.status = LGraph.STATUS_STOPPED; @@ -500,14 +502,13 @@ LGraph.prototype.runStep = function(num) * nodes with only inputs. * @method updateExecutionOrder */ - LGraph.prototype.updateExecutionOrder = function() { - this._nodes_in_order = this.computeExecutionOrder(); + this._nodes_in_order = this.computeExecutionOrder( true ); } //This is more internal, it computes the order and returns it -LGraph.prototype.computeExecutionOrder = function() +LGraph.prototype.computeExecutionOrder = function( only_onExecute ) { var L = []; var S = []; @@ -519,6 +520,9 @@ LGraph.prototype.computeExecutionOrder = function() for (var i = 0, l = this._nodes.length; i < l; ++i) { var n = this._nodes[i]; + if( only_onExecute && !n.onExecute ) + continue; + M[n.id] = n; //add to pending nodes var num = 0; //num of input connections @@ -791,13 +795,14 @@ LGraph.prototype.remove = function(node) /** * Returns a node by its id. * @method getNodeById -* @param {String} id +* @param {Number} id */ -LGraph.prototype.getNodeById = function(id) +LGraph.prototype.getNodeById = function( id ) { - if(id==null) return null; - return this._nodes_by_id[id]; + if( id == null ) + return null; + return this._nodes_by_id[ id ]; } /** @@ -1661,16 +1666,20 @@ LGraphNode.prototype.isOutputConnected = function(slot) */ LGraphNode.prototype.getOutputNodes = function(slot) { - if(!this.outputs || this.outputs.length == 0) return null; - if(slot < this.outputs.length) - { - var output = this.outputs[slot]; - var r = []; - for(var i = 0; i < output.length; i++) - r.push( this.graph.getNodeById( output.links[i].target_id )); - return r; - } - return null; + if(!this.outputs || this.outputs.length == 0) + return null; + + if(slot >= this.outputs.length) + return null; + + var output = this.outputs[slot]; + if(!output.links || output.links.length == 0) + return null; + + var r = []; + for(var i = 0; i < output.links.length; i++) + r.push( this.graph.getNodeById( output.links[i] )); + return r; } /** @@ -4765,6 +4774,11 @@ LGraphCanvas.showMenuNodeInputs = function(node, e, prev_menu) for (var i in options) { var entry = options[i]; + if(!entry) + { + entries.push(null); + continue; + } var label = entry[0]; if(entry[2] && entry[2].label) label = entry[2].label; @@ -10530,9 +10544,6 @@ if(typeof(LiteGraph) != "undefined") this.addInput("Range","number"); this.addOutput("Texture","Texture"); this.properties = { distance:100, range: 50, high_precision: false }; - - if(!LGraphTextureDepthRange._shader) - LGraphTextureDepthRange._shader = new GL.Shader( Shader.SCREEN_VERTEX_SHADER, LGraphTextureDepthRange.pixel_shader ); } LGraphTextureDepthRange.title = "Depth Range"; @@ -10572,6 +10583,8 @@ if(typeof(LiteGraph) != "undefined") 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 ); var shader = LGraphTextureDepthRange._shader; //TODO: this asumes we have LiteScene, change it @@ -10621,9 +10634,6 @@ if(typeof(LiteGraph) != "undefined") this.addInput("Intensity","number"); this.addOutput("Blurred","Texture"); this.properties = { intensity: 1, iterations: 1, preserve_aspect: false, scale:[1,1] }; - - if(!LGraphTextureBlur._shader) - LGraphTextureBlur._shader = new GL.Shader( Shader.SCREEN_VERTEX_SHADER, LGraphTextureBlur.pixel_shader ); } LGraphTextureBlur.title = "Blur"; @@ -10715,6 +10725,169 @@ if(typeof(LiteGraph) != "undefined") LiteGraph.registerNodeType("texture/blur", LGraphTextureBlur ); + // Texture Blur ***************************************** + 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 ) + { + //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 }); + //this._final_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 Webcam ***************************************** function LGraphTextureWebcam() { diff --git a/src/litegraph.js b/src/litegraph.js index 11c524dbd..d75a6737d 100755 --- a/src/litegraph.js +++ b/src/litegraph.js @@ -321,6 +321,7 @@ LGraph.prototype.clear = function() //nodes this._nodes = []; this._nodes_by_id = {}; + this._nodes_in_order = null; //nodes that are executable sorted in execution order //links this.last_link_id = 0; @@ -397,7 +398,8 @@ LGraph.prototype.detachCanvas = function(graphcanvas) LGraph.prototype.start = function(interval) { - if(this.status == LGraph.STATUS_RUNNING) return; + if( this.status == LGraph.STATUS_RUNNING ) + return; this.status = LGraph.STATUS_RUNNING; if(this.onPlayEvent) @@ -423,7 +425,7 @@ LGraph.prototype.start = function(interval) LGraph.prototype.stop = function() { - if(this.status == LGraph.STATUS_STOPPED) + if( this.status == LGraph.STATUS_STOPPED ) return; this.status = LGraph.STATUS_STOPPED; @@ -499,14 +501,13 @@ LGraph.prototype.runStep = function(num) * nodes with only inputs. * @method updateExecutionOrder */ - LGraph.prototype.updateExecutionOrder = function() { - this._nodes_in_order = this.computeExecutionOrder(); + this._nodes_in_order = this.computeExecutionOrder( true ); } //This is more internal, it computes the order and returns it -LGraph.prototype.computeExecutionOrder = function() +LGraph.prototype.computeExecutionOrder = function( only_onExecute ) { var L = []; var S = []; @@ -518,6 +519,9 @@ LGraph.prototype.computeExecutionOrder = function() for (var i = 0, l = this._nodes.length; i < l; ++i) { var n = this._nodes[i]; + if( only_onExecute && !n.onExecute ) + continue; + M[n.id] = n; //add to pending nodes var num = 0; //num of input connections @@ -790,13 +794,14 @@ LGraph.prototype.remove = function(node) /** * Returns a node by its id. * @method getNodeById -* @param {String} id +* @param {Number} id */ -LGraph.prototype.getNodeById = function(id) +LGraph.prototype.getNodeById = function( id ) { - if(id==null) return null; - return this._nodes_by_id[id]; + if( id == null ) + return null; + return this._nodes_by_id[ id ]; } /** @@ -1660,16 +1665,20 @@ LGraphNode.prototype.isOutputConnected = function(slot) */ LGraphNode.prototype.getOutputNodes = function(slot) { - if(!this.outputs || this.outputs.length == 0) return null; - if(slot < this.outputs.length) - { - var output = this.outputs[slot]; - var r = []; - for(var i = 0; i < output.length; i++) - r.push( this.graph.getNodeById( output.links[i].target_id )); - return r; - } - return null; + if(!this.outputs || this.outputs.length == 0) + return null; + + if(slot >= this.outputs.length) + return null; + + var output = this.outputs[slot]; + if(!output.links || output.links.length == 0) + return null; + + var r = []; + for(var i = 0; i < output.links.length; i++) + r.push( this.graph.getNodeById( output.links[i] )); + return r; } /** @@ -4764,6 +4773,11 @@ LGraphCanvas.showMenuNodeInputs = function(node, e, prev_menu) for (var i in options) { var entry = options[i]; + if(!entry) + { + entries.push(null); + continue; + } var label = entry[0]; if(entry[2] && entry[2].label) label = entry[2].label; diff --git a/src/nodes/gltextures.js b/src/nodes/gltextures.js index ebd92724a..e86103bb8 100755 --- a/src/nodes/gltextures.js +++ b/src/nodes/gltextures.js @@ -1645,9 +1645,6 @@ if(typeof(LiteGraph) != "undefined") this.addInput("Range","number"); this.addOutput("Texture","Texture"); this.properties = { distance:100, range: 50, high_precision: false }; - - if(!LGraphTextureDepthRange._shader) - LGraphTextureDepthRange._shader = new GL.Shader( Shader.SCREEN_VERTEX_SHADER, LGraphTextureDepthRange.pixel_shader ); } LGraphTextureDepthRange.title = "Depth Range"; @@ -1687,6 +1684,8 @@ if(typeof(LiteGraph) != "undefined") 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 ); var shader = LGraphTextureDepthRange._shader; //TODO: this asumes we have LiteScene, change it @@ -1736,9 +1735,6 @@ if(typeof(LiteGraph) != "undefined") this.addInput("Intensity","number"); this.addOutput("Blurred","Texture"); this.properties = { intensity: 1, iterations: 1, preserve_aspect: false, scale:[1,1] }; - - if(!LGraphTextureBlur._shader) - LGraphTextureBlur._shader = new GL.Shader( Shader.SCREEN_VERTEX_SHADER, LGraphTextureBlur.pixel_shader ); } LGraphTextureBlur.title = "Blur"; @@ -1830,6 +1826,169 @@ if(typeof(LiteGraph) != "undefined") LiteGraph.registerNodeType("texture/blur", LGraphTextureBlur ); + // Texture Blur ***************************************** + 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 ) + { + //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 }); + //this._final_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 Webcam ***************************************** function LGraphTextureWebcam() {