From f1737c56471be201ac9115f5eca8ce2a82f05763 Mon Sep 17 00:00:00 2001 From: Brian Chirls Date: Fri, 6 Dec 2013 12:43:47 +0900 Subject: [PATCH 01/45] Support string input types (will be used for expressions) --- seriously.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/seriously.js b/seriously.js index c0b57c4..8c11355 100644 --- a/seriously.js +++ b/seriously.js @@ -4268,6 +4268,21 @@ } return true; + }, + 'string': function (value) { + if (typeof value === 'string') { + return value; + } + + if (value !== 0 && !value) { + return ''; + } + + if (value.toString) { + return value.toString(); + } + + return String(value); } //todo: date/time }; From 78b358d815f52d45791415ed0cce5d2991368237 Mon Sep 17 00:00:00 2001 From: Brian Chirls Date: Fri, 6 Dec 2013 17:26:09 +0900 Subject: [PATCH 02/45] Reduce jank and GPU resource usage: 1. Reuse identical shaders instead of duplicating 2. Initialize fixed shaders when node is created instead of waiting for render --- effects/seriously.ascii.js | 5 +-- effects/seriously.bleach-bypass.js | 1 + effects/seriously.blur.js | 5 +-- effects/seriously.brightness-contrast.js | 1 + effects/seriously.chroma.js | 1 + effects/seriously.color.js | 1 + effects/seriously.colorcomplements.js | 1 + effects/seriously.colorcube.js | 1 + effects/seriously.daltonize.js | 1 + effects/seriously.directionblur.js | 3 +- effects/seriously.dither.js | 1 + effects/seriously.emboss.js | 1 + effects/seriously.exposure.js | 1 + effects/seriously.fader.js | 1 + effects/seriously.falsecolor.js | 1 + effects/seriously.filmgrain.js | 1 + effects/seriously.hex.js | 1 + effects/seriously.highlights-shadows.js | 1 + effects/seriously.hue-saturation.js | 1 + effects/seriously.invert.js | 1 + effects/seriously.kaleidoscope.js | 1 + effects/seriously.layers.js | 1 + effects/seriously.linear-transfer.js | 1 + effects/seriously.lumakey.js | 1 + effects/seriously.nightvision.js | 1 + effects/seriously.repeat.js | 1 + effects/seriously.ripple.js | 1 + effects/seriously.scanlines.js | 1 + effects/seriously.sepia.js | 1 + effects/seriously.sketch.js | 1 + effects/seriously.split.js | 4 +-- effects/seriously.tone.js | 1 + effects/seriously.tvglitch.js | 3 +- effects/seriously.vignette.js | 1 + effects/seriously.whitebalance.js | 2 +- seriously.js | 42 +++++++++++++++++++----- 36 files changed, 76 insertions(+), 17 deletions(-) diff --git a/effects/seriously.ascii.js b/effects/seriously.ascii.js index d975eaf..f2370ae 100644 --- a/effects/seriously.ascii.js +++ b/effects/seriously.ascii.js @@ -86,10 +86,11 @@ scaledBuffer = new Seriously.util.FrameBuffer(gl, 1, 1); this.uniforms.transform = identity; + + baseShader = this.baseShader; }, + commonShader: true, shader: function (inputs, shaderSource) { - baseShader = new Seriously.util.ShaderProgram(this.gl, shaderSource.vertex, shaderSource.fragment); - shaderSource.fragment = '#ifdef GL_ES\n\n' + 'precision mediump float;\n\n' + '#endif\n\n' + diff --git a/effects/seriously.bleach-bypass.js b/effects/seriously.bleach-bypass.js index 7fa9065..f771276 100644 --- a/effects/seriously.bleach-bypass.js +++ b/effects/seriously.bleach-bypass.js @@ -28,6 +28,7 @@ */ Seriously.plugin('bleach-bypass', { + commonShader: true, shader: function (inputs, shaderSource) { shaderSource.fragment = '#ifdef GL_ES\n\n' + 'precision mediump float;\n\n' + diff --git a/effects/seriously.blur.js b/effects/seriously.blur.js index 5b5eacd..17cfe0d 100644 --- a/effects/seriously.blur.js +++ b/effects/seriously.blur.js @@ -67,9 +67,12 @@ http://v002.info/plugins/v002-blurs/ return; } + baseShader = this.baseShader; + fbHorizontal = new Seriously.util.FrameBuffer(gl, this.width, this.height); fbVertical = new Seriously.util.FrameBuffer(gl, this.width, this.height); }, + commonShader: true, shader: function (inputs, shaderSource) { var gl = this.gl, /* @@ -79,8 +82,6 @@ http://v002.info/plugins/v002-blurs/ maxVaryings = gl.getParameter(gl.MAX_VARYING_VECTORS), defineVaryings = (maxVaryings >= 10 ? '#define USE_VARYINGS' : ''); - baseShader = new Seriously.util.ShaderProgram(gl, shaderSource.vertex, shaderSource.fragment); - shaderSource.vertex = [ defineVaryings, '#define PI ' + Math.PI, diff --git a/effects/seriously.brightness-contrast.js b/effects/seriously.brightness-contrast.js index 6f60c73..d1f99b4 100644 --- a/effects/seriously.brightness-contrast.js +++ b/effects/seriously.brightness-contrast.js @@ -18,6 +18,7 @@ 'use strict'; Seriously.plugin('brightness-contrast', { + commonShader: true, shader: function (inputs, shaderSource) { shaderSource.fragment = [ '#ifdef GL_ES\n', diff --git a/effects/seriously.chroma.js b/effects/seriously.chroma.js index 0c69502..d206a30 100644 --- a/effects/seriously.chroma.js +++ b/effects/seriously.chroma.js @@ -25,6 +25,7 @@ todo: rename parameters */ Seriously.plugin('chroma', { + commonShader: true, shader: function (inputs, shaderSource) { shaderSource.vertex = [ '#ifdef GL_ES', diff --git a/effects/seriously.color.js b/effects/seriously.color.js index 0053fcf..65f8714 100644 --- a/effects/seriously.color.js +++ b/effects/seriously.color.js @@ -18,6 +18,7 @@ 'use strict'; Seriously.plugin('color', { + commonShader: true, shader: function(inputs, shaderSource, utilities) { shaderSource.fragment = '#ifdef GL_ES\n\n' + 'precision mediump float;\n\n' + diff --git a/effects/seriously.colorcomplements.js b/effects/seriously.colorcomplements.js index b36b79c..7de300d 100644 --- a/effects/seriously.colorcomplements.js +++ b/effects/seriously.colorcomplements.js @@ -18,6 +18,7 @@ 'use strict'; Seriously.plugin('colorcomplements', { + commonShader: true, shader: function (inputs, shaderSource) { shaderSource.fragment = [ '#ifdef GL_ES', diff --git a/effects/seriously.colorcube.js b/effects/seriously.colorcube.js index eb34cf0..82a3aff 100644 --- a/effects/seriously.colorcube.js +++ b/effects/seriously.colorcube.js @@ -22,6 +22,7 @@ //todo: find a way to not invert every single texture Seriously.plugin('colorcube', { + commonShader: true, shader: function (inputs, shaderSource) { shaderSource.fragment = '#ifdef GL_ES\n' + 'precision mediump float;\n' + diff --git a/effects/seriously.daltonize.js b/effects/seriously.daltonize.js index db3c5c8..d6d3ced 100644 --- a/effects/seriously.daltonize.js +++ b/effects/seriously.daltonize.js @@ -53,6 +53,7 @@ * */ Seriously.plugin('daltonize', { + commonShader: true, shader: function (inputs, shaderSource) { //Vertex shader shaderSource.vertex = '#ifdef GL_ES\n' + diff --git a/effects/seriously.directionblur.js b/effects/seriously.directionblur.js index 37adaa9..d9f94be 100644 --- a/effects/seriously.directionblur.js +++ b/effects/seriously.directionblur.js @@ -68,6 +68,7 @@ http://v002.info/plugins/v002-blurs/ new Seriously.util.FrameBuffer(gl, this.width, this.height) ]; }, + commonShader: true, shader: function (inputs, shaderSource) { var gl = this.gl, /* @@ -77,7 +78,7 @@ http://v002.info/plugins/v002-blurs/ maxVaryings = gl.getParameter(gl.MAX_VARYING_VECTORS), defineVaryings = (maxVaryings >= 10 ? '#define USE_VARYINGS' : ''); - baseShader = new Seriously.util.ShaderProgram(gl, shaderSource.vertex, shaderSource.fragment); + baseShader = this.baseShader; shaderSource.vertex = [ defineVaryings, diff --git a/effects/seriously.dither.js b/effects/seriously.dither.js index be20da4..56e1cf3 100644 --- a/effects/seriously.dither.js +++ b/effects/seriously.dither.js @@ -24,6 +24,7 @@ */ Seriously.plugin('dither', { + commonShader: true, shader: function (inputs, shaderSource) { shaderSource.fragment = [ '#ifdef GL_ES\n', diff --git a/effects/seriously.emboss.js b/effects/seriously.emboss.js index 3ce94a4..36b5b1f 100644 --- a/effects/seriously.emboss.js +++ b/effects/seriously.emboss.js @@ -18,6 +18,7 @@ 'use strict'; Seriously.plugin('emboss', { + commonShader: true, shader: function (inputs, shaderSource) { shaderSource.fragment = '#ifdef GL_ES\n\n' + 'precision mediump float;\n\n' + diff --git a/effects/seriously.exposure.js b/effects/seriously.exposure.js index 5b827d0..d189208 100644 --- a/effects/seriously.exposure.js +++ b/effects/seriously.exposure.js @@ -27,6 +27,7 @@ */ Seriously.plugin('exposure', { + commonShader: true, shader: function (inputs, shaderSource) { shaderSource.fragment = '#ifdef GL_ES\n\n' + 'precision mediump float;\n\n' + diff --git a/effects/seriously.fader.js b/effects/seriously.fader.js index 5cb2055..47ecd6f 100644 --- a/effects/seriously.fader.js +++ b/effects/seriously.fader.js @@ -18,6 +18,7 @@ 'use strict'; Seriously.plugin('fader', { + commonShader: true, shader: function (inputs, shaderSource) { shaderSource.fragment = '#ifdef GL_ES\n\n' + 'precision mediump float;\n\n' + diff --git a/effects/seriously.falsecolor.js b/effects/seriously.falsecolor.js index a0d397e..a8a609f 100644 --- a/effects/seriously.falsecolor.js +++ b/effects/seriously.falsecolor.js @@ -18,6 +18,7 @@ 'use strict'; Seriously.plugin('falsecolor', { + commonShader: true, shader: function (inputs, shaderSource) { shaderSource.fragment = '#ifdef GL_ES\n\n' + 'precision mediump float;\n\n' + diff --git a/effects/seriously.filmgrain.js b/effects/seriously.filmgrain.js index 58e8f25..1acaf83 100644 --- a/effects/seriously.filmgrain.js +++ b/effects/seriously.filmgrain.js @@ -29,6 +29,7 @@ Modified to preserve alpha 'use strict'; Seriously.plugin('filmgrain', { + commonShader: true, shader: function (inputs, shaderSource) { shaderSource.fragment = [ '#ifdef GL_ES', diff --git a/effects/seriously.hex.js b/effects/seriously.hex.js index 373af4c..ff132e5 100644 --- a/effects/seriously.hex.js +++ b/effects/seriously.hex.js @@ -24,6 +24,7 @@ */ Seriously.plugin('hex', { + commonShader: true, shader: function (inputs, shaderSource) { shaderSource.fragment = '#ifdef GL_ES\n\n' + 'precision mediump float;\n\n' + diff --git a/effects/seriously.highlights-shadows.js b/effects/seriously.highlights-shadows.js index e48edee..5afa48c 100644 --- a/effects/seriously.highlights-shadows.js +++ b/effects/seriously.highlights-shadows.js @@ -18,6 +18,7 @@ 'use strict'; Seriously.plugin('highlights-shadows', { + commonShader: true, shader: function (inputs, shaderSource) { shaderSource.fragment = '#ifdef GL_ES\n\n' + 'precision mediump float;\n\n' + diff --git a/effects/seriously.hue-saturation.js b/effects/seriously.hue-saturation.js index 377b7f6..d60c861 100644 --- a/effects/seriously.hue-saturation.js +++ b/effects/seriously.hue-saturation.js @@ -20,6 +20,7 @@ //inspired by Evan Wallace (https://github.com/evanw/glfx.js) Seriously.plugin('hue-saturation', { + commonShader: true, shader: function (inputs, shaderSource) { shaderSource.vertex = [ '#ifdef GL_ES', diff --git a/effects/seriously.invert.js b/effects/seriously.invert.js index 87b7efc..e49818d 100644 --- a/effects/seriously.invert.js +++ b/effects/seriously.invert.js @@ -18,6 +18,7 @@ 'use strict'; Seriously.plugin('invert', { + commonShader: true, shader: function (inputs, shaderSource) { shaderSource.fragment = '#ifdef GL_ES\n\n' + 'precision mediump float;\n\n' + diff --git a/effects/seriously.kaleidoscope.js b/effects/seriously.kaleidoscope.js index 96e3a80..843fe1b 100644 --- a/effects/seriously.kaleidoscope.js +++ b/effects/seriously.kaleidoscope.js @@ -18,6 +18,7 @@ 'use strict'; Seriously.plugin('kaleidoscope', { + commonShader: true, shader: function (inputs, shaderSource) { shaderSource.fragment = [ '#ifdef GL_ES', diff --git a/effects/seriously.layers.js b/effects/seriously.layers.js index ba19c7a..6e43f46 100644 --- a/effects/seriously.layers.js +++ b/effects/seriously.layers.js @@ -149,6 +149,7 @@ }; return { + commonShader: true, shader: function (inputs, shaderSource) { shaderSource.vertex = [ 'precision mediump float;', diff --git a/effects/seriously.linear-transfer.js b/effects/seriously.linear-transfer.js index cf6b8fd..d036ac8 100644 --- a/effects/seriously.linear-transfer.js +++ b/effects/seriously.linear-transfer.js @@ -18,6 +18,7 @@ 'use strict'; Seriously.plugin('linear-transfer', { + commonShader: true, shader: function (inputs, shaderSource) { shaderSource.fragment = [ '#ifdef GL_ES\n', diff --git a/effects/seriously.lumakey.js b/effects/seriously.lumakey.js index e123352..0e020be 100644 --- a/effects/seriously.lumakey.js +++ b/effects/seriously.lumakey.js @@ -18,6 +18,7 @@ 'use strict'; Seriously.plugin('lumakey', { + commonShader: true, shader: function (inputs, shaderSource) { shaderSource.fragment = '#ifdef GL_ES\n\n' + 'precision mediump float;\n\n' + diff --git a/effects/seriously.nightvision.js b/effects/seriously.nightvision.js index c0043e8..1161c35 100644 --- a/effects/seriously.nightvision.js +++ b/effects/seriously.nightvision.js @@ -21,6 +21,7 @@ //todo: make noise better? Seriously.plugin('nightvision', { + commonShader: true, shader: function (inputs, shaderSource, utilities) { shaderSource.fragment = '#ifdef GL_ES\n\n' + 'precision mediump float;\n\n' + diff --git a/effects/seriously.repeat.js b/effects/seriously.repeat.js index f53bf21..d4144b7 100644 --- a/effects/seriously.repeat.js +++ b/effects/seriously.repeat.js @@ -101,6 +101,7 @@ initialize(); this.uniforms.transform = transform; }, + commonShader: true, shader: function (inputs, shaderSource) { shaderSource.vertex = [ 'precision mediump float;', diff --git a/effects/seriously.ripple.js b/effects/seriously.ripple.js index 8b3fff8..65bf4e7 100644 --- a/effects/seriously.ripple.js +++ b/effects/seriously.ripple.js @@ -19,6 +19,7 @@ //http://msdn.microsoft.com/en-us/library/bb313868(v=xnagamestudio.10).aspx Seriously.plugin('ripple', { + commonShader: true, shader: function (inputs, shaderSource) { shaderSource.fragment = '#ifdef GL_ES\n\n' + 'precision mediump float;\n\n' + diff --git a/effects/seriously.scanlines.js b/effects/seriously.scanlines.js index d0477fc..a02c984 100644 --- a/effects/seriously.scanlines.js +++ b/effects/seriously.scanlines.js @@ -18,6 +18,7 @@ 'use strict'; Seriously.plugin('scanlines', { + commonShader: true, shader: function (inputs, shaderSource) { shaderSource.fragment = '#ifdef GL_ES\n\n' + 'precision mediump float;\n\n' + diff --git a/effects/seriously.sepia.js b/effects/seriously.sepia.js index eaf72f2..324057e 100644 --- a/effects/seriously.sepia.js +++ b/effects/seriously.sepia.js @@ -21,6 +21,7 @@ // http://www.techrepublic.com/blog/howdoi/how-do-i-convert-images-to-grayscale-and-sepia-tone-using-c/120 Seriously.plugin('sepia', { + commonShader: true, shader: function (inputs, shaderSource) { shaderSource.fragment = '#ifdef GL_ES\n\n' + 'precision mediump float;\n\n' + diff --git a/effects/seriously.sketch.js b/effects/seriously.sketch.js index 3c15f91..223838f 100644 --- a/effects/seriously.sketch.js +++ b/effects/seriously.sketch.js @@ -20,6 +20,7 @@ /* inspired by http://lab.adjazent.com/2009/01/09/more-pixel-bender/ */ Seriously.plugin('sketch', { + commonShader: true, shader: function (inputs, shaderSource) { shaderSource.fragment = '#ifdef GL_ES\n\n' + 'precision mediump float;\n\n' + diff --git a/effects/seriously.split.js b/effects/seriously.split.js index fc7567a..dcb2602 100644 --- a/effects/seriously.split.js +++ b/effects/seriously.split.js @@ -94,10 +94,10 @@ initialize(); this.uniforms.resolutionA = resolutionA; this.uniforms.resolutionB = resolutionB; + baseShader = this.baseShader; }, + commonShader: true, shader: function (inputs, shaderSource) { - baseShader = new Seriously.util.ShaderProgram(this.gl, shaderSource.vertex, shaderSource.fragment); - shaderSource.vertex = [ '#ifdef GL_ES', 'precision mediump float;', diff --git a/effects/seriously.tone.js b/effects/seriously.tone.js index 94c4deb..6b4ad34 100644 --- a/effects/seriously.tone.js +++ b/effects/seriously.tone.js @@ -18,6 +18,7 @@ 'use strict'; Seriously.plugin('tone', { + commonShader: true, shader: function (inputs, shaderSource) { shaderSource.fragment = '#ifdef GL_ES\n\n' + 'precision mediump float;\n\n' + diff --git a/effects/seriously.tvglitch.js b/effects/seriously.tvglitch.js index 9a12ccd..bb560fa 100644 --- a/effects/seriously.tvglitch.js +++ b/effects/seriously.tvglitch.js @@ -98,8 +98,9 @@ particleFrameBuffer = new Seriously.util.FrameBuffer(gl, 1, this.height / 2); parent(); }, + commonShader: true, shader: function (inputs, shaderSource) { - //baseShader = new Seriously.util.ShaderProgram(this.gl, shaderSource.vertex, shaderSource.fragment); + //baseShader = this.baseShader; shaderSource.fragment = '#ifdef GL_ES\n\n' + 'precision mediump float;\n\n' + diff --git a/effects/seriously.vignette.js b/effects/seriously.vignette.js index 3ee6692..159d4fd 100644 --- a/effects/seriously.vignette.js +++ b/effects/seriously.vignette.js @@ -18,6 +18,7 @@ 'use strict'; Seriously.plugin('vignette', { + commonShader: true, shader: function (inputs, shaderSource) { shaderSource.fragment = '#ifdef GL_ES\n\n' + 'precision mediump float;\n\n' + diff --git a/effects/seriously.whitebalance.js b/effects/seriously.whitebalance.js index a874d10..02c33e6 100644 --- a/effects/seriously.whitebalance.js +++ b/effects/seriously.whitebalance.js @@ -119,7 +119,7 @@ }, shader: function (inputs, shaderSource) { var auto = inputs.auto; - //baseShader = new Seriously.util.ShaderProgram(this.gl, shaderSource.vertex, shaderSource.fragment); + //baseShader = this.baseShader; //todo: gl.getExtension('OES_texture_float_linear') if (auto && !pyramidShader) { diff --git a/seriously.js b/seriously.js index 8c11355..4977a23 100644 --- a/seriously.js +++ b/seriously.js @@ -885,6 +885,7 @@ glCanvas, gl, rectangleModel, + commonShaders = {}, baseShader, baseVertexShader, baseFragmentShader, Node, SourceNode, EffectNode, TransformNode, TargetNode, @@ -960,12 +961,9 @@ for (i = 0; i < effects.length; i++) { node = effects[i]; - node.gl = gl; - - if (node.initialized) { - node.buildShader(); - } + node.initialize(); + node.buildShader(); } for (i = 0; i < sources.length; i++) { @@ -1636,7 +1634,12 @@ } if (gl) { - this.buildShader(); + this.initialize(); + if (this.effect.commonShader) { + //this effect is unlikely to need to be modified again + //by changing parameters + this.buildShader(); + } } this.inPlace = this.effect.inPlace; @@ -1654,6 +1657,8 @@ if (!this.initialized) { var that = this; + this.baseShader = baseShader; + if (this.shape) { this.model = makeGlModel(this.shape, this.gl); } else { @@ -1729,7 +1734,15 @@ EffectNode.prototype.buildShader = function () { var shader, effect = this.effect; if (this.shaderDirty) { - if (effect.shader) { + if (effect.commonShader && commonShaders[this.hook]) { + if (!this.shader) { + commonShaders[this.hook].count++; + } + this.shader = commonShaders[this.hook].shader; + } else if (effect.shader) { + if (this.shader && !effect.commonShader) { + this.shader.destroy(); + } shader = effect.shader.call(this, this.inputs, { vertex: baseVertexShader, fragment: baseFragmentShader @@ -1742,6 +1755,13 @@ } else { this.shader = baseShader; } + + if (effect.commonShader) { + commonShaders[this.hook] = { + count: 1, + shader: this.shader + }; + } } else { this.shader = baseShader; } @@ -2297,7 +2317,13 @@ delete this.effect; //shader - if (this.shader && this.shader.destroy && this.shader !== baseShader) { + if (commonShaders[hook]) { + commonShaders[hook].count--; + if (!commonShaders[hook].count) { + delete commonShaders[hook]; + } + } + if (this.shader && this.shader.destroy && this.shader !== baseShader && !commonShaders[hook]) { this.shader.destroy(); } delete this.shader; From ed10a20913d0540fd9343f8bdd98ff8cf846a9de Mon Sep 17 00:00:00 2001 From: Brian Chirls Date: Fri, 6 Dec 2013 17:29:48 +0900 Subject: [PATCH 03/45] First pass at Expression effect --- effects/seriously.expression.js | 462 ++++++++++++++++++++++++++++++++ 1 file changed, 462 insertions(+) create mode 100644 effects/seriously.expression.js diff --git a/effects/seriously.expression.js b/effects/seriously.expression.js new file mode 100644 index 0000000..7f19a07 --- /dev/null +++ b/effects/seriously.expression.js @@ -0,0 +1,462 @@ +/* global define, require */ +(function (root, factory) { + 'use strict'; + + if (typeof exports === 'object') { + // Node/CommonJS + factory(require('seriously'), require('jsep')); + } else if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module. + define(['seriously', 'jsep'], factory); + } else { + if (!root.Seriously) { + root.Seriously = { plugin: function (name, opt) { this[name] = opt; } }; + } + factory(root.Seriously, root.jsep); + } +}(this, function (Seriously, jsep, undefined) { + 'use strict'; + + function formatFloat(n) { + if (n - Math.floor(n) === 0) { + return n + '.0'; + } + return n; + } + + var symbols = { + red: 'rgba.r', + blue: 'rgba.b', + green: 'rgba.g', + alpha: 'rgba.a', + x: 'dim.x', + y: 'dim.y', + width: 'resolution.x', + height: 'resolution.y', + a: 'a', + b: 'b', + c: 'c', + d: 'd', + luma: 'luma' //todo: multiple dependencies + + /* + todo: + - time of day in seconds + - video time + - year, month, day + - datetime in milliseconds + - transform? + - transformed position? + */ + }, + definitions = { + and: [ + 'float and(float a, float b) {', + ' if (a == 0.0) {', + ' return 0.0;', + ' }', + ' return b;', + '}' + ].join('\n'), + or: [ + 'float or(float a, float b) {', + ' if (a != 0.0) {', + ' return a;', + ' }', + ' return b;', + '}' + ].join('\n') + }, + declarations = { + dim: 'vec2 dim = vTexCoord * resolution;', + rgba: 'vec4 rgba = texture2D(source, vTexCoord);', + luma: 'float luma = dot(rgba, vec3(0.2125,0.7154,0.0721));' + }, + functions = { + radians: 1, + degrees: 1, + sin: 1, + cos: 1, + tan: 1, + asin: 1, + acos: 1, + atan: 1, + //atan2: 2, //todo: define this + pow: 2, + exp: 1, + log: 1, + exp2: 1, + log2: 1, + sqrt: 1, + inversesqrt: 1, + abs: 1, + sign: 1, + floor: 1, + ceil: 1, + fract: 1, + mod: 2, + min: 2, + max: 2, + clamp: 3, + mix: 3, + step: 2, + smoothstep: 3, + + //custom logic functions + and: 2, + or: 2 + + /* + todo: + noise, random, hue, sat, lightness, hslRed, hslGreen, hslBlue, + int, sinh, cosh, tanh, mantissa, hypot, lerp, step + noise with multiple octaves (See fBm) + */ + }, + unaryOps = { + '-': true, + '!': true, + //'~': false, //todo: implement this or just get rid of it? + '+': true + }, + binaryOps = { + //true means it's a comparison and needs to be converted to float + '+': false, + '-': false, + '*': false, + '/': false, + '%': 'mod', + '&&': 'and', + '||': 'or', + //'^^': false, //todo: implement xor? + //'&', + //'|', + //'<<', + //'>>', + '===': '==', + '==': true, + '!==': '!=', + '!=': true, + '>=': true, + '<=': true, + '<': true, + '>': true + }, + objRegex = /(\w+)(\.\w+)?/; + + ['E', 'LN2', 'LN10', 'LOG2E', 'LOG10E', 'PI', 'SQRT1_2', 'SQRT2'].forEach(function (key) { + symbols[key] = key; + declarations[key] = 'const float ' + key + ' = ' + Math[key] + ';'; + }); + + Seriously.plugin('expression', function () { + var me = this; + + function updateSingle() { + var inputs = me.inputs; + + if (inputs.blue === inputs.red && + inputs.green === inputs.blue) { + inputs.rgb = inputs.red; + } else { + inputs.rgb = ''; + } + } + + function resize() { + if (me.inputs.source) { + me.width = me.inputs.width = me.inputs.source.width; + me.height = me.inputs.height = me.inputs.source.height; + } + } + + return { + shader: function (inputs, shaderSource) { + var expressions = {}, + channels = { + red: '', + green: '', + blue: '', + alpha: '' + }, + dependencies = {}, + deps, + expr, + key, + statements, + cs = [], + tree; + + function makeExpression(tree) { + var verb, x, i, + args; + /* + COMPOUND = 'Compound' + */ + + //We do not have any objects to offer + if (tree.type === 'MemberExpression') { + throw new Error('Expression Error: Unknown object "' + (tree.object.name || 'this') + '"'); + } + + if (tree.type === 'BinaryExpression' || tree.type === 'LogicalExpression') { + if (!tree.right) { + throw new Error('Expression Error: Bad binary expression'); + } + + //jsep seems to parse some unary expressions as binary with missing left side + //todo: consider removing this if/when jsep fixes it. file a github issue + if (!tree.left) { + tree.type = 'UnaryExpression'; + tree.argument = tree.right; + return makeExpression(tree); + } + + verb = tree.operator; + x = binaryOps[verb]; + if (x === undefined) { + throw new Error('Expression Error: Unknown operator "' + verb + '"'); + } + + if (typeof x === 'string') { + if (x in binaryOps) { + verb = binaryOps[x]; + x = binaryOps[verb]; + } else if (functions[x] === 2) { + deps[x] = true; + return x + '(' + makeExpression(tree.left) + ', ' + makeExpression(tree.right) + ')'; + } + } + + return (x ? 'float' : '') + '(' + makeExpression(tree.left) + ' ' + verb + ' ' + makeExpression(tree.right) + ')'; + } + + if (tree.type === 'CallExpression') { + if (tree.callee.type !== 'Identifier') { + throw new Error('Expression Error: Unknown function'); + } + + verb = tree.callee.name; + x = functions[verb]; + if (x === undefined) { + throw new Error('Expression Error: Unknown function "' + verb + '"'); + } + + if (x > tree.arguments.length) { + throw new Error('Expression Error: Function "' + verb + '" requires at least ' + x + ' arguments'); + } + + args = []; + for (i = 0; i < x; i++) { + args.push(makeExpression(tree.arguments[i])); + } + deps[verb] = true; + return verb + '(' + args.join(', ') + ')'; + } + + if (tree.type === 'Identifier') { + x = symbols[tree.name]; + if (!x) { + throw new Error('Expression Error: Unknown identifier "' + tree.name + '"'); + } + + args = objRegex.exec(x); + if (args && declarations[args[1]]) { + deps[args[1]] = true; + } + return x; + } + + if (tree.type === 'Literal') { + if (tree.raw === 'true') { + return 1.0; + } + + if (tree.raw === 'true') { + return 0.0; + } + + if (typeof tree.value !== 'number' || isNaN(tree.value)) { + throw new Error('Expression Error: Invalid literal ' + tree.raw); + } + + return formatFloat(tree.value); + } + + if (tree.type === 'UnaryExpression') { + verb = tree.operator; + x = unaryOps[verb]; + if (!x) { + throw new Error('Expression Error: Unknown operator "' + verb + '"'); + } + + //todo: are there any unary operators that could become functions? + return verb + '(' + makeExpression(tree.argument) + ')'; + } + } + + for (key in channels) { + if (channels.hasOwnProperty(key)) { + expr = inputs[key] || key; + channels[key] = expr; + expressions[expr] = ''; + } + } + + for (expr in expressions) { + if (expressions.hasOwnProperty(expr)) { + try { + deps = {}; + tree = jsep(expr); + //todo: convert this to a function? + expressions[expr] = makeExpression(tree); + + //flag any required declarations/precalculations + for (key in deps) { + if (deps.hasOwnProperty(key)) { + dependencies[key] = deps[key]; + } + } + + //special case for luma. todo: generalize if we need to + if (deps.luma) { + dependencies.rgba = true; + } + } catch (parseError) { + console.log(parseError.message); + expressions[expr] = '0.0'; + } + } + } + + statements = [ + 'precision mediump float;', + 'varying vec2 vTexCoord;', + 'varying vec4 vPosition;', + + 'uniform sampler2D source;', + 'uniform float a, b, c, d;', + 'uniform vec2 resolution;', + ]; + + for (key in dependencies) { + if (dependencies.hasOwnProperty(key)) { + if (definitions[key]) { + statements.push(definitions[key]); + } + } + } + + statements.push('void main(void) {'); + + for (key in dependencies) { + if (dependencies.hasOwnProperty(key)) { + if (declarations[key]) { + statements.push('\t' + declarations[key]); + } + } + } + + /* + todo: assign duplicate expressions to temp variables + for (expr in expressions) { + if (expressions.hasOwnProperty(expr)) { + statements.push('float val' + index = ) + } + } + */ + + for (key in channels) { + if (channels.hasOwnProperty(key)) { + expr = channels[key]; + cs.push(expressions[expr]); + } + } + + statements.push( + '\tgl_FragColor = vec4(', + '\t\t' + cs.join(',\n\t\t'), + '\t);', + '}' + ); + + shaderSource.fragment = statements.join('\n'); + + return shaderSource; + }, + inputs: { + source: { + type: 'image', + uniform: 'source', + update: resize, + shaderDirty: true + }, + a: { + type: 'number', + uniform: 'a', + defaultValue: 0 + }, + b: { + type: 'number', + uniform: 'b', + defaultValue: 0 + }, + c: { + type: 'number', + uniform: 'c', + defaultValue: 0 + }, + d: { + type: 'number', + uniform: 'd', + defaultValue: 0 + }, + rgb: { + type: 'string', + update: function (val) { + var inputs = me.inputs; + inputs.red = inputs.green = inputs.blue = val; + }, + shaderDirty: true + }, + red: { + type: 'string', + update: updateSingle, + shaderDirty: true + }, + green: { + type: 'string', + update: updateSingle, + shaderDirty: true + }, + blue: { + type: 'string', + update: updateSingle, + shaderDirty: true + }, + alpha: { + type: 'string', + shaderDirty: true + }, + width: { + type: 'number', + min: 0, + step: 1, + update: resize, + defaultValue: 0 + }, + height: { + type: 'number', + min: 0, + step: 1, + update: resize, + defaultValue: 0 + } + } + }; + }, + { + inPlace: false, + title: 'Expression' + }); +})); From e0962cec355785a993c311ea4afbe8e1ba23dcc3 Mon Sep 17 00:00:00 2001 From: Brian Chirls Date: Fri, 6 Dec 2013 17:41:00 +0900 Subject: [PATCH 04/45] Include jsep right in Expressions code for now. jsep doesn't support loading via AMD, and it's too much to ask everybody to figure that out. --- effects/seriously.expression.js | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/effects/seriously.expression.js b/effects/seriously.expression.js index 7f19a07..1a0272d 100644 --- a/effects/seriously.expression.js +++ b/effects/seriously.expression.js @@ -4,15 +4,15 @@ if (typeof exports === 'object') { // Node/CommonJS - factory(require('seriously'), require('jsep')); + factory(require('seriously')); } else if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module. - define(['seriously', 'jsep'], factory); + define(['seriously'], factory); } else { if (!root.Seriously) { root.Seriously = { plugin: function (name, opt) { this[name] = opt; } }; } - factory(root.Seriously, root.jsep); + factory(root.Seriously); } }(this, function (Seriously, jsep, undefined) { 'use strict'; @@ -142,7 +142,9 @@ '<': true, '>': true }, - objRegex = /(\w+)(\.\w+)?/; + objRegex = /(\w+)(\.\w+)?/, + + jsep; ['E', 'LN2', 'LN10', 'LOG2E', 'LOG10E', 'PI', 'SQRT1_2', 'SQRT2'].forEach(function (key) { symbols[key] = key; @@ -459,4 +461,9 @@ inPlace: false, title: 'Expression' }); + + /*! jsep - v0.2.1 - 2013-12-06 5:34:35 PM */ + !function(a){"use strict";var b=["-","!","~","+"],c=["+","-","*","/","%","&&","||","&","|","<<",">>","===","==","!==","!=",">=","<=","<",">"],d=c.length,e={"true":!0,"false":!1,"null":null},f="this",g=function(a){switch(a){case"||":return 1;case"&&":return 2;case"|":return 3;case"^":return 4;case"&":return 5;case"==":case"!=":case"===":case"!==":return 6;case"<":case">":case"<=":case">=":return 7;case"<<":case">>":case">>>":return 8;case"+":case"-":return 9;case"*":case"/":case"%":return 11}return 0},h=function(a,b,c){var d="||"===a||"&&"===a?s:r;return{type:d,operator:a,left:b,right:c}},i=function(a){return a>=48&&57>=a},j=function(a){return 36===a||95===a||a>=65&&90>=a||a>=97&&122>=a||a>=48&&57>=a},k="Compound",l="Identifier",m="MemberExpression",n="Literal",o="ThisExpression",p="CallExpression",q="UnaryExpression",r="BinaryExpression",s="LogicalExpression",t=(new RegExp("^['\"]"),new RegExp("^(\\d+(\\.\\d+)?)"),function(a){var r=0,s=a.length,t=function(){for(var b,c,d=[];s>r;)if(b=a[r],";"===b||","===b)r++;else if(c=v())d.push(c);else if(s>r)throw new Error("Unexpected '"+a[r]+"' at character "+r);return 1===d.length?d[0]:{type:k,body:d}},u=function(){var b,e,f,g;x();a:for(e=0;d>e;e++){for(b=c[e],g=b.length,f=0;g>f;f++)if(b[f]!==a[r+f])continue a;return r+=g,b}return!1},v=function(){var a,b,c,d,e,f,i,j;if(f=w(),b=u(),c=g(b),0===c)return f;if(e={value:b,prec:c},i=w(),!i)throw new Error("Expected expression after "+b+" at character "+r);for(d=[f,e,i];(b=u())&&(c=g(b),0!==c);){for(e={value:b,prec:c};d.length>2&&c<=d[d.length-2].prec;)i=d.pop(),b=d.pop().value,f=d.pop(),a=h(b,f,i),d.push(a);if(a=w(),!a)throw new Error("Expected expression after "+b+" at character "+r);d.push(e),d.push(a)}for(j=d.length-1,a=d[j];j>1;)a=h(d[j-1].value,d[j-2],a),j-=2;return a},w=function(){var c,d;return x(),c=a.charCodeAt(r),i(c)||46===c?y():39===c||34===c?z():j(c)?C():(d=b.indexOf(c))>=0?(r++,{type:q,operator:b[d],argument:w(),prefix:!0}):40===c?(r++,D()):!1},x=function(){for(var b=a.charCodeAt(r);32===b||9===b;)b=a.charCodeAt(++r)},y=function(){for(var b="";i(a.charCodeAt(r));)b+=a[r++];if("."===a[r])for(b+=a[r++];i(a.charCodeAt(r));)b+=a[r++];return{type:n,value:parseFloat(b),raw:b}},z=function(){for(var b,c="",d=a[r++],e=!1;s>r;){if(b=a[r++],b===d){e=!0;break}if("\\"===b)switch(b=a[r++]){case"n":c+="\n";break;case"r":c+="\r";break;case"t":c+=" ";break;case"b":c+="\b";break;case"f":c+="\f";break;case"v":c+=" "}else c+=b}if(!e)throw new Error('Unclosed quote after "'+c+'"');return{type:n,value:c,raw:d+c+d}},A=function(){for(var b,c,d=r;s>r&&(b=a.charCodeAt(r),j(b));)r++;return c=a.slice(d,r),e.hasOwnProperty(c)?{type:n,value:e[c],raw:c}:c===f?{type:o}:{type:l,name:c}},B=function(){for(var b,c,d=[];s>r;){if(x(),b=a[r],")"===b){r++;break}if(","===b)r++;else{if(c=v(),!c||c.type===k)throw new Error("Expected comma at character "+r);d.push(c)}}return d},C=function(){var b,c,d;for(c=A(),b=a[r];"."===b||"["===b||"("===b;){if("."===b)r++,c={type:m,computed:!1,object:c,property:A()};else if("["===b){if(d=r,r++,c={type:m,computed:!0,object:c,property:v()},x(),b=a[r],"]"!==b)throw new Error("Unclosed [ at character "+r);r++}else"("===b&&(r++,c={type:p,arguments:B(),callee:c});b=a[r]}return c},D=function(){var b=v();if(x(),")"===a[r])return r++,b;throw new Error("Unclosed ( at character "+r)};return t()});if(t.version="0.2.1","undefined"!=typeof exports)"undefined"!=typeof module&&module.exports&&(exports=module.exports=t),exports.do_parse=t;else{var u=a.jsep;a.jsep=t,t.noConflict=function(){var b=a.jsep;return a.jsep=u,b}}}(this || window); + + jsep = window.jsep.noConflict(); })); From 6cc697f7e86c67e2bc7e73227f54d2e04b0f3c51 Mon Sep 17 00:00:00 2001 From: Brian Chirls Date: Sun, 8 Dec 2013 21:44:09 +0900 Subject: [PATCH 05/45] Transform node inputs now can watch form elements --- examples/transform/reformat.html | 5 +- seriously.js | 112 +++++++++++++++++++++++++++++-- 2 files changed, 109 insertions(+), 8 deletions(-) diff --git a/examples/transform/reformat.html b/examples/transform/reformat.html index 1e65595..e94dee5 100644 --- a/examples/transform/reformat.html +++ b/examples/transform/reformat.html @@ -45,10 +45,7 @@

Target Canvas

reformat.width = 480; reformat.height = 270; //reformat.mode = '#mode'; - reformat.mode = 'contain'; - document.getElementById('mode').addEventListener('change', function () { - reformat.mode = this.value; - }, false); + reformat.mode = '#mode'; // connect all our nodes in the right order reformat.source = '#source'; diff --git a/seriously.js b/seriously.js index 4977a23..4ba5a49 100644 --- a/seriously.js +++ b/seriously.js @@ -3257,6 +3257,110 @@ self = this, key; + function setInput(inputName, def, input) { + var key, lookup, value; + + lookup = me.inputElements[inputName]; + + //todo: there is some duplicate code with Effect here. Consolidate. + if (typeof input === 'string' && isNaN(input)) { + if (def.type === 'enum') { + if (def.options && def.options.filter) { + key = String(input).toLowerCase(); + + //todo: possible memory leak on this function? + value = def.options.filter(function (e) { + return (typeof e === 'string' && e.toLowerCase() === key) || + (e.length && typeof e[0] === 'string' && e[0].toLowerCase() === key); + }); + + value = value.length; + } + + if (!value) { + input = getElement(input, ['select']); + } + + } else if (def.type === 'number' || def.type === 'boolean') { + input = getElement(input, ['input', 'select']); + } else if (def.type === 'image') { + input = getElement(input, ['canvas', 'img', 'video']); + } + } + + if (input instanceof HTMLInputElement || input instanceof HTMLSelectElement) { + value = input.value; + + if (lookup && lookup.element !== input) { + lookup.element.removeEventListener('change', lookup.listener, true); + lookup.element.removeEventListener('input', lookup.listener, true); + delete me.inputElements[inputName]; + lookup = null; + } + + if (!lookup) { + lookup = { + element: input, + listener: (function (name, element) { + return function () { + var oldValue, newValue; + + if (input.type === 'checkbox') { + //special case for check box + oldValue = input.checked; + } else { + oldValue = element.value; + } + + if (def.set.call(me, oldValue)) { + me.setTransformDirty(); + } + + newValue = def.get.call(me); + + //special case for color type + /* + no colors on transform nodes just yet. maybe later + if (def.type === 'color') { + newValue = arrayToHex(newValue); + } + */ + + //if input validator changes our value, update HTML Element + //todo: make this optional...somehow + if (newValue !== oldValue) { + element.value = newValue; + } + }; + }(inputName, input)) + }; + + me.inputElements[inputName] = lookup; + if (input.type === 'range') { + input.addEventListener('input', lookup.listener, true); + input.addEventListener('change', lookup.listener, true); + } else { + input.addEventListener('change', lookup.listener, true); + } + } + + if (lookup && value.type === 'checkbox') { + value = value.checked; + } + } else { + if (lookup) { + lookup.element.removeEventListener('change', lookup.listener, true); + lookup.element.removeEventListener('input', lookup.listener, true); + delete me.inputElements[inputName]; + } + value = input; + } + + if (def.set.call(me, value)) { + me.setTransformDirty(); + } + } + function setProperty(name, def) { // todo: validate value passed to 'set' Object.defineProperty(self, name, { @@ -3266,9 +3370,7 @@ return def.get.call(me); }, set: function (val) { - if (def.set.call(me, val)) { - me.setTransformDirty(); - } + setInput(name, def, val); } }); } @@ -3281,6 +3383,8 @@ }; } + this.inputElements = {}; + //priveleged accessor methods Object.defineProperties(this, { id: { @@ -4616,7 +4720,7 @@ } //todo: fmod 360deg or Math.PI * 2 radians - rotation = angle; + rotation = parseFloat(angle); recompute(); return true; From d83bd7f43f866eba6e887524d038cedb22d78329 Mon Sep 17 00:00:00 2001 From: Brian Chirls Date: Mon, 9 Dec 2013 08:55:02 -0500 Subject: [PATCH 06/45] New feature: Source type plugins. ImageData and Array(typed and untyped) moved into separate files --- seriously.js | 148 ++++++++++++++++++--------------- sources/seriously.array.js | 87 +++++++++++++++++++ sources/seriously.imagedata.js | 51 ++++++++++++ test/index.html | 2 + 4 files changed, 222 insertions(+), 66 deletions(-) create mode 100644 sources/seriously.array.js create mode 100644 sources/seriously.imagedata.js diff --git a/seriously.js b/seriously.js index 4ba5a49..9fc530c 100644 --- a/seriously.js +++ b/seriously.js @@ -35,9 +35,11 @@ incompatibility, seriousEffects = {}, seriousTransforms = {}, + seriousSources = {}, timeouts = [], allEffectsByHook = {}, allTransformsByHook = {}, + allSourcesByHook = {}, identity, nop = function () {}, @@ -2453,7 +2455,9 @@ height = opts.height, deferTexture = false, that = this, - matchedType = false; + matchedType = false, + key, + plugin; Node.call(this); @@ -2503,43 +2507,6 @@ throw 'Not a valid HTML element: ' + source.tagName + ' (must be img, video or canvas)'; } matchedType = true; - - } else if (source instanceof Object && source.data && - source.width && source.height && - source.width * source.height * 4 === source.data.length - ) { - - //Because of this bug, Firefox doesn't recognize ImageData, so we have to duck type - //https://bugzilla.mozilla.org/show_bug.cgi?id=637077 - - this.width = source.width; - this.height = source.height; - matchedType = true; - - this.render = this.renderImageCanvas; - } else if (isArrayLike(source)) { - if (!width || !height) { - throw 'Height and width must be provided with an Array'; - } - - if (width * height * 4 !== source.length) { - throw 'Array length must be height x width x 4.'; - } - - this.width = width; - this.height = height; - - matchedType = true; - - //use opposite default for flip - if (opts.flip === undefined) { - flip = false; - } - - if (!(source instanceof Uint8Array)) { - source = new Uint8Array(source); - } - this.render = this.renderTypedArray; } else if (source instanceof WebGLTexture) { if (gl && !gl.isTexture(source)) { throw 'Not a valid WebGL texture.'; @@ -2570,6 +2537,22 @@ //todo: if WebGLTexture source is from a different context render it and copy it over this.render = function () {}; + } else { + for (key in seriousSources) { + if (seriousSources.hasOwnProperty(key)) { + plugin = seriousSources[key].definition.call(this, source, options); + if (plugin) { + matchedType = true; + deferTexture = plugin.deferTexture; + this.plugin = plugin; + if (plugin.source) { + source = plugin.source; + } + + break; + } + } + } } if (!matchedType) { @@ -2577,7 +2560,9 @@ } this.source = source; - this.flip = flip; + if (this.flip === undefined) { + this.flip = flip; + } this.targets = []; @@ -2665,6 +2650,27 @@ } }; + SourceNode.prototype.render = function () { + var media = this.source; + + if (!gl || !media) { + return; + } + + if (!this.initialized) { + this.initialize(); + } + + if (!this.allowRefresh) { + return; + } + + if (this.plugin && this.plugin.render && + this.plugin.render.call(this, gl)) { + this.dirty = false; + } + }; + SourceNode.prototype.renderVideo = function () { var video = this.source; @@ -2737,36 +2743,13 @@ } }; - SourceNode.prototype.renderTypedArray = function () { - var media = this.source; - - if (!gl || !media || !media.length) { - return; - } - - if (!this.initialized) { - this.initialize(); - } - - //this.currentTime = media.currentTime || 0; - - if (!this.allowRefresh) { - return; - } - - if (this.dirty) { - gl.bindTexture(gl.TEXTURE_2D, this.texture); - gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, this.flip); - gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, this.width, this.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, media); - - this.lastRenderTime = Date.now() / 1000; - this.dirty = false; - } - }; - SourceNode.prototype.destroy = function () { var i, key, item; + if (this.plugin && this.plugin.destroy) { + this.plugin.destroy.call(this); + } + if (this.gl && this.texture) { this.gl.deleteTexture(this.texture); } @@ -4135,6 +4118,39 @@ return this; }; + Seriously.source = function (hook, definition, meta) { + var source; + + if (seriousSources[hook]) { + console.log('Source [' + hook + '] already loaded'); + return; + } + + if (meta === undefined && typeof definition === 'object') { + meta = definition; + } + + if (!meta && !definition) { + return; + } + + source = extend({}, meta); + + if (typeof definition === 'function') { + source.definition = definition; + } + + if (!source.title) { + source.title = hook; + } + + + seriousSources[hook] = source; + allSourcesByHook[hook] = []; + + return source; + }; + Seriously.transform = function (hook, definition, meta) { var transform; diff --git a/sources/seriously.array.js b/sources/seriously.array.js new file mode 100644 index 0000000..13a670c --- /dev/null +++ b/sources/seriously.array.js @@ -0,0 +1,87 @@ +/* global define, require */ +(function (root, factory) { + 'use strict'; + + if (typeof exports === 'object') { + // Node/CommonJS + factory(require('seriously')); + } else if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module. + define(['seriously'], factory); + } else { + /* + todo: build out-of-order loading for sources and transforms or remove this + if (!root.Seriously) { + root.Seriously = { plugin: function (name, opt) { this[name] = opt; } }; + } + */ + factory(root.Seriously); + } +}(this, function (Seriously, undefined) { + 'use strict'; + + Seriously.source('array', function (source, options, force) { + var width, + height, + typedArray; + + if (options && (Array.isArray(source) || + (source && source.BYTES_PER_ELEMENT && 'length' in source))) { + + width = options.width; + height = options.height; + + if (!width || !height) { + if (force) { + throw 'Height and width must be provided with an Array'; + } + return; + } + + if (width * height * 4 !== source.length) { + if (force) { + throw 'Array length must be height x width x 4.'; + } + return; + } + + this.width = width; + this.height = height; + + //use opposite default for flip + if (options.flip === undefined) { + this.flip = false; + } + + if (!(source instanceof Uint8Array)) { + typedArray = new Uint8Array(source); + } + + return { + render: function (gl) { + var i; + if (this.dirty) { + //pixel array can be updated, but we need to load from the typed array + //todo: see if there's a faster copy method + if (typedArray) { + for (i = 0; i < typedArray.length; i++) { + typedArray[i] = source[i]; + } + } + + gl.bindTexture(gl.TEXTURE_2D, this.texture); + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, this.flip); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, this.width, this.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, typedArray || source); + + this.lastRenderTime = Date.now() / 1000; + + return true; + } + } + }; + } + }, { + title: 'Array', + description: 'Array or Uint8Array' + }); +})); \ No newline at end of file diff --git a/sources/seriously.imagedata.js b/sources/seriously.imagedata.js new file mode 100644 index 0000000..2ffb5e6 --- /dev/null +++ b/sources/seriously.imagedata.js @@ -0,0 +1,51 @@ +/* global define, require */ +(function (root, factory) { + 'use strict'; + + if (typeof exports === 'object') { + // Node/CommonJS + factory(require('seriously')); + } else if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module. + define(['seriously'], factory); + } else { + /* + todo: build out-of-order loading for sources and transforms or remove this + if (!root.Seriously) { + root.Seriously = { plugin: function (name, opt) { this[name] = opt; } }; + } + */ + factory(root.Seriously); + } +}(this, function (Seriously, undefined) { + 'use strict'; + + Seriously.source('imagedata', function (source) { + if (source instanceof Object && source.data && + source.width && source.height && + source.width * source.height * 4 === source.data.length + ) { + + //Because of this bug, Firefox doesn't recognize ImageData, so we have to duck type + //https://bugzilla.mozilla.org/show_bug.cgi?id=637077 + + this.width = source.width; + this.height = source.height; + + return { + render: function (gl) { + if (this.dirty) { + gl.bindTexture(gl.TEXTURE_2D, this.texture); + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, this.flip); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, source); + this.lastRenderTime = Date.now() / 1000; + return true; + } + } + }; + } + }, { + title: 'ImageData', + description: '2D Canvas ImageData' + }); +})); \ No newline at end of file diff --git a/test/index.html b/test/index.html index 9258f1e..b290329 100644 --- a/test/index.html +++ b/test/index.html @@ -7,6 +7,8 @@ + + + + +
+ +
+ + + + + From f6f72509260f5a0451f9adbca6dd9a0dd5c80633 Mon Sep 17 00:00:00 2001 From: Brian Chirls Date: Fri, 13 Dec 2013 09:43:10 -0500 Subject: [PATCH 23/45] Added checkerboard generator effect --- effects/seriously.checkerboard.js | 141 ++++++++++++++++++++++++++++++ index.html | 1 + 2 files changed, 142 insertions(+) create mode 100644 effects/seriously.checkerboard.js diff --git a/effects/seriously.checkerboard.js b/effects/seriously.checkerboard.js new file mode 100644 index 0000000..258c1b7 --- /dev/null +++ b/effects/seriously.checkerboard.js @@ -0,0 +1,141 @@ +/* global define, require */ +(function (root, factory) { + 'use strict'; + + if (typeof exports === 'object') { + // Node/CommonJS + factory(require('seriously')); + } else if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module. + define(['seriously'], factory); + } else { + if (!root.Seriously) { + root.Seriously = { plugin: function (name, opt) { this[name] = opt; } }; + } + factory(root.Seriously); + } +}(this, function (Seriously, undefined) { + 'use strict'; + + Seriously.plugin('checkerboard', function () { + var me = this; + + function resize() { + me.resize(); + } + + return { + initialize: function (initialize) { + initialize(); + resize(); + }, + shader: function (inputs, shaderSource) { + shaderSource.vertex = [ + '#ifdef GL_ES', + 'precision mediump float;', + '#endif', + + 'attribute vec4 position;', + 'attribute vec2 texCoord;', + + 'uniform vec2 resolution;', + 'uniform mat4 transform;', + + 'uniform vec2 size;', + 'uniform vec2 anchor;', + + 'vec2 pixelCoord;', //based in center + 'varying vec2 vGridCoord;', //based in center + + 'void main(void) {', + // first convert to screen space + ' vec4 screenPosition = vec4(position.xy * resolution / 2.0, position.z, position.w);', + ' screenPosition = transform * screenPosition;', + + // convert back to OpenGL coords + ' gl_Position.xy = screenPosition.xy * 2.0 / resolution;', + ' gl_Position.z = screenPosition.z * 2.0 / (resolution.x / resolution.y);', + ' gl_Position.w = screenPosition.w;', + + ' pixelCoord = resolution * (texCoord - 0.5) / 2.0;', + ' vGridCoord = (pixelCoord - anchor) / size;', + '}\n' + ].join('\n'); + shaderSource.fragment = [ + '#ifdef GL_ES', + 'precision mediump float;', + '#endif', + + 'varying vec2 vTexCoord;', + 'varying vec2 vPixelCoord;', + 'varying vec2 vGridCoord;', + + 'uniform vec2 resolution;', + 'uniform vec2 anchor;', + 'uniform vec2 size;', + 'uniform vec4 color1;', + 'uniform vec4 color2;', + + + 'void main(void) {', + ' vec2 modGridCoord = floor(mod(vGridCoord, 2.0));', + ' if (modGridCoord.x == modGridCoord.y) {', + ' gl_FragColor = color1;', + ' } else {', + ' gl_FragColor = color2;', + ' }', + '}' + ].join('\n'); + return shaderSource; + }, + inPlace: true, + inputs: { + source: { + type: 'image', + uniform: 'source', + shaderDirty: false + }, + anchor: { + type: 'vector', + uniform: 'anchor', + dimensions: 2, + defaultValue: [0, 0] + }, + size: { + type: 'vector', + uniform: 'size', + dimensions: 2, + defaultValue: [4, 4] + }, + color1: { + type: 'color', + uniform: 'color1', + defaultValue: [1, 1, 1, 1] + }, + color2: { + type: 'color', + uniform: 'color2', + defaultValue: [187 / 255, 187 / 255, 187 / 255, 1] + }, + width: { + type: 'number', + min: 0, + step: 1, + update: resize, + defaultValue: 640 + }, + height: { + type: 'number', + min: 0, + step: 1, + update: resize, + defaultValue: 360 + } + }, + }; + }, { + commonShader: true, + title: 'Checkerboard', + categories: ['generator'] + }); +})); diff --git a/index.html b/index.html index 65c99d6..399dd45 100644 --- a/index.html +++ b/index.html @@ -10,6 +10,7 @@ + From 31620e4001e56c18e7e40ec3848f5ff0c55d30fe Mon Sep 17 00:00:00 2001 From: Brian Chirls Date: Sun, 15 Dec 2013 13:11:25 -0500 Subject: [PATCH 24/45] Added generators category to color effect --- effects/seriously.color.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/effects/seriously.color.js b/effects/seriously.color.js index 65f8714..c0396ed 100644 --- a/effects/seriously.color.js +++ b/effects/seriously.color.js @@ -43,6 +43,7 @@ } }, title: 'Color', - description: 'Generate color' + description: 'Generate color', + categories: ['generator'] }); })); From 7dc01b10b2767bc6a96ef6b3b2ddcde65fae9941 Mon Sep 17 00:00:00 2001 From: Brian Chirls Date: Sun, 15 Dec 2013 13:11:40 -0500 Subject: [PATCH 25/45] Added more functions to Expression effect --- effects/seriously.expression.js | 206 +++++++++++++++++++++++--------- 1 file changed, 150 insertions(+), 56 deletions(-) diff --git a/effects/seriously.expression.js b/effects/seriously.expression.js index 1a0272d..87f6795 100644 --- a/effects/seriously.expression.js +++ b/effects/seriously.expression.js @@ -14,7 +14,7 @@ } factory(root.Seriously); } -}(this, function (Seriously, jsep, undefined) { +}(this, function (Seriously, undefined) { 'use strict'; function formatFloat(n) { @@ -37,7 +37,7 @@ b: 'b', c: 'c', d: 'd', - luma: 'luma' //todo: multiple dependencies + luma: ['luma', 'rgba'] /* todo: @@ -50,29 +50,85 @@ */ }, definitions = { - and: [ - 'float and(float a, float b) {', - ' if (a == 0.0) {', - ' return 0.0;', - ' }', - ' return b;', - '}' - ].join('\n'), - or: [ - 'float or(float a, float b) {', - ' if (a != 0.0) {', - ' return a;', - ' }', - ' return b;', - '}' - ].join('\n') - }, - declarations = { dim: 'vec2 dim = vTexCoord * resolution;', rgba: 'vec4 rgba = texture2D(source, vTexCoord);', - luma: 'float luma = dot(rgba, vec3(0.2125,0.7154,0.0721));' + luma: 'float luma = dot(rgba.rgb, vec3(0.2125,0.7154,0.0721));', + atan2: { + source: '#define atan2(x, y) atan(x / y)', + global: true + }, + and: { + source: [ + 'float and(float a, float b) {', + ' if (a == 0.0) {', + ' return 0.0;', + ' }', + ' return b;', + '}' + ].join('\n'), + global: true + }, + or: { + source: [ + 'float or(float a, float b) {', + ' if (a != 0.0) {', + ' return a;', + ' }', + ' return b;', + '}' + ].join('\n'), + global: true + }, + luminance: { + source: [ + 'const vec3 lumaCoeffs = vec3(0.2125,0.7154,0.0721);', + 'float luminance(float r, float g, float b) {', + ' return dot(vec3(r, g, b), vec3(0.2125,0.7154,0.0721));', + '}' + ].join('\n'), + global: true + }, + saturation: { + source: [ + 'float saturation(float r, float g, float b) {', + ' float lo = min(r, min(g, b));', + ' float hi = max(r, max(g, b));', + ' float l = (lo + hi) / 2.0;', + ' float d = hi - lo;', + ' return l > 0.5 ? d / (2.0 - hi - lo) : d / (hi + lo);', + '}' + ].join('\n'), + global: true + }, + lightness: { + source: [ + 'float lightness(float r, float g, float b) {', + ' float lo = min(r, min(g, b));', + ' float hi = max(r, max(g, b));', + ' return (lo + hi) / 2.0;', + '}' + ].join('\n'), + global: true + }, + hue: { + source: [ + 'float hue(float r, float g, float b) {', + ' float h;', + ' if (r > g && r > b) {', //red is max + ' h = (g - b) / d + (g < b ? 6.0 : 0.0);', + ' } else if (g > r && g > b) {', //green is max + ' h = (b - r) / d + 2.0;', + ' } else {', //blue is max + ' h = (r - g) / d + 4.0;', + ' }', + ' return h / 6.0;', + '}' + ].join('\n'), + global: true + } }, functions = { + //built-in shader functions radians: 1, degrees: 1, sin: 1, @@ -81,7 +137,6 @@ asin: 1, acos: 1, atan: 1, - //atan2: 2, //todo: define this pow: 2, exp: 1, log: 1, @@ -104,11 +159,18 @@ //custom logic functions and: 2, - or: 2 + or: 2, + + //custom functions + atan2: 2, + hue: 3, + saturation: 3, + lightness: 3, + luminance: 3 /* todo: - noise, random, hue, sat, lightness, hslRed, hslGreen, hslBlue, + noise, random, hslRed, hslGreen, hslBlue, int, sinh, cosh, tanh, mantissa, hypot, lerp, step noise with multiple octaves (See fBm) */ @@ -142,15 +204,48 @@ '<': true, '>': true }, - objRegex = /(\w+)(\.\w+)?/, + pair, + key, + def, jsep; ['E', 'LN2', 'LN10', 'LOG2E', 'LOG10E', 'PI', 'SQRT1_2', 'SQRT2'].forEach(function (key) { symbols[key] = key; - declarations[key] = 'const float ' + key + ' = ' + Math[key] + ';'; + definitions[key] = { + source: 'const float ' + key + ' = ' + Math[key] + ';', + global: true + }; }); + //clean up lookup tables + for (key in symbols) { + if (symbols.hasOwnProperty(key)) { + def = symbols[key]; + if (typeof def === 'string') { + def = [def]; + } + + pair = def[0].split('.'); + if (pair.length > 1) { + def.push(pair[0]); + } + symbols[key] = def; + } + } + + for (key in definitions) { + if (definitions.hasOwnProperty(key)) { + def = definitions[key]; + if (typeof def === 'string') { + definitions[key] = { + source: def, + global: false + }; + } + } + } + Seriously.plugin('expression', function () { var me = this; @@ -186,6 +281,8 @@ expr, key, statements, + globalDefinitions = [], + nonGlobalDefinitions = [], cs = [], tree; @@ -257,16 +354,18 @@ } if (tree.type === 'Identifier') { - x = symbols[tree.name]; - if (!x) { + args = symbols[tree.name]; + if (!args) { throw new Error('Expression Error: Unknown identifier "' + tree.name + '"'); } - args = objRegex.exec(x); - if (args && declarations[args[1]]) { - deps[args[1]] = true; + for (i = args.length - 1; i >= 0; i--) { + x = args[i]; + if (definitions[x]) { + deps[x] = true; + } } - return x; + return args[0]; } if (tree.type === 'Literal') { @@ -331,30 +430,15 @@ } } - statements = [ - 'precision mediump float;', - 'varying vec2 vTexCoord;', - 'varying vec4 vPosition;', - - 'uniform sampler2D source;', - 'uniform float a, b, c, d;', - 'uniform vec2 resolution;', - ]; - - for (key in dependencies) { - if (dependencies.hasOwnProperty(key)) { - if (definitions[key]) { - statements.push(definitions[key]); - } - } - } - - statements.push('void main(void) {'); - for (key in dependencies) { if (dependencies.hasOwnProperty(key)) { - if (declarations[key]) { - statements.push('\t' + declarations[key]); + deps = definitions[key]; + if (deps) { + if (deps.global) { + globalDefinitions.push(deps.source); + } else { + nonGlobalDefinitions.push('\t' + deps.source); + } } } } @@ -375,12 +459,22 @@ } } - statements.push( + statements = [ + 'precision mediump float;', + 'varying vec2 vTexCoord;', + 'varying vec4 vPosition;', + + 'uniform sampler2D source;', + 'uniform float a, b, c, d;', + 'uniform vec2 resolution;', + globalDefinitions.join('\n'), + 'void main(void) {', + nonGlobalDefinitions.join('\n'), '\tgl_FragColor = vec4(', '\t\t' + cs.join(',\n\t\t'), '\t);', '}' - ); + ]; shaderSource.fragment = statements.join('\n'); From 57b5057a9d483307c1f3bfe8781055bcd6fcca8f Mon Sep 17 00:00:00 2001 From: Brian Chirls Date: Thu, 26 Dec 2013 21:16:12 -0500 Subject: [PATCH 26/45] Each instance of Seriously now has an id --- seriously.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/seriously.js b/seriously.js index 4f1be0b..b4e8c4e 100644 --- a/seriously.js +++ b/seriously.js @@ -41,6 +41,7 @@ allTransformsByHook = {}, allSourcesByHook = {}, identity, + maxSeriouslyId = 0, nop = function () {}, /* @@ -874,7 +875,8 @@ } //initialize object, private properties - var seriously = this, + var id = ++maxSeriouslyId, + seriously = this, nodes = [], nodesById = {}, nodeId = 0, @@ -4010,6 +4012,16 @@ return false; }; + Object.defineProperties(this, { + id: { + enumerable: true, + configurable: true, + get: function () { + return id; + } + } + }); + //todo: load, save, find baseVertexShader = [ From 5e35d83035df301428e1b2609de177091ef8b5bb Mon Sep 17 00:00:00 2001 From: Brian Chirls Date: Thu, 26 Dec 2013 21:17:39 -0500 Subject: [PATCH 27/45] Node dimensions must be integer. Float values are rounded down --- effects/seriously.repeat.js | 3 +++ seriously.js | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/effects/seriously.repeat.js b/effects/seriously.repeat.js index d4144b7..6c9b6fd 100644 --- a/effects/seriously.repeat.js +++ b/effects/seriously.repeat.js @@ -70,6 +70,9 @@ } } + width = Math.floor(width); + height = Math.floor(height); + if (source) { this.uniforms.resolution[0] = source.width; this.uniforms.resolution[1] = source.height; diff --git a/seriously.js b/seriously.js index b4e8c4e..f5b569d 100644 --- a/seriously.js +++ b/seriously.js @@ -1299,6 +1299,9 @@ height = 1; } + width = Math.floor(width); + height = Math.floor(height); + if (this.width !== width || this.height !== height) { this.width = width; this.height = height; @@ -5190,6 +5193,7 @@ width: { get: getWidth, set: function (x) { + x = Math.floor(x); if (x === forceWidth) { return false; } @@ -5206,6 +5210,7 @@ height: { get: getHeight, set: function (y) { + y = Math.floor(y); if (y === forceHeight) { return false; } From 5bbf30f410af338f86a5f2f7a09bfbe9a4819509 Mon Sep 17 00:00:00 2001 From: Brian Chirls Date: Fri, 3 Jan 2014 17:55:55 -0500 Subject: [PATCH 28/45] Add black to pre-computed color names that chrome doesn't provide --- seriously.js | 1 + 1 file changed, 1 insertion(+) diff --git a/seriously.js b/seriously.js index f5b569d..61f2441 100644 --- a/seriously.js +++ b/seriously.js @@ -51,6 +51,7 @@ // http://www.w3.org/TR/css3-color/#svg-color colorNames = { transparent: [0, 0, 0, 0], + black: [0, 0, 0, 1], red: [1, 0, 0, 1], green: [0, 1, 0, 1], blue: [0, 0, 1, 1], From 0683c7ef6c3347fbdfd96e518929a477abb20c27 Mon Sep 17 00:00:00 2001 From: Brian Chirls Date: Fri, 3 Jan 2014 17:57:18 -0500 Subject: [PATCH 29/45] Better loading when mixing AMD and non-AMD environment --- seriously.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/seriously.js b/seriously.js index 61f2441..2c337a1 100644 --- a/seriously.js +++ b/seriously.js @@ -16,7 +16,7 @@ } return Seriously; }); - } else { + } else if (typeof root.Seriously !== 'function') { // Browser globals root.Seriously = factory(root); } @@ -4585,9 +4585,6 @@ } } }()); - } else { - //seriously has already been loaded, so don't replace it - return; } } From e331f9d0589be9df541405a54a96a12d609bab4e Mon Sep 17 00:00:00 2001 From: Brian Chirls Date: Fri, 3 Jan 2014 17:58:08 -0500 Subject: [PATCH 30/45] Fix clean-up of source node texture in destroy --- seriously.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/seriously.js b/seriously.js index 2c337a1..b578ca7 100644 --- a/seriously.js +++ b/seriously.js @@ -2800,8 +2800,8 @@ this.plugin.destroy.call(this); } - if (this.gl && this.texture) { - this.gl.deleteTexture(this.texture); + if (gl && this.texture) { + gl.deleteTexture(this.texture); } //targets @@ -3281,6 +3281,8 @@ targets.splice(i, 1); } + //todo: if this.gl === gl, clear out context so we can start over + Node.prototype.destroy.call(this); }; From d13c9b01a63e39f7eaf66a07cef635bb278a4d91 Mon Sep 17 00:00:00 2001 From: Brian Chirls Date: Fri, 3 Jan 2014 17:59:01 -0500 Subject: [PATCH 31/45] Enable resize callback for transform plugins --- seriously.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/seriously.js b/seriously.js index b578ca7..b93f034 100644 --- a/seriously.js +++ b/seriously.js @@ -3586,6 +3586,10 @@ Node.prototype.resize.call(this); + if (this.plugin.resize) { + this.plugin.resize.call(this); + } + for (i = 0; i < this.targets.length; i++) { this.targets[i].resize(); } From 2a5137bf769a38c8c57665c6a027bf995408fee6 Mon Sep 17 00:00:00 2001 From: Brian Chirls Date: Fri, 3 Jan 2014 18:00:44 -0500 Subject: [PATCH 32/45] Bug fix: ASCII effect resizing was broken when loaded with bad source dimensions --- effects/seriously.ascii.js | 50 +++++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/effects/seriously.ascii.js b/effects/seriously.ascii.js index 1bb6f71..abb95fa 100644 --- a/effects/seriously.ascii.js +++ b/effects/seriously.ascii.js @@ -26,7 +26,7 @@ var identity, letters; letters = document.createElement('img'); - letters.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAvAAAAAICAYAAACf+MsnAAAFY0lEQVR4Xu2Z644bOwyDN+//0NsOigEMQdRHyU6CFDnA+bHVWNaFojiTx8/Pz+/f/4/89/v7z9Xj8Tjib3XyTN9usFcMz8gt3h9zXf/O6nD/W1V7Vb9uXad+nHucZ9xenX7OqTHdSfmRXfmPsSn8xPMrllcfCkdVfHSe7Ned0/yp7jv2GPfqK+MCByc0zzvxKi5RPq8cuvE4+JrwpFM7N78K2yu+qb9kd3qV+ZjUx5n/+xnXP81ctW/UHQ5P3Gd360vxKf+n8dGpxXTeKu6h2ansFT6pvo5G2/FP99NsUf9d/xleInfetcj629m9cf9WOV5K+78R8ERGRLYO8VQiecd/1vwKEJV46JBJRzhRfXftVL/MTgM48UmL0l2OSmzs9kctAJfE4/1KkNFzbj8cjFHsJ/u460vhnPDfqddujJ27poLCWWBuHt0YKr/ki+yOKJnk5Z7pPLfLf4TZif+qvi7XuDWg+HbtNEe79ds9H7m1m2/3+YzLK5Hc9e/gYxdfNP+ZfdV9lT3usWn+9310/qiAdxa1O5gTEqVhoLudxVwVNPrvCqDp/ZX4d0Uk1Y7sbgyU4zooCk8nB3i9Y61V5wWpIjDlP+ZJsxPvmLxEOD2sntk5Pz1LBOb0L+sPfQGs6ksYpt7QAiHuUwtkgl+F3Qyf2YxTX53+Vdjfjc8VYIq7KT+abzof7ervZ8fX8d/Jyc3PmTcnRrrPEbyVTnD8T+Y38pH624mfNIr6muzO95S/sh1Gvog/XmW/a6N+scww43zgqLjcOX9cwFeESQK3Gpx32QggTlwk8Ei8OXfE4VMLeCLQiLBjfJM7VA069XefnZBGJz7Vr24dK3GwEoqLD7p/1+4IMWdRdxaMK9CmP4E62F7nm8S7s4B3BMCkBzQPVQ0IM06+2WLvzlDlI+NfF4d0ljiHuF/Zb/4m/4ojTgnA6f0qfiWA135P5l/NoFv/7txm+5ZyyOw0e1R/skd8ZKKwwnjXf9xLrkBV+2x3Pib9Vz3JOMaNL/KZ+oCkXhDUTLxEwLsC41OfI5DEYe9+mXfr0l2mJH5ISHTOUw2U8IjD5LyVUtxEmrvi4V5ejvijWNWicBbOyfsrYejkMMXmdIFEAZH19ASWnNyrPlBdKH+yU3y0gGjGKf4Mv51ft9zzKk83vul5qr9r7+CT9gHx2zvs0/yofpGX1AuC4svqhYJeJJydNZk/urcSxet91dfiUy94HX6oBHCHi5+F38svCeg1h+zZ6nyF5VUzVC8Q0X9LwE/IkMjmpJ3i27XvxuqQ0c4dp/JTfnb9T847AoNIW/nokIYrYKvnJvln/siPwtD0XAeTU+x0luEugWdLNeY4ecl260vxK8Efl3OnZi4uaZZIMBFeJ/hw6xrFvppvV1Q559d8MwwR50cskIBQ2KhE3y7/ZeddAUjxOr3diZ/8U3+I953z7uzR7Lj4rvjl9HxXvaHaOflSfSkf93y24xx94PpX89I5H2t9+fwK+KVzNOwdIeM+e905+ZqqRIj7pYHiU3FNFnBnkO+41EKige3cpX7GunwoARfjIwKrxNhEJFLfMrsbI+G/smfkojAa60vxPcNeCZCqhjSra6ydBaAWSFzaqnb01c4VEdVCWWPM7svstKDWuKrZpwUb7dVsOzPcxUeGdYdfdgV8Vr+Mv1R8Tn/iHcSNWR8jjjv9URzama9qbp0XlBP4y2Jw6u/E577AZTVz/BM/OfySzSjl79o73FRxaFdfuPG5/XE58PbXEvAT8UBn1HKuSIB8ThYwiZfJnd8z768Aib/3R/iN4J0VeMXcVwvynbl/735OBV6BKTfyT+e/T4/f7dP3uW8F3Aqs/PIHbWXeeeKjnSsAAAAASUVORK5CYII="; + letters.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAvAAAAAICAYAAACf+MsnAAAFY0lEQVR4Xu2Z644bOwyDN+//0NsOigEMQdRHyU6CFDnA+bHVWNaFojiTx8/Pz+/f/4/89/v7z9Xj8Tjib3XyTN9usFcMz8gt3h9zXf/O6nD/W1V7Vb9uXad+nHucZ9xenX7OqTHdSfmRXfmPsSn8xPMrllcfCkdVfHSe7Ned0/yp7jv2GPfqK+MCByc0zzvxKi5RPq8cuvE4+JrwpFM7N78K2yu+qb9kd3qV+ZjUx5n/+xnXP81ctW/UHQ5P3Gd360vxKf+n8dGpxXTeKu6h2ansFT6pvo5G2/FP99NsUf9d/xleInfetcj629m9cf9WOV5K+78R8ERGRLYO8VQiecd/1vwKEJV46JBJRzhRfXftVL/MTgM48UmL0l2OSmzs9kctAJfE4/1KkNFzbj8cjFHsJ/u460vhnPDfqddujJ27poLCWWBuHt0YKr/ki+yOKJnk5Z7pPLfLf4TZif+qvi7XuDWg+HbtNEe79ds9H7m1m2/3+YzLK5Hc9e/gYxdfNP+ZfdV9lT3usWn+9310/qiAdxa1O5gTEqVhoLudxVwVNPrvCqDp/ZX4d0Uk1Y7sbgyU4zooCk8nB3i9Y61V5wWpIjDlP+ZJsxPvmLxEOD2sntk5Pz1LBOb0L+sPfQGs6ksYpt7QAiHuUwtkgl+F3Qyf2YxTX53+Vdjfjc8VYIq7KT+abzof7ervZ8fX8d/Jyc3PmTcnRrrPEbyVTnD8T+Y38pH624mfNIr6muzO95S/sh1Gvog/XmW/a6N+scww43zgqLjcOX9cwFeESQK3Gpx32QggTlwk8Ei8OXfE4VMLeCLQiLBjfJM7VA069XefnZBGJz7Vr24dK3GwEoqLD7p/1+4IMWdRdxaMK9CmP4E62F7nm8S7s4B3BMCkBzQPVQ0IM06+2WLvzlDlI+NfF4d0ljiHuF/Zb/4m/4ojTgnA6f0qfiWA135P5l/NoFv/7txm+5ZyyOw0e1R/skd8ZKKwwnjXf9xLrkBV+2x3Pib9Vz3JOMaNL/KZ+oCkXhDUTLxEwLsC41OfI5DEYe9+mXfr0l2mJH5ISHTOUw2U8IjD5LyVUtxEmrvi4V5ejvijWNWicBbOyfsrYejkMMXmdIFEAZH19ASWnNyrPlBdKH+yU3y0gGjGKf4Mv51ft9zzKk83vul5qr9r7+CT9gHx2zvs0/yofpGX1AuC4svqhYJeJJydNZk/urcSxet91dfiUy94HX6oBHCHi5+F38svCeg1h+zZ6nyF5VUzVC8Q0X9LwE/IkMjmpJ3i27XvxuqQ0c4dp/JTfnb9T847AoNIW/nokIYrYKvnJvln/siPwtD0XAeTU+x0luEugWdLNeY4ecl260vxK8Efl3OnZi4uaZZIMBFeJ/hw6xrFvppvV1Q559d8MwwR50cskIBQ2KhE3y7/ZeddAUjxOr3diZ/8U3+I953z7uzR7Lj4rvjl9HxXvaHaOflSfSkf93y24xx94PpX89I5H2t9+fwK+KVzNOwdIeM+e905+ZqqRIj7pYHiU3FNFnBnkO+41EKige3cpX7GunwoARfjIwKrxNhEJFLfMrsbI+G/smfkojAa60vxPcNeCZCqhjSra6ydBaAWSFzaqnb01c4VEdVCWWPM7svstKDWuKrZpwUb7dVsOzPcxUeGdYdfdgV8Vr+Mv1R8Tn/iHcSNWR8jjjv9URzama9qbp0XlBP4y2Jw6u/E577AZTVz/BM/OfySzSjl79o73FRxaFdfuPG5/XE58PbXEvAT8UBn1HKuSIB8ThYwiZfJnd8z768Aib/3R/iN4J0VeMXcVwvynbl/735OBV6BKTfyT+e/T4/f7dP3uW8F3Aqs/PIHbWXeeeKjnSsAAAAASUVORK5CYII='; identity = new Float32Array([ 1, 0, 0, 0, 0, 1, 0, 0, @@ -43,7 +43,24 @@ height, scaledWidth, scaledHeight, - unif; + unif = {}, + me = this; + + function resize() { + //set up scaledBuffer if (width or height have changed) + height = me.height; + width = me.width; + scaledWidth = Math.ceil(width / 8); + scaledHeight = Math.ceil(height / 8); + + unif.resolution = me.uniforms.resolution; + unif.transform = identity; + + if (scaledBuffer) { + scaledBuffer.resize(scaledWidth, scaledHeight); + } + } + return { initialize: function (parent) { function setLetters() { @@ -74,16 +91,20 @@ }); } - unif = { - letters: lettersTexture - }; + unif.letters = lettersTexture; //when the output scales up, don't smooth it out gl.bindTexture(gl.TEXTURE_2D, this.texture || this.frameBuffer && this.frameBuffer.texture); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); gl.bindTexture(gl.TEXTURE_2D, null); - scaledBuffer = new Seriously.util.FrameBuffer(gl, 1, 1); + resize(); + + scaledBuffer = new Seriously.util.FrameBuffer(gl, scaledWidth, scaledHeight); + + //so it stays blocky + gl.bindTexture(gl.TEXTURE_2D, scaledBuffer.texture); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); this.uniforms.transform = identity; @@ -121,22 +142,7 @@ return shaderSource; }, - resize: function () { - //set up scaledBuffer if (width or height have changed) - height = this.height; - width = this.width; - scaledWidth = Math.ceil(width / 8); - scaledHeight = Math.ceil(height / 8); - - unif.resolution = this.uniforms.resolution; - unif.transform = identity; - - scaledBuffer.resize(scaledWidth, scaledHeight); - - //so it stays blocky - gl.bindTexture(gl.TEXTURE_2D, scaledBuffer.texture); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); - }, + resize: resize, draw: function (shader, model, uniforms, frameBuffer, draw) { draw(baseShader, model, uniforms, scaledBuffer.frameBuffer, false, { width: scaledWidth, From 6d37bc030b4dc6676be6a0a108d734f9d119b4e9 Mon Sep 17 00:00:00 2001 From: Brian Chirls Date: Fri, 3 Jan 2014 18:01:13 -0500 Subject: [PATCH 33/45] Remove unused source uniform from simplex effect shader --- effects/seriously.simplex.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/effects/seriously.simplex.js b/effects/seriously.simplex.js index dd09e28..5ee4661 100644 --- a/effects/seriously.simplex.js +++ b/effects/seriously.simplex.js @@ -45,8 +45,6 @@ 'varying vec2 vTexCoord;\n' + 'varying vec4 vPosition;\n' + '\n' + - 'uniform sampler2D source;\n' + - '\n' + 'uniform float amount;\n' + 'uniform vec2 noiseScale;\n' + 'uniform vec2 noiseOffset;\n' + From 7072f5b37365dda8508a64f5a24403d70a6e78dd Mon Sep 17 00:00:00 2001 From: Brian Chirls Date: Fri, 3 Jan 2014 18:02:49 -0500 Subject: [PATCH 34/45] Start Unit tests for effect plugins (invert) --- test/index.html | 6 +++++ test/seriously.unit.js | 50 ++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/test/index.html b/test/index.html index b290329..aca2b97 100644 --- a/test/index.html +++ b/test/index.html @@ -9,6 +9,12 @@ + +