diff --git a/js/Tracker.js b/js/Tracker.js index 4c011c1d..e5c579d7 100644 --- a/js/Tracker.js +++ b/js/Tracker.js @@ -301,6 +301,7 @@ export default class Tracker extends EventEmitter { if ('lbp' in this.weights) this.lbpInit = true; if ('sobel' in this.weights) this.sobelInit = true; } catch (err) { + console.error(err); alert('There was a problem setting up webGL programs, falling back to slightly slower javascript version. :('); this.webglFi = undefined; this.svmFi = new SvmFilter(); diff --git a/js/svmfilter/svmfilter_webgl.js b/js/svmfilter/svmfilter_webgl.js index 280ac83e..db3c4cf0 100644 --- a/js/svmfilter/svmfilter_webgl.js +++ b/js/svmfilter/svmfilter_webgl.js @@ -1,4 +1,8 @@ -'use strict'; +import { + setupWebGL, + loadShader, + loadProgram +} from 'clmtrackr/js/utils/webgl'; import createLbpResponseVS from './shaders/lbpResponse.vert'; import createLbpResponseFS from './shaders/lbpResponse.frag'; @@ -142,8 +146,11 @@ var webglFilter = function () { canvas.setAttribute('style', 'display:none;'); // FIXME: dont litter the DOM with render canvases document.body.appendChild(canvas); - // TODO : isolate this library from webgl-util.js - gl = setupWebGL(canvas, {premultipliedAlpha: false, preserveDrawingBuffer : true, antialias : false}); + gl = setupWebGL(canvas, { + premultipliedAlpha: false, + preserveDrawingBuffer: true, + antialias: false + }); // check for float textures support and fail if not @@ -316,7 +323,7 @@ var webglFilter = function () { if ('sobel' in filters) { var grVertexShader = loadShader(gl, gradientResponseVS, gl.VERTEX_SHADER); var grFragmentShader = loadShader(gl, gradientResponseFS, gl.FRAGMENT_SHADER); - gradientResponseProgram = createProgram(gl, [grVertexShader, grFragmentShader]); + gradientResponseProgram = loadProgram(gl, [grVertexShader, grFragmentShader]); gl.useProgram(gradientResponseProgram); // set up vertices with rectangles @@ -341,7 +348,7 @@ var webglFilter = function () { if ('lbp' in filters) { var lbpVertexShader = loadShader(gl, lbpResponseVS, gl.VERTEX_SHADER); var lbpFragmentShader = loadShader(gl, lbpResponseFS, gl.FRAGMENT_SHADER); - lbpResponseProgram = createProgram(gl, [lbpVertexShader, lbpFragmentShader]); + lbpResponseProgram = loadProgram(gl, [lbpVertexShader, lbpFragmentShader]); gl.useProgram(lbpResponseProgram); // set up vertices with rectangles @@ -367,7 +374,7 @@ var webglFilter = function () { // setup patchdraw program var drVertexShader = loadShader(gl, drawResponsesVS, gl.VERTEX_SHADER); var drFragmentShader = loadShader(gl, drawResponsesFS, gl.FRAGMENT_SHADER); - patchDrawProgram = createProgram(gl, [drVertexShader, drFragmentShader]); + patchDrawProgram = loadProgram(gl, [drVertexShader, drFragmentShader]); gl.useProgram(patchDrawProgram); // set the resolution/dimension of the canvas @@ -381,7 +388,7 @@ var webglFilter = function () { // setup patchresponse program var prVertexShader = loadShader(gl, patchResponseVS, gl.VERTEX_SHADER); var prFragmentShader = loadShader(gl, patchResponseFS, gl.FRAGMENT_SHADER); - patchResponseProgram = createProgram(gl, [prVertexShader, prFragmentShader]); + patchResponseProgram = loadProgram(gl, [prVertexShader, prFragmentShader]); gl.useProgram(patchResponseProgram); // set up vertices with rectangles @@ -851,314 +858,5 @@ var webglFilter = function () { } }; -// The rest of the code is based on webgl-utils.js authored by Gregg Tavares, license below: -/* - * Copyright (c) 2011, Gregg Tavares - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * * Neither the name of greggman.com nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS - * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -(function() { - - /** - * Wrapped logging function. - * @param {string} msg The message to log. - */ - var log = function(msg) { - if (window.console && window.console.log) { - window.console.log(msg); - } - }; - - /** - * Wrapped logging function. - * @param {string} msg The message to log. - */ - var error = function(msg) { - if (window.console) { - if (window.console.error) { - window.console.error(msg); - } - else if (window.console.log) { - window.console.log(msg); - } - } - throw msg; - }; - - /** - * Turn off all logging. - */ - var loggingOff = function() { - log = function() {}; - error = function() {}; - }; - - /** - * Check if the page is embedded. - * @return {boolean} True of we are in an iframe - */ - var isInIFrame = function() { - return window != window.top; - }; - - /** - * Converts a WebGL enum to a string - * @param {!WebGLContext} gl The WebGLContext to use. - * @param {number} value The enum value. - * @return {string} The enum as a string. - */ - var glEnumToString = function(gl, value) { - for (var p in gl) { - if (gl[p] == value) { - return p; - } - } - return "0x" + value.toString(16); - }; - - /** - * Creates the HTLM for a failure message - * @param {string} canvasContainerId id of container of th - * canvas. - * @return {string} The html. - */ - var makeFailHTML = function(msg) { - return '' + - '' + - '
' + - '
' + - '
' + msg + '
' + - '
' + - '
'; - }; - - /** - * Mesasge for getting a webgl browser - * @type {string} - */ - var GET_A_WEBGL_BROWSER = '' + - 'This page requires a browser that supports WebGL.
' + - 'Click here to upgrade your browser.'; - - /** - * Mesasge for need better hardware - * @type {string} - */ - var OTHER_PROBLEM = '' + - "It doesn't appear your computer can support WebGL.
" + - 'Click here for more information.'; - - /** - * Creates a webgl context. If creation fails it will - * change the contents of the container of the - * tag to an error message with the correct links for WebGL. - * @param {Element} canvas. The canvas element to create a - * context from. - * @param {WebGLContextCreationAttirbutes} opt_attribs Any - * creation attributes you want to pass in. - * @return {WebGLRenderingContext} The created context. - */ - var setupWebGL = function(canvas, opt_attribs) { - function showLink(str) { - var container = canvas.parentNode; - if (container) { - container.innerHTML = makeFailHTML(str); - } - }; - - if (!window.WebGLRenderingContext) { - //showLink(GET_A_WEBGL_BROWSER); - return null; - } - - var context = create3DContext(canvas, opt_attribs); - if (!context) { - //showLink(OTHER_PROBLEM); - return null; - } - return context; - }; - - /** - * Creates a webgl context. - * @param {!Canvas} canvas The canvas tag to get context - * from. If one is not passed in one will be created. - * @return {!WebGLContext} The created context. - */ - var create3DContext = function(canvas, opt_attribs) { - var names = ["webgl", "experimental-webgl"]; - var context = null; - for (var ii = 0; ii < names.length; ++ii) { - try { - context = canvas.getContext(names[ii], opt_attribs); - } catch(e) {} - if (context) { - break; - } - } - return context; - } - - var updateCSSIfInIFrame = function() { - if (isInIFrame()) { - document.body.className = "iframe"; - } - }; - - /** - * Gets a WebGL context. - * makes its backing store the size it is displayed. - */ - var getWebGLContext = function(canvas) { - if (isInIFrame()) { - updateCSSIfInIFrame(); - - // make the canvas backing store the size it's displayed. - canvas.width = canvas.clientWidth; - canvas.height = canvas.clientHeight; - } - - var gl = setupWebGL(canvas); - return gl; - }; - - /** - * Loads a shader. - * @param {!WebGLContext} gl The WebGLContext to use. - * @param {string} shaderSource The shader source. - * @param {number} shaderType The type of shader. - * @param {function(string): void) opt_errorCallback callback for errors. - * @return {!WebGLShader} The created shader. - */ - var loadShader = function(gl, shaderSource, shaderType, opt_errorCallback) { - var errFn = opt_errorCallback || error; - // Create the shader object - var shader = gl.createShader(shaderType); - - // Load the shader source - gl.shaderSource(shader, shaderSource); - - // Compile the shader - gl.compileShader(shader); - - // Check the compile status - var compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS); - if (!compiled) { - // Something went wrong during compilation; get the error - var lastError = gl.getShaderInfoLog(shader); - errFn("*** Error compiling shader '" + shader + "':" + lastError); - gl.deleteShader(shader); - return null; - } - - return shader; - } - - /** - * Creates a program, attaches shaders, binds attrib locations, links the - * program and calls useProgram. - * @param {!Array.} shaders The shaders to attach - * @param {!Array.} opt_attribs The attribs names. - * @param {!Array.} opt_locations The locations for the attribs. - */ - var loadProgram = function(gl, shaders, opt_attribs, opt_locations) { - var program = gl.createProgram(); - for (var ii = 0; ii < shaders.length; ++ii) { - gl.attachShader(program, shaders[ii]); - } - if (opt_attribs) { - for (var ii = 0; ii < opt_attribs.length; ++ii) { - gl.bindAttribLocation( - program, - opt_locations ? opt_locations[ii] : ii, - opt_attribs[ii]); - } - } - gl.linkProgram(program); - - // Check the link status - var linked = gl.getProgramParameter(program, gl.LINK_STATUS); - if (!linked) { - // something went wrong with the link - var lastError = gl.getProgramInfoLog (program); - error("Error in program linking:" + lastError); - - gl.deleteProgram(program); - return null; - } - return program; - }; - - /** - * Loads a shader from a script tag. - * @param {!WebGLContext} gl The WebGLContext to use. - * @param {string} scriptId The id of the script tag. - * @param {number} opt_shaderType The type of shader. If not passed in it will - * be derived from the type of the script tag. - * @param {function(string): void) opt_errorCallback callback for errors. - * @return {!WebGLShader} The created shader. - */ - var createShaderFromScript = function( - gl, scriptId, opt_shaderType, opt_errorCallback) { - var shaderSource = ""; - var shaderType; - var shaderScript = document.getElementById(scriptId); - if (!shaderScript) { - throw("*** Error: unknown script element" + scriptId); - } - shaderSource = shaderScript.text; - - if (!opt_shaderType) { - if (shaderScript.type == "x-shader/x-vertex") { - shaderType = gl.VERTEX_SHADER; - } else if (shaderScript.type == "x-shader/x-fragment") { - shaderType = gl.FRAGMENT_SHADER; - } else if (shaderType != gl.VERTEX_SHADER && shaderType != gl.FRAGMENT_SHADER) { - throw("*** Error: unknown shader type"); - return null; - } - } - - return loadShader( - gl, shaderSource, opt_shaderType ? opt_shaderType : shaderType, - opt_errorCallback); - }; - - /* export functions */ - window.setupWebGL = setupWebGL; - window.createProgram = loadProgram; - window.createShaderFromScriptElement = createShaderFromScript; - window.getWebGLContext = getWebGLContext; - window.updateCSSIfInIFrame = updateCSSIfInIFrame; - window.loadShader = loadShader; - -}()); module.exports = webglFilter; diff --git a/js/utils/logging.js b/js/utils/logging.js new file mode 100644 index 00000000..7ba32c8e --- /dev/null +++ b/js/utils/logging.js @@ -0,0 +1,38 @@ +let LOGGING_ENABLED = true; + + +/** + * Wrapped logging function. + * @param {string} msg The message to log. + */ +export const log = function (msg) { + if (!LOGGING_ENABLED) { return; } + if (window.console && window.console.log) { + window.console.log(msg); + } +}; + + +/** + * Wrapped logging function. + * @param {string} msg The message to log. + */ +export const error = function (msg) { + if (!LOGGING_ENABLED) { return; } + if (window.console) { + if (window.console.error) { + window.console.error(msg); + } else if (window.console.log) { + window.console.log(msg); + } + } + throw msg; +}; + + +/** + * Turn off all logging. + */ +export const loggingOff = function () { + LOGGING_ENABLED = false; +}; diff --git a/js/utils/webgl.js b/js/utils/webgl.js new file mode 100644 index 00000000..11709809 --- /dev/null +++ b/js/utils/webgl.js @@ -0,0 +1,234 @@ +import { error } from './logging'; +import { isInIFrame, updateCSSIfInIFrame } from './window'; + +/** + * Converts a WebGL enum to a string + * @param {!WebGLContext} gl The WebGLContext to use. + * @param {number} value The enum value. + * @return {string} The enum as a string. + */ +export const glEnumToString = function (gl, value) { + for (var p in gl) { + if (gl[p] === value) { + return p; + } + } + return '0x' + value.toString(16); +}; + + +/** + * Creates the HTLM for a failure message + * @param {string} canvasContainerId id of container of th + * canvas. + * @return {string} The html. + */ +export const makeFailHTML = function (msg) { + return '' + + '' + + '
' + + '
' + + '
' + msg + '
' + + '
' + + '
'; +}; + + +/** + * Mesasge for getting a webgl browser + * @type {string} + */ +// const GET_A_WEBGL_BROWSER = '' + +// 'This page requires a browser that supports WebGL.
' + +// 'Click here to upgrade your browser.'; + + +/** + * Mesasge for need better hardware + * @type {string} + */ +// const OTHER_PROBLEM = '' + +// "It doesn't appear your computer can support WebGL.
" + +// 'Click here for more information.'; + + +/** + * Creates a webgl context. If creation fails it will + * change the contents of the container of the + * tag to an error message with the correct links for WebGL. + * @param {Element} canvas. The canvas element to create a + * context from. + * @param {WebGLContextCreationAttirbutes} optAttribs Any + * creation attributes you want to pass in. + * @return {WebGLRenderingContext} The created context. + */ +export const setupWebGL = function (canvas, optAttribs) { + // const showLink = function (str) { + // var container = canvas.parentNode; + // if (container) { + // container.innerHTML = makeFailHTML(str); + // } + // }; + + if (!window.WebGLRenderingContext) { + // showLink(GET_A_WEBGL_BROWSER); + return null; + } + + var context = create3DContext(canvas, optAttribs); + if (!context) { + // showLink(OTHER_PROBLEM); + return null; + } + return context; +}; + + +/** + * Creates a webgl context. + * @param {!Canvas} canvas The canvas tag to get context + * from. If one is not passed in one will be created. + * @return {!WebGLContext} The created context. + */ +export const create3DContext = function (canvas, optAttribs) { + var names = ['webgl', 'experimental-webgl']; + var context = null; + for (var ii = 0; ii < names.length; ++ii) { + try { + context = canvas.getContext(names[ii], optAttribs); + } catch (e) {} + if (context) { + break; + } + } + return context; +}; + + +/** + * Gets a WebGL context. + * makes its backing store the size it is displayed. + */ +export const getWebGLContext = function (canvas) { + if (isInIFrame()) { + updateCSSIfInIFrame(); + + // make the canvas backing store the size it's displayed. + canvas.width = canvas.clientWidth; + canvas.height = canvas.clientHeight; + } + + var gl = setupWebGL(canvas); + return gl; +}; + + +/** + * Loads a shader. + * @param {!WebGLContext} gl The WebGLContext to use. + * @param {string} shaderSource The shader source. + * @param {number} shaderType The type of shader. + * @param {function(string): void) optErrorCallback callback for errors. + * @return {!WebGLShader} The created shader. + */ +export const loadShader = function (gl, shaderSource, shaderType, optErrorCallback) { + var errFn = optErrorCallback || error; + // Create the shader object + var shader = gl.createShader(shaderType); + + // Load the shader source + gl.shaderSource(shader, shaderSource); + + // Compile the shader + gl.compileShader(shader); + + // Check the compile status + var compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS); + if (!compiled) { + // Something went wrong during compilation; get the error + var lastError = gl.getShaderInfoLog(shader); + errFn("*** Error compiling shader '" + shader + "':" + lastError); + gl.deleteShader(shader); + return null; + } + + return shader; +}; + + +/** + * Creates a program, attaches shaders, binds attrib locations, links the + * program and calls useProgram. + * @param {!Array.} shaders The shaders to attach + * @param {!Array.} optAttribs The attribs names. + * @param {!Array.} optLocations The locations for the attribs. + */ +export const loadProgram = function (gl, shaders, optAttribs, optLocations) { + var program = gl.createProgram(); + for (let i = 0; i < shaders.length; ++i) { + gl.attachShader(program, shaders[i]); + } + if (optAttribs) { + for (let i = 0; i < optAttribs.length; ++i) { + gl.bindAttribLocation( + program, + optLocations ? optLocations[i] : i, + optAttribs[i]); + } + } + gl.linkProgram(program); + + // Check the link status + const linked = gl.getProgramParameter(program, gl.LINK_STATUS); + if (!linked) { + // something went wrong with the link + const lastError = gl.getProgramInfoLog(program); + error('Error in program linking:' + lastError); + + gl.deleteProgram(program); + return null; + } + return program; +}; + + +/** + * Loads a shader from a script tag. + * @param {!WebGLContext} gl The WebGLContext to use. + * @param {string} scriptId The id of the script tag. + * @param {number} optShaderType The type of shader. If not passed in it will + * be derived from the type of the script tag. + * @param {function(string): void) optErrorCallback callback for errors. + * @return {!WebGLShader} The created shader. + */ +export const createShaderFromScript = function ( + gl, scriptId, optShaderType, optErrorCallback +) { + var shaderSource = ''; + var shaderType; + var shaderScript = document.getElementById(scriptId); + if (!shaderScript) { + throw new Error('*** Error: unknown script element' + scriptId); + } + shaderSource = shaderScript.text; + + if (!optShaderType) { + if (shaderScript.type === 'x-shader/x-vertex') { + shaderType = gl.VERTEX_SHADER; + } else if (shaderScript.type === 'x-shader/x-fragment') { + shaderType = gl.FRAGMENT_SHADER; + } else if ( + shaderType !== gl.VERTEX_SHADER && + shaderType !== gl.FRAGMENT_SHADER + ) { + throw new Error('*** Error: unknown shader type'); + } + } + + return loadShader( + gl, + shaderSource, + optShaderType || shaderType, + optErrorCallback + ); +}; diff --git a/js/utils/window.js b/js/utils/window.js new file mode 100644 index 00000000..2e516793 --- /dev/null +++ b/js/utils/window.js @@ -0,0 +1,14 @@ +/** + * Check if the page is embedded. + * @return {boolean} True of we are in an iframe + */ +export const isInIFrame = function () { + return window !== window.top; +}; + + +export const updateCSSIfInIFrame = function () { + if (isInIFrame()) { + document.body.className = 'iframe'; + } +};