/* * KNWebGLUtil.js * Keynote HTML Player * * Created by Tungwei Cheng * Copyright (c) 2016-2018 Apple Inc. All rights reserved. */ var KNWebGLUtil = {}; KNWebGLUtil.setupProgram = function(gl, programName) { var shader = KNWebGLShader[programName]; var vertexShader = this.loadShader(gl, gl.VERTEX_SHADER, shader.vertex); var fragmentShader = this.loadShader(gl, gl.FRAGMENT_SHADER, shader.fragment); var shaderProgram = this.createShaderProgram(gl, vertexShader, fragmentShader); // creates uniforms and attribs but does not enable attribs. var attribs = {}; var uniforms = {}; for (var i = 0, length = shader.uniformNames.length; i < length; i++) { var uniformName = shader.uniformNames[i]; uniforms[uniformName] = gl.getUniformLocation(shaderProgram, uniformName); } for (var i = 0, length = shader.attribNames.length; i < length; i++) { var attribName = shader.attribNames[i]; attribs[attribName] = gl.getAttribLocation(shaderProgram, attribName); } // create a program object var program = { shaderProgram: shaderProgram, uniforms: uniforms, attribs: attribs }; // use this program for rendering gl.useProgram(shaderProgram); return program; }; KNWebGLUtil.loadShader = function(gl, type, shaderSource) { var shader = gl.createShader(type); gl.shaderSource(shader, shaderSource); gl.compileShader(shader); // Check the compile status var compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS); if (!compiled) { // error during compilation var error = gl.getShaderInfoLog(shader); console.log("*** Error compiling shader '" + shader + "':" + error); gl.deleteShader(shader); return null; } return shader; }; KNWebGLUtil.createShaderProgram = function(gl, vertexShader, fragmentShader) { // create shader program var shaderProgram = gl.createProgram(); // Attach the shaders to the program gl.attachShader(shaderProgram, vertexShader); gl.attachShader(shaderProgram, fragmentShader); // Link the program gl.linkProgram(shaderProgram); var linked = gl.getProgramParameter(shaderProgram, gl.LINK_STATUS); if (!linked) { var error = gl.getProgramInfoLog(shaderProgram); console.log("Error in program linking:" + error); gl.deleteProgram(shaderProgram); } return shaderProgram; }; KNWebGLUtil.createTexture = function(gl, image) { var texture = gl.createTexture(); // bind WebGLTexture object to gl.TEXTURE_2D target gl.bindTexture(gl.TEXTURE_2D, texture); gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true); gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true); // upload texture data to GPU gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); 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.bindTexture(gl.TEXTURE_2D, null); return texture; }; KNWebGLUtil.bindTextureWithImage = function(gl, image) { var texture = gl.createTexture(); // bind WebGLTexture object to gl.TEXTURE_2D target gl.bindTexture(gl.TEXTURE_2D, texture); gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true); gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); 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.bindTexture(gl.TEXTURE_2D, null); return texture; }; KNWebGLUtil.bindDynamicBufferWithData = function(gl, attribLoc, buffer, data, size) { gl.bindBuffer(gl.ARRAY_BUFFER, buffer); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(data), gl.DYNAMIC_DRAW); // we need to enable attrib loc to work with data buffer gl.enableVertexAttribArray(attribLoc); gl.vertexAttribPointer(attribLoc, size, gl.FLOAT, false, 0, 0); }; KNWebGLUtil.bindBufferWithData = function(gl, attribLoc, buffer, data, size, bufferUsage) { gl.bindBuffer(gl.ARRAY_BUFFER, buffer); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(data), bufferUsage); // we need to enable attrib loc to work with data buffer gl.enableVertexAttribArray(attribLoc); gl.vertexAttribPointer(attribLoc, size, gl.FLOAT, false, 0, 0); }; //attribute buffer insertion KNWebGLUtil.setPoint2DAtIndexForAttribute = function(point, index, attribute) { //attribute cannot become an object. we need to create an object where we can place this. BUMMER attribute[index*2] = point.x; attribute[index*2+1] = point.y; attribute.size = 2; }; KNWebGLUtil.setPoint3DAtIndexForAttribute = function(point, index, attribute) { attribute[index*3] = point.x; attribute[index*3+1] = point.y; attribute[index*3+2] = point.z; attribute.size = 3; }; KNWebGLUtil.setPoint4DAtIndexForAttribute = function(point, index, attribute) { attribute[index*4] = point.x; attribute[index*4+1] = point.y; attribute[index*4+2] = point.z; attribute[index*4+3] = point.w; attribute.size = 4; }; KNWebGLUtil.setFloatAtIndexForAttribute = function(f, index, attribute) { attribute[index] = f; }; KNWebGLUtil.getPoint2DForArrayAtIndex = function(attrib, index) { var point = {}; point.x = attrib[index*2]; point.y = attrib[index*2 + 1]; return point; }; KNWebGLUtil.getPoint3DForArrayAtIndex = function(attrib, index) { var point = {}; point.x = attrib[index*3]; point.y = attrib[index*3 + 1]; point.z = attrib[index*3 + 2]; return point; }; KNWebGLUtil.getPoint4DForArrayAtIndex = function(attrib, index) { var point = {}; point.x = attrib[index*4]; point.y = attrib[index*4 + 1]; point.z = attrib[index*4 + 2]; point.w = attrib[index*4 + 3]; return point; }; KNWebGLUtil.bindAllAvailableAttributesToBuffers = function(gl, attribs, bufferdata, size, buffer, bufferUsage) { for (var obj in attribs) { var attribute = attribs[obj]; if (buffer[obj] == undefined) { buffer[obj] = gl.createBuffer(); } KNWebGLUtil.bindBufferWithData(gl, attribute, buffer[obj], bufferdata[obj], size[obj], bufferUsage); } }; // We need to enable attribs before binding. // This also sets the program to the given program. // This never needs to be called in single program animations KNWebGLUtil.enableAttribs = function(gl, program) { var attribs = program.attribs; gl.useProgram(program.shaderProgram); for (var obj in attribs) { gl.enableVertexAttribArray(attribs[obj]); } }; /* * WebGraphics is not a container for any data. It should only computer and return values. * * makePoint(x, y): returns a object with .x and .y properties attached * * randomBetween(a, b): returns a random number between a (lower bound) and b (upper bound) * * mix(x, y, a): returns a linear intern between x and y using a as a weight between them * * clamp(x, minVal, maxVal) : clamps x between a min and max value */ var WebGraphics = {}; WebGraphics.makePoint = function(x, y) { var obj = {}; obj.x = x; obj.y = y; return obj; }; WebGraphics.makePoint3D = function(x, y, z) { var obj = {}; obj.x = x; obj.y = y; obj.z = z; return obj; }; WebGraphics.makePoint4D = function(x, y, z, w) { var obj = {}; obj.x = x; obj.y = y; obj.z = z; obj.w = w; return obj; }; WebGraphics.makeRect = function(x,y, width, height) { var obj = {}; obj.x = x; obj.y = y; obj.width = width; obj.height = height; return obj; }; WebGraphics.makeSize = function(width, height) { var obj = {}; obj.width = width; obj.height = height; return obj; }; WebGraphics.setOrigin = function(obj, point) { obj.x = point.x; obj.y = point.y; return obj; }; WebGraphics.multiplyPoint3DByScalar = function(point, scalar) { var obj = {}; obj.x = point.x * scalar; obj.y = point.y * scalar; obj.z = point.z * scalar; return obj; }; WebGraphics.multiplyPoint4DByScalar = function(point, scalar) { var obj = {}; obj.x = point.x * scalar; obj.y = point.y * scalar; obj.z = point.z * scalar; obj.w = point.w * scalar; return obj; }; WebGraphics.addPoint3DToPoint3D = function(a, b) { var obj = {}; obj.x = a.x + b.x; obj.y = a.y + b.y; obj.z = a.z + b.z; return obj; }; WebGraphics.point3DNormalize = function(pt3d) { var length = Math.sqrt(pt3d.x * pt3d.x + pt3d.y * pt3d.y + pt3d.z * pt3d.z); var obj = {}; obj.z = pt3d.z / length; obj.y = pt3d.y / length; obj.x = pt3d.x / length; return obj; }; WebGraphics.randomBetween = function(min, max) { var x = Math.random(); x *= (max - min); x += min; return x; }; WebGraphics.doubleBetween = function(randMin, randMax) { var result = 0; var bottom, top; if (randMin < randMax) { bottom = randMin; top = randMax; } else { bottom = randMax; top = randMin; } // rnd: random in range [0.0 -> 1.0) // RandBetween(bottom, top) = ((top - bottom) * rnd) + bottom // To avoid overflows, distribute the multiplication: // = top*rand - bottom*rand + bottom var rnd = Math.random(); var topMult = top * rnd; var bottomMult = bottom * rnd; if ((bottom >= 0.0) == (top >= 0.0)) { // Both are the same sign, do the subtraction first to avoid overflow. result = topMult - bottomMult; result = result + bottom; } else { // The signs differ, add bottom in first to avoid overflow. result = topMult + bottom; result = result - bottomMult; } return result; } WebGraphics.mix = function(x, y, a) { return x * (1 - a) + (y * a); }; WebGraphics.clamp = function(x, minVal, maxVal) { return Math.min(Math.max(x, minVal), maxVal); }; WebGraphics.sineMap = function(x) { return (Math.sin(x * Math.PI - (Math.PI / 2)) + 1) * 0.5; }; WebGraphics.createMatrix4 = function() { //creates and identity matrix, column-major matrix library, it is not necessary to use this to get an ortho matrix var obj = new Float32Array(16); obj[0] = 1; obj[1] = 0; obj[2] = 0; obj[3] = 0; obj[4] = 0; obj[5] = 1; obj[6] = 0; obj[7] = 0; obj[8] = 0; obj[9] = 0; obj[10] = 1; obj[11] = 0; obj[12] = 0; obj[13] = 0; obj[14] = 0; obj[15] = 1; return obj; }; WebGraphics.makeIdentityMatrix4 = function() { return WebGraphics.createMatrix4(); }; WebGraphics.makeOrthoMatrix4 = function(left, right, bottom, top, near, far) { var matrix = new Float32Array(16); var rl = right - left; var tb = top - bottom; var fn = far - near; matrix[0] = 2 / rl; matrix[1] = 0; matrix[2] = 0; matrix[3] = 0; matrix[4] = 0; matrix[5] = 2 / tb; matrix[6] = 0; matrix[7] = 0; matrix[8] = 0; matrix[9] = 0; matrix[10] = -2 /fn; matrix[11] = 0; matrix[12] = -(right + left) / rl; matrix[13] = -(top - bottom) / tb; matrix[14] = -(far + near) / fn; matrix[15] = 1; return matrix; }; WebGraphics.makeFrustumMatrix4 = function(left, right, bottom, top, near, far) { var rl = right - left; var tb = top - bottom; var fn = far - near; var m = new Float32Array(16); m[0] = (near * 2) / rl; //11 m[1] = 0; //21 m[2] = 0; //31 m[3] = 0; //41 m[4] = 0; //12 m[5] = (near * 2) / tb; //22 m[6] = 0; //32 m[7] = 0; //42 m[8] = (right + left) / rl; m[9] = (top + bottom) / tb; m[10] = -(far + near) / fn; m[11] = -1; m[12] = 0; m[13] = 0; m[14] = (-2 * far * near) / fn; m[15] = 0; return m; }; WebGraphics.makePerspectiveMatrix4 = function(fovy, aspect, near, far) { var top = near * Math.tan(fovy * Math.PI / 360.0); var right = top * aspect; return WebGraphics.makeFrustumMatrix4(-right, right, -top, top, near, far); }; WebGraphics.multiplyMatrix4 = function(a, b) { //a*b var m = new Float32Array(16); var a11 = a[0], a12 = a[4], a13 = a[8], a14 = a[12], a21 = a[1], a22 = a[5], a23 = a[9], a24 = a[13], a31 = a[2], a32 = a[6], a33 = a[10], a34 = a[14], a41 = a[3], a42 = a[7], a43 = a[11], a44 = a[15]; var b11 = b[0], b12 = b[4], b13 = b[8], b14 = b[12], b21 = b[1], b22 = b[5], b23 = b[9], b24 = b[13], b31 = b[2], b32 = b[6], b33 = b[10], b34 = b[14], b41 = b[3], b42 = b[7], b43 = b[11], b44 = b[15]; m[0] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41; m[4] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42; m[8] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43; m[12] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44; m[1] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41; m[5] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42; m[9] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43; m[13] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44; m[2] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41; m[6] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42; m[10] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43; m[14] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44; m[3] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41; m[7] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42; m[11] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43; m[15] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44; return m; }; WebGraphics.scaleMatrix4 = function(m4, sx, sy, sz) { var m = WebGraphics.createMatrix4(); m[0] = sx; m[5] = sy; m[10] = sz; return WebGraphics.multiplyMatrix4(m4, m); }; WebGraphics.translateMatrix4 = function(m4, tx, ty, tz) { var m = WebGraphics.createMatrix4(); m[12] = tx; m[13] = ty; m[14] = tz; return WebGraphics.multiplyMatrix4(m4, m); }; WebGraphics.rotateMatrix4AboutXYZ = function(m4, theta, x, y, z) { var point3d = WebGraphics.makePoint3D(x, y, z); point3d = WebGraphics.point3DNormalize(point3d); var ux = point3d.x; var uy = point3d.y; var uz = point3d.z; var cos = Math.cos(theta); var oneMinusCos = 1 - cos; var sin = Math.sin(theta); var m = WebGraphics.createMatrix4(); m[0] = cos + (ux * ux) * oneMinusCos; m[1] = ux * uy * oneMinusCos + (uz * sin); m[2] = uz * ux * oneMinusCos - uy * sin; m[4] = ux * uy * oneMinusCos - uz * sin; m[5] = cos + (uy * uy) * oneMinusCos; m[6] = uz * uy * oneMinusCos + ux * sin; m[8] = ux * uy * oneMinusCos + uy * sin; m[9] = uy * uz * oneMinusCos - ux * sin; m[10] = cos + (uz * uz) * oneMinusCos; return WebGraphics.multiplyMatrix4(m4, m); }; WebGraphics.colorWithHSBA = function(hue, saturation, brightness, alpha) { var hueTimesSix, frac, p1, p2, p3, red, blue, green; var obj = {"hue": hue, "saturation": saturation, "brightness": brightness, "alpha": alpha}; if (hue == 1.0) { hue = 0.0; } hueTimesSix = hue * 6.0; frac = hueTimesSix - Math.floor(hueTimesSix); p1 = brightness * (1-saturation); p2 = brightness * (1.0 - (saturation * frac)); p3 = brightness * (1.0 - (saturation * (1.0 - frac))); switch (parseInt(hueTimesSix)) { case 0: red = brightness; green = p3; blue = p1; break; case 1: red = p2; green = brightness; blue = p1; break; case 2: red = p1; green = brightness; blue = p3; break; case 3: red = p1; green = p2; blue = brightness; break; case 4: red = p3; green = p1; blue = brightness; break; case 5: red = brightness; green = p1; blue = p2; break; } obj.red = red; obj.blue = blue; obj.green = green; return obj; }; WebGraphics.makeMat3WithAffineTransform = function(affineTransform) { var obj = new Float32Array(9); obj[0] = affineTransform[0]; obj[1] = affineTransform[1]; obj[2] = 0; obj[3] = affineTransform[2]; obj[4] = affineTransform[3]; obj[5] = 0; obj[6] = affineTransform[4]; obj[7] = affineTransform[5]; obj[8] = 1; return obj; }; /* * High performance vector container for math * Copyright (c) 2011 Apple, Inc */ vector3 = function(vec) { this.create(vec); }; vector3.prototype = { create: function(vec) { var m = this.$matrix = {}; if (!vec) { m.m11 = 0; m.m12 = 0; m.m13 = 0; } else { m.m11 = vec[0]; m.m12 = vec[1]; m.m13 = vec[2]; } }, subtract: function(vec) { var m = this.$matrix; var mm = vec.$matrix; m.m11 -= mm.m11; m.m12 -= mm.m12; m.m13 -= mm.m13; }, add: function(vec) { var m = this.$matrix; var mm = vec.$matrix; m.m11 += mm.m11; m.m12 += mm.m12; m.m13 += mm.m13; }, normalize: function() { var m = this.$matrix; var length = Math.sqrt((m.m11 * m.m11) + (m.m12 * m.m12) + (m.m13 * m.m13)); if (length > 0) { m.m11 /= length; m.m12 /= length; m.m13 /= length; } }, scale: function(scalar) { var m = this.$matrix; m.m11 *= scalar; m.m12 *= scalar; m.m13 *= scalar; }, cross: function(vec) { var m = this.$matrix; var mm = vec.$matrix; var a1 = mm.m11, a2 = mm.m12, a3 = mm.m13; var m1 = m.m11, m2 = m.m12, m3 = m.m13; m.m11 = m2 * a3 - m3 * a2; m.m12 = m3 * a1 - m1 * a3; m.m13 = m1 * a2 - m2 * a1; }, getArray: function() { var m = this.$matrix; return [m.m11, m.m12, m.m13]; } }; // Matrix3, 3x3 Matrix Class // Matrix3 stores row-major order, simply transverse to get a webGL acceptable array Matrix3 = function() { this.identity(); }; Matrix3.prototype = { identity: function() { this.$matrix = { m11: 1, m12: 0, m13: 0, m21: 0, m22: 1, m23: 0, m31: 0, m32: 0, m33: 1 }; }, affineScale: function(sx, sy) { var m = this.$matrix; m.m11 = sx; m.m22 = sy; }, affineTranslate: function(tx, ty) { var m = this.$matrix; m.m13 = tx; m.m23 = ty; }, transformTranslate: function(tx, ty) { var matrix = new Matrix3(); matrix.affineTranslate(tx, ty); this.multiply(matrix.getArray()); }, multiply: function(mat) { var m = this.$matrix; var m0 = m.m11, m1 = m.m12, m2 = m.m13, m3 = m.m21, m4 = m.m22, m5 = m.m23, m6 = m.m31, m7 = m.m32, m8 = m.m33; m.m11 = m0 * mat[0] + m1 * mat[3] + m2 * mat[6]; m.m12 = m0 * mat[1] + m1 * mat[4] + m2 * mat[7]; m.m13 = m0 * mat[2] + m1 * mat[5] + m2 * mat[8]; m.m21 = m3 * mat[0] + m4 * mat[3] + m5 * mat[6]; m.m22 = m3 * mat[1] + m4 * mat[4] + m5 * mat[7]; m.m23 = m3 * mat[2] + m4 * mat[5] + m5 * mat[8]; m.m31 = m6 * mat[0] + m7 * mat[3] + m8 * mat[6]; m.m32 = m6 * mat[1] + m7 * mat[4] + m8 * mat[7]; m.m33 = m6 * mat[2] + m7 * mat[5] + m8 * mat[8]; }, getArray: function() { // this is row major order, for WebGL you'll need to transverse this var m = this.$matrix; return [m.m11, m.m12, m.m13, m.m21, m.m22, m.m23, m.m31, m.m32, m.m33]; }, getFloat32Array: function() { return new Float32Array(this.getArray()); }, getColumnMajorArray: function() { // this is row major order, for WebGL you'll need to transverse this var m = this.$matrix; return [m.m11, m.m21, m.m31, m.m12, m.m22, m.m32, m.m13, m.m23, m.m33 ]; }, getColumnMajorFloat32Array: function() { return new Float32Array(this.getColumnMajorArray()); } }; Matrix4 = function() { this.identity(); }; Matrix4.prototype = { identity: function() { this.$matrix = { m11: 1, m12: 0, m13: 0, m14: 0, m21: 0, m22: 1, m23: 0, m24: 0, m31: 0, m32: 0, m33: 1, m34: 0, m41: 0, m42: 0, m43: 0, m44: 1 }; }, translate: function(x, y, z) { var matrix = new Matrix4(); var m = matrix.$matrix; m.m14 = x; m.m24 = y; m.m34 = z; this.multiply(matrix); /* * this.$matrix.m41 = this.$matrix.m11*x + this.$matrix.m21*y + * this.$matrix.m31*z + this.$matrix.m41; this.$matrix.m42 = * this.$matrix.m12*x + this.$matrix.m22*y + this.$matrix.m32*z + * this.$matrix.m42; this.$matrix.m43 = this.$matrix.m13*x + * this.$matrix.m23*y + this.$matrix.m33*z + this.$matrix.m43; * this.$matrix.m44 = this.$matrix.m14*x + this.$matrix.m24*y + * this.$matrix.m34*z + this.$matrix.m44; */ }, scale: function(x, y, z) { var matrix = new Matrix4(); var m = matrix.$matrix; m.m11 = x; m.m22 = y; m.m33 = z; this.multiply(matrix); }, multiply: function(mat) { var m = this.$matrix; var mm = mat.$matrix; var m11 = (mm.m11 * m.m11 + mm.m21 * m.m12 + mm.m31 * m.m13 + mm.m41 * m.m14); var m12 = (mm.m12 * m.m11 + mm.m22 * m.m12 + mm.m32 * m.m13 + mm.m42 * m.m14); var m13 = (mm.m13 * m.m11 + mm.m23 * m.m12 + mm.m33 * m.m13 + mm.m43 * m.m14); var m14 = (mm.m14 * m.m11 + mm.m24 * m.m12 + mm.m34 * m.m13 + mm.m44 * m.m14); var m21 = (mm.m11 * m.m21 + mm.m21 * m.m22 + mm.m31 * m.m23 + mm.m41 * m.m24); var m22 = (mm.m12 * m.m21 + mm.m22 * m.m22 + mm.m32 * m.m23 + mm.m42 * m.m24); var m23 = (mm.m13 * m.m21 + mm.m23 * m.m22 + mm.m33 * m.m23 + mm.m43 * m.m24); var m24 = (mm.m14 * m.m21 + mm.m24 * m.m22 + mm.m34 * m.m23 + mm.m44 * m.m24); var m31 = (mm.m11 * m.m31 + mm.m21 * m.m32 + mm.m31 * m.m33 + mm.m41 * m.m34); var m32 = (mm.m12 * m.m31 + mm.m22 * m.m32 + mm.m32 * m.m33 + mm.m42 * m.m34); var m33 = (mm.m13 * m.m31 + mm.m23 * m.m32 + mm.m33 * m.m33 + mm.m43 * m.m34); var m34 = (mm.m14 * m.m31 + mm.m24 * m.m32 + mm.m34 * m.m33 + mm.m44 * m.m34); var m41 = (mm.m11 * m.m41 + mm.m21 * m.m42 + mm.m31 * m.m43 + mm.m41 * m.m44); var m42 = (mm.m12 * m.m41 + mm.m22 * m.m42 + mm.m32 * m.m43 + mm.m42 * m.m44); var m43 = (mm.m13 * m.m41 + mm.m23 * m.m42 + mm.m33 * m.m43 + mm.m43 * m.m44); var m44 = (mm.m14 * m.m41 + mm.m24 * m.m42 + mm.m34 * m.m43 + mm.m44 * m.m44); m.m11 = m11; m.m12 = m12; m.m13 = m13; m.m14 = m14; m.m21 = m21; m.m22 = m22; m.m23 = m23; m.m24 = m24; m.m31 = m31; m.m32 = m32; m.m33 = m33; m.m34 = m34; m.m41 = m41; m.m42 = m42; m.m43 = m43; m.m44 = m44; }, perspective: function(fovy, aspect, near, far) { var top = near * Math.tan(fovy * Math.PI / 360.0); var right = top * aspect; return this.frustum(-right, right, -top, top, near, far); }, ortho: function(left, right, bottom, top, near, far) { var rl = right - left; var tb = top - bottom; var fn = far - near; var m = this.$matrix; m.m11 = 2 / rl; m.m12 = 0; m.m13 = 0; m.m14 = -(right + left) / rl; m.m21 = 0; m.m22 = 2 / tb; m.m23 = 0; m.m24 = -(top + bottom) / tb; m.m31 = 0; m.m32 = 0; m.m33 = -2 / fn; m.m34 = -(far + near) / fn; m.m41 = 0; m.m42 = 0; m.m43 = 0; m.m44 = 1; }, frustum: function(left, right, bottom, top, near, far) { var rl = right - left; var tb = top - bottom; var fn = far - near; var m = this.$matrix; m.m11 = (near * 2) / rl; m.m12 = 0; m.m13 = (right + left) / rl; m.m14 = 0; m.m21 = 0; m.m22 = (near * 2) / tb; m.m23 = (top + bottom) / tb; m.m24 = 0; m.m31 = 0; m.m32 = 0; m.m33 = -(far + near) / fn; m.m34 = (-2 * far * near) / fn; m.m41 = 0; m.m42 = 0; m.m43 = -1; m.m44 = 0; }, getArray: function() { // this is row major order, for WebGL you'll need to transverse this var m = this.$matrix; return [m.m11, m.m12, m.m13, m.m14, m.m21, m.m22, m.m23, m.m24, m.m31, m.m32, m.m33, m.m34, m.m41, m.m42, m.m43, m.m44]; }, getFloat32Array: function() { return new Float32Array(this.getArray()); }, getColumnMajorArray: function() { // this is row major order, for WebGL you'll need to transverse this var m = this.$matrix; return [m.m11, m.m21, m.m31, m.m41, m.m12, m.m22, m.m32, m.m42, m.m13, m.m23, m.m33, m.m43, m.m14, m.m24, m.m34, m.m44]; }, getColumnMajorFloat32Array: function() { return new Float32Array(this.getColumnMajorArray()); } }; function TSUMix(a, b, x) { return a + (b - a) * x; } //sinusoidal timing function function TSUSineMap(x) { return (Math.sin(x * Math.PI - (Math.PI / 2)) + 1) * 0.5; } //function for Twist sizing function TwistFX(location, percent) { var twist = 4.0 / 10.25; var x = (1 + twist) * percent - twist * location; if (x < 0) { return 0; } else if (x > 1) { return 1; } else { return TSUSineMap(x); } } //CGAffineTransformMakeRotation function CGAffineTransformMakeRotation(angle) { var sine, consine; sine = Math.sin(angle); cosine = Math.cos(angle); return [cosine, sine, -sine, cosine, 0, 0]; } //CGAffineTransformEqualToTransform function CGAffineTransformEqualToTransform(t1, t2) { return t1.a === t2.a && t1.b === t2.b && t1.c === t2.c && t1.d === t2.d && t1.tx === t2.tx && t1.ty === t2.ty; } //CATransform3DEqualToTransform function CATransform3DEqualToTransform(a, b) { var result = a[0] === b[0] && a[1] === b[1] && a[2] === b[2] && a[3] === b[3] && a[4] === b[4] && a[5] === b[5] && a[6] === b[6] && a[7] === b[7] && a[8] === b[8] && a[9] === b[9] && a[10] === b[10] && a[11] === b[11] && a[12] === b[12] && a[13] === b[13] && a[14] === b[14] && a[15] === b[15]; return result; } //CGPointMake function CGPointMake(x, y) { var p = { x: x, y: y }; return p; } //CGRectIntersection function CGRectIntersection(r1, r2) { var r = { "origin": { "x": 0, "y": 0 }, "size": { "width": 0, "height": 0 } }; var x1, x2, y1, y2; x1 = Math.max(r1.origin.x, r2.origin.x); x2 = Math.min(r1.origin.x + r1.size.width, r2.origin.x + r2.size.width); if (x1 > x2) { return r; } y1 = Math.max(r1.origin.y, r2.origin.y); y2 = Math.min(r1.origin.y + r1.size.height, r2.origin.y + r2.size.height); if (y1 > y2) { return r; } r.origin.x = x1; r.size.width = x2 - x1; r.origin.y = y1; r.size.height = y2 - y1; return r; } // CGRectIntegral function CGRectIntegral(rect) { var r = { "origin": { "x": 0, "y": 0 }, "size": { "width": 0, "height": 0 } }; r.origin.x = Math.floor(rect.origin.x); r.origin.y = Math.floor(rect.origin.y); r.size.width = Math.ceil(rect.origin.x + rect.size.width) - r.origin.x; r.size.height = Math.ceil(rect.origin.y + rect.size.height) - r.origin.y; return r; } // CGRectGetMinX function CGRectGetMinX(rect) { return rect.origin.x; } // CGRectGetMinY function CGRectGetMinY(rect) { return rect.origin.y; } // CGRectGetMidX function CGRectGetMidX(rect) { return rect.origin.x + rect.size.width / 2; } // CGRectGetMidY function CGRectGetMidY(rect) { return rect.origin.y + rect.size.height / 2; } // CGRectGetMaxX function CGRectGetMaxX(rect) { return rect.origin.x + rect.size.width; } // CGRectGetMaxY function CGRectGetMaxY(rect) { return rect.origin.y + rect.size.height; } // CGRectEqualToRect function CGRectEqualToRect(rect1, rect2) { return (rect1.origin.x == rect2.origin.x) && (rect1.origin.y == rect2.origin.y) && (rect1.size.width == rect2.size.width) && (rect1.size.height == rect2.size.height); } // CGRectMake function CGRectMake(x, y, width, height) { var r = { "origin": { "x": x, "y": y }, "size": { "width": width, "height": height } }; return r; } // CGSizeMake function CGSizeMake(width, height) { var sizeOut = {}; sizeOut.width = width; sizeOut.height = height; return sizeOut; } // CGSizeEqualToSize function CGSizeEqualToSize (size1, size2) { return size1.width === size2.width && size1.height === size2.height; } // CGSizeZero var CGSizeZero = { "width": 0, "height": 0 }; // CGRectZero var CGRectZero = { "origin": { "x": 0, "y": 0 }, "size": { "width": 0, "height": 0 } }; // TSDRectUnit var TSDRectUnit = { "origin": { "x": 0, "y": 0 }, "size": { "width": 1, "height": 1 } }; //TSDMixFloats function TSDMixFloats(a, b, fraction) { return a * (1.0 - fraction) + b * fraction; } // TSDCenterOfRect function TSDCenterOfRect(rect) { return WebGraphics.makePoint(CGRectGetMidX(rect), CGRectGetMidY(rect)); } // TSDPointFromNormalizedRect function TSDPointFromNormalizedRect(pt, rect) { return WebGraphics.makePoint(rect.origin.x + pt.x * rect.size.width, rect.origin.y + pt.y * rect.size.height); } // TSDRectWithPoints function TSDRectWithPoints(a, b) { // smallest rect enclosing two points var minX = Math.min(a.x, b.x); var maxX = Math.max(a.x, b.x); var minY = Math.min(a.y, b.y); var maxY = Math.max(a.y, b.y); return CGRectMake(minX, minY, maxX - minX, maxY - minY); } function TSDGLColor(r, g, b, a) { var color = { r: r, g: g, b: b, a: a }; return color; } var TSD8bitColorDenominator = 0.003906402593851; /// Creates a TSDGLColor4f from a 32-bit BGRA-encoded unsigned int function TSDGLColor4fMakeWithUInt(anInt) { var color = WebGraphics.makePoint4D( ((anInt & 0x00ff0000) >> 16) * TSD8bitColorDenominator, ((anInt & 0x0000ff00) >> 8) * TSD8bitColorDenominator, ((anInt & 0x000000ff)) * TSD8bitColorDenominator, ((anInt & 0xff000000) >> 24) * TSD8bitColorDenominator ); return color; } // TSUReverseSquare function TSUReverseSquare(x) { var reverse = 1.0 - x; return 1.0 - reverse * reverse; } window.requestAnimFrame = (function() { return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function(callback, element) { window.setTimeout(callback, 1000 / 60); }; })();