UNPKG

gl-conformance

Version:
1,773 lines (1,627 loc) 69.1 kB
/*eslint-disable */ var _windowShim = require("../lib/shims/window-shim"); var _documentShim = require("../lib/shims/document-shim"); var _canvasShim = require("../lib/shims/canvas-shim"); var _imageShim = require("../lib/shims/image-shim"); var _rafShim = require("../lib/shims/raf-shim"); var _RESOURCES = require("./resources.json"); function more_functions_copyTexSubImage2DBadArgs(ENVIRONMENT) { var HTMLElement = function() {}; ENVIRONMENT.CONTEXT_LIST = []; ENVIRONMENT.tape.end = (function(tape_end) { return function() { _rafShim.clear(); ENVIRONMENT.CONTEXT_LIST.forEach(function(gl) { (gl.destroy && gl.destroy()); }); ENVIRONMENT.CONTEXT_LIST = []; tape_end.call(ENVIRONMENT.tape); } })(ENVIRONMENT.tape.end); ENVIRONMENT._createContext = ENVIRONMENT.createContext; ENVIRONMENT.createContext = function(w, h, o) { var gl = ENVIRONMENT._createContext(w, h, o); ENVIRONMENT.CONTEXT_LIST.push(gl); return gl; }; ENVIRONMENT.document = _documentShim(ENVIRONMENT); ENVIRONMENT.window = _windowShim(ENVIRONMENT); ENVIRONMENT.scriptList = {}; ENVIRONMENT.canvasList = [{ "id": "gl", "width": "16", "height": "16" }].map(function(opts) { return _canvasShim(ENVIRONMENT, opts); }); ENVIRONMENT.RESOURCES = _RESOURCES; ENVIRONMENT.BASEPATH = "more/functions"; var document = ENVIRONMENT.document; var window = ENVIRONMENT.window; var Image = _imageShim; var requestAnimationFrame = _rafShim.requestAnimationFrame; var cancelAnimationFrame = _rafShim.cancelAnimationFrame;; /* Unit testing library for the OpenGL ES 2.0 HTML Canvas context */ /* ** Copyright (c) 2012 The Khronos Group Inc. ** ** Permission is hereby granted, free of charge, to any person obtaining a ** copy of this software and/or associated documentation files (the ** "Materials"), to deal in the Materials without restriction, including ** without limitation the rights to use, copy, modify, merge, publish, ** distribute, sublicense, and/or sell copies of the Materials, and to ** permit persons to whom the Materials are furnished to do so, subject to ** the following conditions: ** ** The above copyright notice and this permission notice shall be included ** in all copies or substantial portions of the Materials. ** ** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. ** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY ** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, ** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE ** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. */ Tests = { autorun: true, message: null, delay: 0, startUnit: function() { return []; }, setup: function() { return arguments; }, teardown: function() {}, endUnit: function() {} } var CONSOLE = (1, eval)("console") var __testSuccess__ = true; var __testFailCount__ = 0; var __testLog__ = { appendChild: function() {} } var __backlog__ = []; function toSource(a, seen) { if (a == null) return "null"; if (typeof a == 'boolean') return a ? "true" : "false"; if (typeof a == 'string') return '"' + a.replace(/"/g, '\\"') + '"'; if (a instanceof HTMLElement) return a.toString(); if (a.width && a.height && a.data) return "[ImageData]"; if (a instanceof Array) { if (!seen) seen = []; var idx = seen.indexOf(a); if (idx != -1) return '#' + (idx + 1) + '#'; seen.unshift(a); var srcs = a.map(function(o) { return toSource(o, seen) }); var prefix = ''; idx = seen.indexOf(a); if (idx != -1) prefix = '#' + (idx + 1) + '='; return prefix + '[' + srcs.join(", ") + ']'; } if (typeof a == 'object') { if (!seen) seen = []; var idx = seen.indexOf(a); if (idx != -1) return '#' + (idx + 1) + '#'; seen.unshift(a); var members = []; var name; try { for (var i in a) { if (i.search(/^[a-zA-Z0-9]+$/) != -1) name = i; else name = '"' + i.replace(/"/g, '\\"') + '"'; var ai; try { ai = a[i]; } catch (e) { ai = 'null /*ERROR_ACCESSING*/'; } var s = name + ':' + toSource(ai, seen); members.push(s); } } catch (e) {} var prefix = ''; idx = seen.indexOf(a); if (idx != -1) prefix = '#' + (idx + 1) + '='; return prefix + '{' + members.join(", ") + '}' } if (typeof a == 'function') return '(' + a.toString().replace(/\n/g, " ").replace(/\s+/g, " ") + ')'; return a.toString(); } function formatError(e) { CONSOLE.error(e) } function runTests() { var setup_args = []; if (Tests.startUnit != null) { try { setup_args = Tests.startUnit(); } catch (e) { testFailed("startUnit", formatError(e)); printTestStatus(); return; } } var testsRun = false; var allTestsSuccessful = true; for (var i in Tests) { if (i.substring(0, 4) != "test") continue; __testSuccess__ = true; try { doTestNotify(i); var args = setup_args; if (Tests.setup != null) args = Tests.setup.apply(Tests, setup_args); Tests[i].apply(Tests, args); if (Tests.teardown != null) Tests.teardown.apply(Tests, args); } catch (e) { testFailed(i, e.name, formatError(e)); } if (__testSuccess__ == false) { ++__testFailCount__; } allTestsSuccessful = allTestsSuccessful && __testSuccess__ == true; reportTestResultsToHarness(__testSuccess__, i); doTestNotify(i + "--" + (__testSuccess__ ? "OK" : "FAIL")); testsRun = true; } printTestStatus(testsRun); if (Tests.endUnit != null) { try { Tests.endUnit.apply(Tests, setup_args); } catch (e) { testFailed("endUnit", e.name, formatError(e)); } } notifyFinishedToHarness(allTestsSuccessful, "finished tests"); } function doTestNotify(name) { //try { // var xhr = new XMLHttpRequest(); // xhr.open("GET", "http://localhost:8888/"+name, true); // xhr.send(null); //} catch(e) {} } function testFailed(assertName, name) { ENVIRONMENT.tape.fail(assertName + " -- " + name) } function testPassed(assertName, name) { ENVIRONMENT.tape.pass(assertName + " -- " + name) } function checkTestSuccess() { return __testFailCount__ == 0; } function log(msg) { CONSOLE.log(msg) } function printTestStatus(testsRun) { CONSOLE.log("STATUS", testsRun ? "OK" : "FAIL") } function assertFail(name, f) { if (f == null) { f = name; name = null; } var r = false; try { f(); } catch (e) { r = true; } if (!r) { testFailed("assertFail", name, f); return false; } else { testPassed("assertFail", name, f); return true; } } function assertOk(name, f) { if (f == null) { f = name; name = null; } var r = false; var err; try { f(); r = true; } catch (e) { err = e; } if (!r) { testFailed("assertOk", name, f, err.toString()); return false; } else { testPassed("assertOk", name, f); return true; } } function assert(name, v) { if (v == null) { v = name; name = null; } if (!v) { testFailed("assert", name, v); return false; } else { testPassed("assert", name, v); return true; } } function assertProperty(name, v, p) { if (p == null) { p = v; v = name; name = p; } if (v[p] == null) { testFailed("assertProperty", name); return false; } else { testPassed("assertProperty", name); return true; } } function compare(a, b) { if (typeof a == 'number' && typeof b == 'number') { return a == b; } else { return toSource(a) == toSource(b); } } function assertEquals(name, v, p) { if (p == null) { p = v; v = name; name = null; } if (!compare(v, p)) { testFailed("assertEquals", name, v, p); return false; } else { testPassed("assertEquals", name, v, p); return true; } } function assertArrayEquals(name, v, p) { if (p == null) { p = v; v = name; name = null; } if (!v) { testFailed("assertArrayEquals: first array undefined", name, v, p); return false; } if (!p) { testFailed("assertArrayEquals: second array undefined", name, v, p); return false; } if (v.length != p.length) { testFailed("assertArrayEquals", name, v, p); return false; } for (var ii = 0; ii < v.length; ++ii) { if (v[ii] != p[ii]) { testFailed("assertArrayEquals", name, v, p); return false; } } testPassed("assertArrayEquals", name, v, p); return true; } function assertArrayEqualsWithEpsilon(name, v, p, l) { if (l == null) { l = p; p = v; v = name; name = null; } if (!v) { testFailed("assertArrayEqualsWithEpsilon: first array undefined", name, v, p); return false; } if (!p) { testFailed("assertArrayEqualsWithEpsilon: second array undefined", name, v, p); return false; } if (!l) { testFailed("assertArrayEqualsWithEpsilon: limit array undefined", name, v, p); return false; } if (v.length != p.length) { testFailed("assertArrayEqualsWithEpsilon", name, v, p, l); return false; } if (v.length != l.length) { testFailed("assertArrayEqualsWithEpsilon", name, v, p, l); return false; } for (var ii = 0; ii < v.length; ++ii) { if (Math.abs(v[ii] - p[ii]) > l[ii]) { testFailed("assertArrayEqualsWithEpsilon", name, v, p, l); return false; } } testPassed("assertArrayEqualsWithEpsilon", name, v, p, l); return true; } function assertNotEquals(name, v, p) { if (p == null) { p = v; v = name; name = null; } if (compare(v, p)) { testFailed("assertNotEquals", name, v, p) return false; } else { testPassed("assertNotEquals", name, v, p) return true; } } function time(elementId, f) { var t0 = Date.now() f(); var t1 = Date.now() CONSOLE.log("Elapsed time:", (t1 - t0), "ms") } function randomFloat() { // note that in fuzz-testing, this can used as the size of a buffer to allocate. // so it shouldn't return astronomic values. The maximum value 10000000 is already quite big. var fac = 1.0; var r = Math.random(); if (r < 0.25) fac = 10; else if (r < 0.4) fac = 100; else if (r < 0.5) fac = 1000; else if (r < 0.6) fac = 100000; else if (r < 0.7) fac = 10000000; else if (r < 0.8) fac = NaN; return -0.5 * fac + Math.random() * fac; } function randomFloatFromRange(lo, hi) { var r = Math.random(); if (r < 0.05) return lo; else if (r > 0.95) return hi; else return lo + Math.random() * (hi - lo); } function randomInt(sz) { if (sz != null) return Math.floor(Math.random() * sz); else return Math.floor(randomFloat()); } function randomIntFromRange(lo, hi) { return Math.floor(randomFloatFromRange(lo, hi)); } function randomLength() { var l = Math.floor(Math.random() * 256); if (Math.random < 0.5) l = l / 10; if (Math.random < 0.3) l = l / 10; return l; } function randomSmallIntArray() { var l = randomLength(); var s = new Array(l); for (var i = 0; i < l; i++) s[i] = Math.floor(Math.random() * 256) - 1; return s; } function randomFloatArray() { var l = randomLength(); var s = new Array(l); for (var i = 0; i < l; i++) s[i] = randomFloat(); return s; } function randomIntArray() { var l = randomLength(); var s = new Array(l); for (var i = 0; i < l; i++) s[i] = randomFloat(); return s; } function randomMixedArray() { var l = randomLength(); var s = new Array(l); for (var i = 0; i < l; i++) s[i] = randomNonArray(); return s; } function randomArray() { var r = Math.random(); if (r < 0.3) return randomFloatArray(); else if (r < 0.6) return randomIntArray(); else if (r < 0.8) return randomSmallIntArray(); else return randomMixedArray(); } function randomString() { return String.fromCharCode.apply(String, randomSmallIntArray()); } function randomGLConstant() { return GLConstants[Math.floor(Math.random() * GLConstants.length)]; } function randomNonArray() { var r = Math.random(); if (r < 0.25) { return randomFloat(); } else if (r < 0.6) { return randomInt(); } else if (r < 0.7) { return (r < 0.65); } else if (r < 0.87) { return randomString(); } else if (r < 0.98) { return randomGLConstant(); } else { return null; } } function generateRandomArg(pos, count) { if (pos == 0 && Math.random() < 0.5) return randomGLConstant(); if (pos == count - 1 && Math.random() < 0.25) if (Math.random() < 0.5) return randomString(); else return randomArray(); var r = Math.random(); if (r < 0.25) { return randomFloat(); } else if (r < 0.6) { return randomInt(); } else if (r < 0.7) { return (r < 0.65); } else if (r < 0.77) { return randomString(); } else if (r < 0.84) { return randomArray(); } else if (r < 0.98) { return randomGLConstant(); } else { return null; } } function generateRandomArgs(count) { var arr = new Array(count); for (var i = 0; i < count; i++) arr[i] = generateRandomArg(i, count); return arr; } // qc (arg1gen, arg2gen, ..., predicate) // qc (randomString, randomInt, randomInt, function(s,i,j){ s.substring(i,j) }) function qc() {} GLConstants = [ 1, 0x00000100, 0x00000400, 0x00004000, 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0, 1, 0x0300, 0x0301, 0x0302, 0x0303, 0x0304, 0x0305, 0x0306, 0x0307, 0x0308, 0x8006, 0x8009, 0x8009, 0x883D, 0x800A, 0x800B, 0x80C8, 0x80C9, 0x80CA, 0x80CB, 0x8001, 0x8002, 0x8003, 0x8004, 0x8005, 0x8892, 0x8893, 0x8894, 0x8895, 0x88E0, 0x88E4, 0x88E8, 0x8764, 0x8765, 0x8626, 0x0404, 0x0405, 0x0408, 0x0DE1, 0x0B44, 0x0BE2, 0x0BD0, 0x0B90, 0x0B71, 0x0C11, 0x8037, 0x809E, 0x80A0, 0, 0x0500, 0x0501, 0x0502, 0x0505, 0x0900, 0x0901, 0x0B21, 0x846D, 0x846E, 0x0B45, 0x0B46, 0x0B70, 0x0B72, 0x0B73, 0x0B74, 0x0B91, 0x0B92, 0x0B94, 0x0B95, 0x0B96, 0x0B97, 0x0B93, 0x0B98, 0x8800, 0x8801, 0x8802, 0x8803, 0x8CA3, 0x8CA4, 0x8CA5, 0x0BA2, 0x0C10, 0x0C22, 0x0C23, 0x0CF5, 0x0D05, 0x0D33, 0x0D3A, 0x0D50, 0x0D52, 0x0D53, 0x0D54, 0x0D55, 0x0D56, 0x0D57, 0x2A00, 0x8038, 0x8069, 0x80A8, 0x80A9, 0x80AA, 0x80AB, 0x86A2, 0x86A3, 0x1100, 0x1101, 0x1102, 0x8192, 0x1400, 0x1401, 0x1402, 0x1403, 0x1404, 0x1405, 0x1406, 0x140C, 0x1902, 0x1906, 0x1907, 0x1908, 0x1909, 0x190A, 0x8033, 0x8034, 0x8363, 0x8B30, 0x8B31, 0x8869, 0x8DFB, 0x8DFC, 0x8B4D, 0x8B4C, 0x8872, 0x8DFD, 0x8B4F, 0x8B80, 0x8B82, 0x8B83, 0x8B85, 0x8B86, 0x8B87, 0x8B89, 0x8B8A, 0x8B8C, 0x8B8D, 0x0200, 0x0201, 0x0202, 0x0203, 0x0204, 0x0205, 0x0206, 0x0207, 0x1E00, 0x1E01, 0x1E02, 0x1E03, 0x150A, 0x8507, 0x8508, 0x1F00, 0x1F01, 0x1F02, 0x1F03, 0x2600, 0x2601, 0x2700, 0x2701, 0x2702, 0x2703, 0x2800, 0x2801, 0x2802, 0x2803, 0x1702, 0x8513, 0x8514, 0x8515, 0x8516, 0x8517, 0x8518, 0x8519, 0x851A, 0x851C, 0x84C0, 0x84C1, 0x84C2, 0x84C3, 0x84C4, 0x84C5, 0x84C6, 0x84C7, 0x84C8, 0x84C9, 0x84CA, 0x84CB, 0x84CC, 0x84CD, 0x84CE, 0x84CF, 0x84D0, 0x84D1, 0x84D2, 0x84D3, 0x84D4, 0x84D5, 0x84D6, 0x84D7, 0x84D8, 0x84D9, 0x84DA, 0x84DB, 0x84DC, 0x84DD, 0x84DE, 0x84DF, 0x84E0, 0x2901, 0x812F, 0x8370, 0x8B50, 0x8B51, 0x8B52, 0x8B53, 0x8B54, 0x8B55, 0x8B56, 0x8B57, 0x8B58, 0x8B59, 0x8B5A, 0x8B5B, 0x8B5C, 0x8B5E, 0x8B60, 0x8622, 0x8623, 0x8624, 0x8625, 0x886A, 0x8645, 0x889F, 0x8B9A, 0x8B9B, 0x8B81, 0x8B84, 0x8B88, 0x8DFA, 0x8DF8, 0x8DF9, 0x8DF0, 0x8DF1, 0x8DF2, 0x8DF3, 0x8DF4, 0x8DF5, 0x8D40, 0x8D41, 0x8056, 0x8057, 0x8D62, 0x81A5, 0x1901, 0x8D48, 0x8D42, 0x8D43, 0x8D44, 0x8D50, 0x8D51, 0x8D52, 0x8D53, 0x8D54, 0x8D55, 0x8CD0, 0x8CD1, 0x8CD2, 0x8CD3, 0x8CE0, 0x8D00, 0x8D20, 0, 0x8CD5, 0x8CD6, 0x8CD7, 0x8CD9, 0x8CDD, 0x8CA6, 0x8CA7, 0x84E8, 0x0506, 0x809D ]; function reportTestResultsToHarness(success, msg) { // ENVIRONMENT.tape.end() } function notifyFinishedToHarness() { ENVIRONMENT.tape.end() } function initTests() { if (Tests.message != null) { CONSOLE.log(Tests.message) } runTests(); } setTimeout(initTests, Tests.delay || 1);; /* Utilities for the OpenGL ES 2.0 HTML Canvas context */ /* ** Copyright (c) 2012 The Khronos Group Inc. ** ** Permission is hereby granted, free of charge, to any person obtaining a ** copy of this software and/or associated documentation files (the ** "Materials"), to deal in the Materials without restriction, including ** without limitation the rights to use, copy, modify, merge, publish, ** distribute, sublicense, and/or sell copies of the Materials, and to ** permit persons to whom the Materials are furnished to do so, subject to ** the following conditions: ** ** The above copyright notice and this permission notice shall be included ** in all copies or substantial portions of the Materials. ** ** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. ** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY ** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, ** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE ** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. */ function loadTexture(gl, elem, mipmaps) { var tex = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, tex); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, elem); if (mipmaps != false) gl.generateMipmap(gl.TEXTURE_2D); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); if (mipmaps) gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR); else gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); return tex; } function getShader(gl, id) { var shaderScript = document.getElementById(id); if (!shaderScript) { throw (new Error("No shader element with id: " + id)); } var str = shaderScript.text; var shader; if (shaderScript.type == "x-shader/x-fragment") { shader = gl.createShader(gl.FRAGMENT_SHADER); } else if (shaderScript.type == "x-shader/x-vertex") { shader = gl.createShader(gl.VERTEX_SHADER); } else { throw (new Error("Unknown shader type " + shaderScript.type)); } gl.shaderSource(shader, str); gl.compileShader(shader); if (gl.getShaderParameter(shader, gl.COMPILE_STATUS) != 1) { var ilog = gl.getShaderInfoLog(shader); gl.deleteShader(shader); throw (new Error("Failed to compile shader " + shaderScript.id + ", Shader info log: " + ilog)); } return shader; } function loadShaderArray(gl, shaders) { var id = gl.createProgram(); var shaderObjs = []; for (var i = 0; i < shaders.length; ++i) { try { var sh = getShader(gl, shaders[i]); shaderObjs.push(sh); gl.attachShader(id, sh); } catch (e) { var pr = { program: id, shaders: shaderObjs }; deleteShader(gl, pr); throw (e); } } var prog = { program: id, shaders: shaderObjs }; gl.linkProgram(id); gl.validateProgram(id); if (gl.getProgramParameter(id, gl.LINK_STATUS) != 1) { deleteShader(gl, prog); throw (new Error("Failed to link shader")); } if (gl.getProgramParameter(id, gl.VALIDATE_STATUS) != 1) { deleteShader(gl, prog); throw (new Error("Failed to validate shader")); } return prog; } function loadShader(gl) { var sh = []; for (var i = 1; i < arguments.length; ++i) sh.push(arguments[i]); return loadShaderArray(gl, sh); } function deleteShader(gl, sh) { gl.useProgram(null); sh.shaders.forEach(function(s) { gl.detachShader(sh.program, s); gl.deleteShader(s); }); gl.deleteProgram(sh.program); } function getGLErrorAsString(ctx, err) { if (err === ctx.NO_ERROR) { return "NO_ERROR"; } for (var name in ctx) { if (ctx[name] === err) { return name; } } return err.toString(); } function checkError(gl, msg) { var e = gl.getError(); if (e != gl.NO_ERROR) { log("Error " + getGLErrorAsString(gl, e) + " at " + msg); } return e; } function throwError(gl, msg) { var e = gl.getError(); if (e != 0) { throw (new Error("Error " + getGLErrorAsString(gl, e) + " at " + msg)); } } Math.cot = function(z) { return 1.0 / Math.tan(z); } /* Matrix utilities, using the OpenGL element order where the last 4 elements are the translation column. Uses flat arrays as matrices for performance. Most operations have in-place variants to avoid allocating temporary matrices. Naming logic: Matrix.method operates on a 4x4 Matrix and returns a new Matrix. Matrix.method3x3 operates on a 3x3 Matrix and returns a new Matrix. Not all operations have a 3x3 version (as 3x3 is usually only used for the normal matrix: Matrix.transpose3x3(Matrix.inverseTo3x3(mat4x4))) Matrix.method[3x3]InPlace(args, target) stores its result in the target matrix. Matrix.scale([sx, sy, sz]) -- non-uniform scale by vector Matrix.scale1(s) -- uniform scale by scalar Matrix.scale3(sx, sy, sz) -- non-uniform scale by scalars Ditto for translate. */ Matrix = { identity: [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ], newIdentity: function() { return [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ]; }, newIdentity3x3: function() { return [ 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0 ]; }, copyMatrix: function(src, dst) { for (var i = 0; i < 16; i++) dst[i] = src[i]; return dst; }, to3x3: function(m) { return [ m[0], m[1], m[2], m[4], m[5], m[6], m[8], m[9], m[10] ]; }, // orthonormal matrix inverse inverseON: function(m) { var n = this.transpose4x4(m); var t = [m[12], m[13], m[14]]; n[3] = n[7] = n[11] = 0; n[12] = -Vec3.dot([n[0], n[4], n[8]], t); n[13] = -Vec3.dot([n[1], n[5], n[9]], t); n[14] = -Vec3.dot([n[2], n[6], n[10]], t); return n; }, inverseTo3x3: function(m) { return this.inverse4x4to3x3InPlace(m, this.newIdentity3x3()); }, inverseTo3x3InPlace: function(m, n) { var a11 = m[10] * m[5] - m[6] * m[9], a21 = -m[10] * m[1] + m[2] * m[9], a31 = m[6] * m[1] - m[2] * m[5], a12 = -m[10] * m[4] + m[6] * m[8], a22 = m[10] * m[0] - m[2] * m[8], a32 = -m[6] * m[0] + m[2] * m[4], a13 = m[9] * m[4] - m[5] * m[8], a23 = -m[9] * m[0] + m[1] * m[8], a33 = m[5] * m[0] - m[1] * m[4]; var det = m[0] * (a11) + m[1] * (a12) + m[2] * (a13); if (det == 0) // no inverse return [1, 0, 0, 0, 1, 0, 0, 0, 1]; var idet = 1 / det; n[0] = idet * a11; n[1] = idet * a21; n[2] = idet * a31; n[3] = idet * a12; n[4] = idet * a22; n[5] = idet * a32; n[6] = idet * a13; n[7] = idet * a23; n[8] = idet * a33; return n; }, inverse3x3: function(m) { return this.inverse3x3InPlace(m, this.newIdentity3x3()); }, inverse3x3InPlace: function(m, n) { var a11 = m[8] * m[4] - m[5] * m[7], a21 = -m[8] * m[1] + m[2] * m[7], a31 = m[5] * m[1] - m[2] * m[4], a12 = -m[8] * m[3] + m[5] * m[6], a22 = m[8] * m[0] - m[2] * m[6], a32 = -m[5] * m[0] + m[2] * m[3], a13 = m[7] * m[4] - m[4] * m[8], a23 = -m[7] * m[0] + m[1] * m[6], a33 = m[4] * m[0] - m[1] * m[3]; var det = m[0] * (a11) + m[1] * (a12) + m[2] * (a13); if (det == 0) // no inverse return [1, 0, 0, 0, 1, 0, 0, 0, 1]; var idet = 1 / det; n[0] = idet * a11; n[1] = idet * a21; n[2] = idet * a31; n[3] = idet * a12; n[4] = idet * a22; n[5] = idet * a32; n[6] = idet * a13; n[7] = idet * a23; n[8] = idet * a33; return n; }, frustum: function(left, right, bottom, top, znear, zfar) { var X = 2 * znear / (right - left); var Y = 2 * znear / (top - bottom); var A = (right + left) / (right - left); var B = (top + bottom) / (top - bottom); var C = -(zfar + znear) / (zfar - znear); var D = -2 * zfar * znear / (zfar - znear); return [ X, 0, 0, 0, 0, Y, 0, 0, A, B, C, -1, 0, 0, D, 0 ]; }, perspective: function(fovy, aspect, znear, zfar) { var ymax = znear * Math.tan(fovy * Math.PI / 360.0); var ymin = -ymax; var xmin = ymin * aspect; var xmax = ymax * aspect; return this.frustum(xmin, xmax, ymin, ymax, znear, zfar); }, mul4x4: function(a, b) { return this.mul4x4InPlace(a, b, this.newIdentity()); }, mul4x4InPlace: function(a, b, c) { c[0] = b[0] * a[0] + b[0 + 1] * a[4] + b[0 + 2] * a[8] + b[0 + 3] * a[12]; c[0 + 1] = b[0] * a[1] + b[0 + 1] * a[5] + b[0 + 2] * a[9] + b[0 + 3] * a[13]; c[0 + 2] = b[0] * a[2] + b[0 + 1] * a[6] + b[0 + 2] * a[10] + b[0 + 3] * a[14]; c[0 + 3] = b[0] * a[3] + b[0 + 1] * a[7] + b[0 + 2] * a[11] + b[0 + 3] * a[15]; c[4] = b[4] * a[0] + b[4 + 1] * a[4] + b[4 + 2] * a[8] + b[4 + 3] * a[12]; c[4 + 1] = b[4] * a[1] + b[4 + 1] * a[5] + b[4 + 2] * a[9] + b[4 + 3] * a[13]; c[4 + 2] = b[4] * a[2] + b[4 + 1] * a[6] + b[4 + 2] * a[10] + b[4 + 3] * a[14]; c[4 + 3] = b[4] * a[3] + b[4 + 1] * a[7] + b[4 + 2] * a[11] + b[4 + 3] * a[15]; c[8] = b[8] * a[0] + b[8 + 1] * a[4] + b[8 + 2] * a[8] + b[8 + 3] * a[12]; c[8 + 1] = b[8] * a[1] + b[8 + 1] * a[5] + b[8 + 2] * a[9] + b[8 + 3] * a[13]; c[8 + 2] = b[8] * a[2] + b[8 + 1] * a[6] + b[8 + 2] * a[10] + b[8 + 3] * a[14]; c[8 + 3] = b[8] * a[3] + b[8 + 1] * a[7] + b[8 + 2] * a[11] + b[8 + 3] * a[15]; c[12] = b[12] * a[0] + b[12 + 1] * a[4] + b[12 + 2] * a[8] + b[12 + 3] * a[12]; c[12 + 1] = b[12] * a[1] + b[12 + 1] * a[5] + b[12 + 2] * a[9] + b[12 + 3] * a[13]; c[12 + 2] = b[12] * a[2] + b[12 + 1] * a[6] + b[12 + 2] * a[10] + b[12 + 3] * a[14]; c[12 + 3] = b[12] * a[3] + b[12 + 1] * a[7] + b[12 + 2] * a[11] + b[12 + 3] * a[15]; return c; }, mulv4: function(a, v) { c = new Array(4); for (var i = 0; i < 4; ++i) { var x = 0; for (var k = 0; k < 4; ++k) x += v[k] * a[k * 4 + i]; c[i] = x; } return c; }, rotate: function(angle, axis) { axis = Vec3.normalize(axis); var x = axis[0], y = axis[1], z = axis[2]; var c = Math.cos(angle); var c1 = 1 - c; var s = Math.sin(angle); return [ x * x * c1 + c, y * x * c1 + z * s, z * x * c1 - y * s, 0, x * y * c1 - z * s, y * y * c1 + c, y * z * c1 + x * s, 0, x * z * c1 + y * s, y * z * c1 - x * s, z * z * c1 + c, 0, 0, 0, 0, 1 ]; }, rotateInPlace: function(angle, axis, m) { axis = Vec3.normalize(axis); var x = axis[0], y = axis[1], z = axis[2]; var c = Math.cos(angle); var c1 = 1 - c; var s = Math.sin(angle); var tmpMatrix = this.tmpMatrix; var tmpMatrix2 = this.tmpMatrix2; tmpMatrix[0] = x * x * c1 + c; tmpMatrix[1] = y * x * c1 + z * s; tmpMatrix[2] = z * x * c1 - y * s; tmpMatrix[3] = 0; tmpMatrix[4] = x * y * c1 - z * s; tmpMatrix[5] = y * y * c1 + c; tmpMatrix[6] = y * z * c1 + x * s; tmpMatrix[7] = 0; tmpMatrix[8] = x * z * c1 + y * s; tmpMatrix[9] = y * z * c1 - x * s; tmpMatrix[10] = z * z * c1 + c; tmpMatrix[11] = 0; tmpMatrix[12] = 0; tmpMatrix[13] = 0; tmpMatrix[14] = 0; tmpMatrix[15] = 1; this.copyMatrix(m, tmpMatrix2); return this.mul4x4InPlace(tmpMatrix2, tmpMatrix, m); }, scale: function(v) { return [ v[0], 0, 0, 0, 0, v[1], 0, 0, 0, 0, v[2], 0, 0, 0, 0, 1 ]; }, scale3: function(x, y, z) { return [ x, 0, 0, 0, 0, y, 0, 0, 0, 0, z, 0, 0, 0, 0, 1 ]; }, scale1: function(s) { return [ s, 0, 0, 0, 0, s, 0, 0, 0, 0, s, 0, 0, 0, 0, 1 ]; }, scale3InPlace: function(x, y, z, m) { var tmpMatrix = this.tmpMatrix; var tmpMatrix2 = this.tmpMatrix2; tmpMatrix[0] = x; tmpMatrix[1] = 0; tmpMatrix[2] = 0; tmpMatrix[3] = 0; tmpMatrix[4] = 0; tmpMatrix[5] = y; tmpMatrix[6] = 0; tmpMatrix[7] = 0; tmpMatrix[8] = 0; tmpMatrix[9] = 0; tmpMatrix[10] = z; tmpMatrix[11] = 0; tmpMatrix[12] = 0; tmpMatrix[13] = 0; tmpMatrix[14] = 0; tmpMatrix[15] = 1; this.copyMatrix(m, tmpMatrix2); return this.mul4x4InPlace(tmpMatrix2, tmpMatrix, m); }, scale1InPlace: function(s, m) { return this.scale3InPlace(s, s, s, m); }, scaleInPlace: function(s, m) { return this.scale3InPlace(s[0], s[1], s[2], m); }, translate3: function(x, y, z) { return [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, x, y, z, 1 ]; }, translate: function(v) { return this.translate3(v[0], v[1], v[2]); }, tmpMatrix: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], tmpMatrix2: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], translate3InPlace: function(x, y, z, m) { var tmpMatrix = this.tmpMatrix; var tmpMatrix2 = this.tmpMatrix2; tmpMatrix[0] = 1; tmpMatrix[1] = 0; tmpMatrix[2] = 0; tmpMatrix[3] = 0; tmpMatrix[4] = 0; tmpMatrix[5] = 1; tmpMatrix[6] = 0; tmpMatrix[7] = 0; tmpMatrix[8] = 0; tmpMatrix[9] = 0; tmpMatrix[10] = 1; tmpMatrix[11] = 0; tmpMatrix[12] = x; tmpMatrix[13] = y; tmpMatrix[14] = z; tmpMatrix[15] = 1; this.copyMatrix(m, tmpMatrix2); return this.mul4x4InPlace(tmpMatrix2, tmpMatrix, m); }, translateInPlace: function(v, m) { return this.translate3InPlace(v[0], v[1], v[2], m); }, lookAt: function(eye, center, up) { var z = Vec3.direction(eye, center); var x = Vec3.normalizeInPlace(Vec3.cross(up, z)); var y = Vec3.normalizeInPlace(Vec3.cross(z, x)); var m = [ x[0], y[0], z[0], 0, x[1], y[1], z[1], 0, x[2], y[2], z[2], 0, 0, 0, 0, 1 ]; var t = [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -eye[0], -eye[1], -eye[2], 1 ]; return this.mul4x4(m, t); }, transpose4x4: function(m) { return [ m[0], m[4], m[8], m[12], m[1], m[5], m[9], m[13], m[2], m[6], m[10], m[14], m[3], m[7], m[11], m[15] ]; }, transpose4x4InPlace: function(m) { var tmp = 0.0; tmp = m[1]; m[1] = m[4]; m[4] = tmp; tmp = m[2]; m[2] = m[8]; m[8] = tmp; tmp = m[3]; m[3] = m[12]; m[12] = tmp; tmp = m[6]; m[6] = m[9]; m[9] = tmp; tmp = m[7]; m[7] = m[13]; m[13] = tmp; tmp = m[11]; m[11] = m[14]; m[14] = tmp; return m; }, transpose3x3: function(m) { return [ m[0], m[3], m[6], m[1], m[4], m[7], m[2], m[5], m[8] ]; }, transpose3x3InPlace: function(m) { var tmp = 0.0; tmp = m[1]; m[1] = m[3]; m[3] = tmp; tmp = m[2]; m[2] = m[6]; m[6] = tmp; tmp = m[5]; m[5] = m[7]; m[7] = tmp; return m; }, } Vec3 = { make: function() { return [0, 0, 0]; }, copy: function(v) { return [v[0], v[1], v[2]]; }, add: function(u, v) { return [u[0] + v[0], u[1] + v[1], u[2] + v[2]]; }, sub: function(u, v) { return [u[0] - v[0], u[1] - v[1], u[2] - v[2]]; }, negate: function(u) { return [-u[0], -u[1], -u[2]]; }, direction: function(u, v) { return this.normalizeInPlace(this.sub(u, v)); }, normalizeInPlace: function(v) { var imag = 1.0 / Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); v[0] *= imag; v[1] *= imag; v[2] *= imag; return v; }, normalize: function(v) { return this.normalizeInPlace(this.copy(v)); }, scale: function(f, v) { return [f * v[0], f * v[1], f * v[2]]; }, dot: function(u, v) { return u[0] * v[0] + u[1] * v[1] + u[2] * v[2]; }, inner: function(u, v) { return [u[0] * v[0], u[1] * v[1], u[2] * v[2]]; }, cross: function(u, v) { return [ u[1] * v[2] - u[2] * v[1], u[2] * v[0] - u[0] * v[2], u[0] * v[1] - u[1] * v[0] ]; } } Shader = function(gl) { this.gl = gl; this.shaders = []; this.uniformLocations = {}; this.attribLocations = {}; for (var i = 1; i < arguments.length; i++) { this.shaders.push(arguments[i]); } } Shader.prototype = { id: null, gl: null, compiled: false, shader: null, shaders: [], destroy: function() { if (this.shader != null) deleteShader(this.gl, this.shader); }, compile: function() { this.shader = loadShaderArray(this.gl, this.shaders); }, use: function() { if (this.shader == null) this.compile(); this.gl.useProgram(this.shader.program); }, uniform1fv: function(name, value) { var loc = this.uniform(name); this.gl.uniform1fv(loc, value); }, uniform2fv: function(name, value) { var loc = this.uniform(name); this.gl.uniform2fv(loc, value); }, uniform3fv: function(name, value) { var loc = this.uniform(name); this.gl.uniform3fv(loc, value); }, uniform4fv: function(name, value) { var loc = this.uniform(name); this.gl.uniform4fv(loc, value); }, uniform1f: function(name, value) { var loc = this.uniform(name); this.gl.uniform1f(loc, value); }, uniform2f: function(name, v1, v2) { var loc = this.uniform(name); this.gl.uniform2f(loc, v1, v2); }, uniform3f: function(name, v1, v2, v3) { var loc = this.uniform(name); this.gl.uniform3f(loc, v1, v2, v3); }, uniform4f: function(name, v1, v2, v3, v4) { var loc = this.uniform(name); this.gl.uniform4f(loc, v1, v2, v3, v4); }, uniform1iv: function(name, value) { var loc = this.uniform(name); this.gl.uniform1iv(loc, value); }, uniform2iv: function(name, value) { var loc = this.uniform(name); this.gl.uniform2iv(loc, value); }, uniform3iv: function(name, value) { var loc = this.uniform(name); this.gl.uniform3iv(loc, value); }, uniform4iv: function(name, value) { var loc = this.uniform(name); this.gl.uniform4iv(loc, value); }, uniform1i: function(name, value) { var loc = this.uniform(name); this.gl.uniform1i(loc, value); }, uniform2i: function(name, v1, v2) { var loc = this.uniform(name); this.gl.uniform2i(loc, v1, v2); }, uniform3i: function(name, v1, v2, v3) { var loc = this.uniform(name); this.gl.uniform3i(loc, v1, v2, v3); }, uniform4i: function(name, v1, v2, v3, v4) { var loc = this.uniform(name); this.gl.uniform4i(loc, v1, v2, v3, v4); }, uniformMatrix4fv: function(name, value) { var loc = this.uniform(name); this.gl.uniformMatrix4fv(loc, false, value); }, uniformMatrix3fv: function(name, value) { var loc = this.uniform(name); this.gl.uniformMatrix3fv(loc, false, value); }, uniformMatrix2fv: function(name, value) { var loc = this.uniform(name); this.gl.uniformMatrix2fv(loc, false, value); }, attrib: function(name) { if (this.attribLocations[name] == null) { var loc = this.gl.getAttribLocation(this.shader.program, name); this.attribLocations[name] = loc; } return this.attribLocations[name]; }, uniform: function(name) { if (this.uniformLocations[name] == null) { var loc = this.gl.getUniformLocation(this.shader.program, name); this.uniformLocations[name] = loc; } return this.uniformLocations[name]; } } Filter = function(gl, shader) { Shader.apply(this, arguments); } Filter.prototype = new Shader(); Filter.prototype.apply = function(init) { this.use(); var va = this.attrib("Vertex"); var ta = this.attrib("Tex"); var vbo = Quad.getCachedVBO(this.gl); if (init) init(this); vbo.draw(va, null, ta); } VBO = function(gl) { this.gl = gl; this.data = []; this.elementsVBO = null; for (var i = 1; i < arguments.length; i++) { if (arguments[i].elements) this.elements = arguments[i]; else this.data.push(arguments[i]); } } VBO.prototype = { initialized: false, length: 0, vbos: null, type: 'TRIANGLES', elementsVBO: null, elements: null, setData: function() { this.destroy(); this.data = []; for (var i = 0; i < arguments.length; i++) { if (arguments[i].elements) this.elements = arguments[i]; else this.data.push(arguments[i]); } }, destroy: function() { if (this.vbos != null) for (var i = 0; i < this.vbos.length; i++) this.gl.deleteBuffer(this.vbos[i]); if (this.elementsVBO != null) this.gl.deleteBuffer(this.elementsVBO); this.length = this.elementsLength = 0; this.vbos = this.elementsVBO = null; this.initialized = false; }, init: function() { this.destroy(); var gl = this.gl; gl.getError(); var vbos = []; var length = 0; for (var i = 0; i < this.data.length; i++) vbos.push(gl.createBuffer()); if (this.elements != null) this.elementsVBO = gl.createBuffer(); try { throwError(gl, "genBuffers"); for (var i = 0; i < this.data.length; i++) { var d = this.data[i]; var dlen = Math.floor(d.data.length / d.size); if (i == 0 || dlen < length) length = dlen; if (!d.floatArray) d.floatArray = new Float32Array(d.data); gl.bindBuffer(gl.ARRAY_BUFFER, vbos[i]); throwError(gl, "bindBuffer"); gl.bufferData(gl.ARRAY_BUFFER, d.floatArray, gl.STATIC_DRAW); throwError(gl, "bufferData"); } if (this.elementsVBO != null) { var d =