gl-conformance
Version:
Khronos WebGL conformance test suite
1,773 lines (1,627 loc) • 70 kB
JavaScript
/*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_texSubImage2DBadArgs(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 = thi