├── LICENSE.md ├── README.md ├── src ├── wglu-debug-geometry.js ├── wglu-preserve-state.js ├── wglu-program.js ├── wglu-stats.js ├── wglu-texture.js └── wglu-url.js ├── stats-test.html └── third-party └── gl-matrix-min.js /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Brandon Jones 2 | 3 | This software is provided 'as-is', without any express or implied 4 | warranty. In no event will the authors be held liable for any damages 5 | arising from the use of this software. 6 | 7 | Permission is granted to anyone to use this software for any purpose, 8 | including commercial applications, and to alter it and redistribute it 9 | freely, subject to the following restrictions: 10 | 11 | 1. The origin of this software must not be misrepresented; you must not 12 | claim that you wrote the original software. If you use this software 13 | in a product, an acknowledgment in the product documentation would be 14 | appreciated but is not required. 15 | 16 | 2. Altered source versions must be plainly marked as such, and must not 17 | be misrepresented as being the original software. 18 | 19 | 3. This notice may not be removed or altered from any source distribution. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WebGL Utils 2 | A collection of lightweight utility libraries to smooth over some of the more 3 | tedious parts of building a WebGL app. These are designed to be used 4 | individually with little to no interdependence and no dependencies on external 5 | libraries. 6 | 7 | This is not a Framework or Engine, just some functions that make WebGL a little 8 | easier. I'll probably add to it over time, but don't expect too much in the way 9 | of ongoing support. -------------------------------------------------------------------------------- /src/wglu-debug-geometry.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2016, Brandon Jones. 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | var WGLUDebugGeometry = (function() { 24 | 25 | "use strict"; 26 | 27 | var debugGeomVS = [ 28 | "uniform mat4 projectionMat;", 29 | "uniform mat4 viewMat;", 30 | "uniform mat4 modelMat;", 31 | "attribute vec3 position;", 32 | 33 | "void main() {", 34 | " gl_Position = projectionMat * viewMat * modelMat * vec4( position, 1.0 );", 35 | "}", 36 | ].join("\n"); 37 | 38 | var debugGeomFS = [ 39 | "precision mediump float;", 40 | "uniform vec4 color;", 41 | 42 | "void main() {", 43 | " gl_FragColor = color;", 44 | "}", 45 | ].join("\n"); 46 | 47 | var DebugGeometry = function(gl) { 48 | this.gl = gl; 49 | 50 | this.projMat = mat4.create(); 51 | this.viewMat = mat4.create(); 52 | this.modelMat = mat4.create(); 53 | 54 | this.program = new WGLUProgram(gl); 55 | this.program.attachShaderSource(debugGeomVS, gl.VERTEX_SHADER); 56 | this.program.attachShaderSource(debugGeomFS, gl.FRAGMENT_SHADER); 57 | this.program.bindAttribLocation({ position: 0 }); 58 | this.program.link(); 59 | 60 | var verts = []; 61 | var indices = []; 62 | 63 | // 64 | // Cube Geometry 65 | // 66 | this.cubeIndexOffset = indices.length; 67 | 68 | var size = 0.5; 69 | // Bottom 70 | var idx = verts.length / 3.0; 71 | indices.push(idx, idx+1, idx+2); 72 | indices.push(idx, idx+2, idx+3); 73 | 74 | verts.push(-size, -size, -size); 75 | verts.push(+size, -size, -size); 76 | verts.push(+size, -size, +size); 77 | verts.push(-size, -size, +size); 78 | 79 | // Top 80 | idx = verts.length / 3.0; 81 | indices.push(idx, idx+2, idx+1); 82 | indices.push(idx, idx+3, idx+2); 83 | 84 | verts.push(-size, +size, -size); 85 | verts.push(+size, +size, -size); 86 | verts.push(+size, +size, +size); 87 | verts.push(-size, +size, +size); 88 | 89 | // Left 90 | idx = verts.length / 3.0; 91 | indices.push(idx, idx+2, idx+1); 92 | indices.push(idx, idx+3, idx+2); 93 | 94 | verts.push(-size, -size, -size); 95 | verts.push(-size, +size, -size); 96 | verts.push(-size, +size, +size); 97 | verts.push(-size, -size, +size); 98 | 99 | // Right 100 | idx = verts.length / 3.0; 101 | indices.push(idx, idx+1, idx+2); 102 | indices.push(idx, idx+2, idx+3); 103 | 104 | verts.push(+size, -size, -size); 105 | verts.push(+size, +size, -size); 106 | verts.push(+size, +size, +size); 107 | verts.push(+size, -size, +size); 108 | 109 | // Back 110 | idx = verts.length / 3.0; 111 | indices.push(idx, idx+2, idx+1); 112 | indices.push(idx, idx+3, idx+2); 113 | 114 | verts.push(-size, -size, -size); 115 | verts.push(+size, -size, -size); 116 | verts.push(+size, +size, -size); 117 | verts.push(-size, +size, -size); 118 | 119 | // Front 120 | idx = verts.length / 3.0; 121 | indices.push(idx, idx+1, idx+2); 122 | indices.push(idx, idx+2, idx+3); 123 | 124 | verts.push(-size, -size, +size); 125 | verts.push(+size, -size, +size); 126 | verts.push(+size, +size, +size); 127 | verts.push(-size, +size, +size); 128 | 129 | this.cubeIndexCount = indices.length - this.cubeIndexOffset; 130 | 131 | // 132 | // Rect geometry 133 | // 134 | this.rectIndexOffset = indices.length; 135 | 136 | idx = verts.length / 3.0; 137 | indices.push(idx, idx+1, idx+2, idx+3, idx); 138 | 139 | verts.push(0, 0, 0); 140 | verts.push(1, 0, 0); 141 | verts.push(1, 1, 0); 142 | verts.push(0, 1, 0); 143 | 144 | this.rectIndexCount = indices.length - this.rectIndexOffset; 145 | 146 | this.vertBuffer = gl.createBuffer(); 147 | gl.bindBuffer(gl.ARRAY_BUFFER, this.vertBuffer); 148 | gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(verts), gl.STATIC_DRAW); 149 | 150 | this.indexBuffer = gl.createBuffer(); 151 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); 152 | gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW); 153 | }; 154 | 155 | DebugGeometry.prototype.bind = function(projectionMat, viewMat) { 156 | var gl = this.gl; 157 | var program = this.program; 158 | 159 | program.use(); 160 | 161 | gl.uniformMatrix4fv(program.uniform.projectionMat, false, projectionMat); 162 | gl.uniformMatrix4fv(program.uniform.viewMat, false, viewMat); 163 | 164 | gl.bindBuffer(gl.ARRAY_BUFFER, this.vertBuffer); 165 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); 166 | 167 | gl.enableVertexAttribArray(program.attrib.position); 168 | gl.enableVertexAttribArray(program.attrib.texCoord); 169 | 170 | gl.vertexAttribPointer(program.attrib.position, 3, gl.FLOAT, false, 12, 0); 171 | }; 172 | 173 | DebugGeometry.prototype.bindOrtho = function() { 174 | mat4.ortho(this.projMat, 0, this.gl.canvas.width, this.gl.canvas.height, 0, 0.1, 1024); 175 | mat4.identity(this.viewMat); 176 | this.bind(this.projMat, this.viewMat); 177 | }; 178 | 179 | DebugGeometry.prototype._bindUniforms = function(orientation, position, scale, color) { 180 | if (!position) { position = [0, 0, 0]; } 181 | if (!orientation) { orientation = [0, 0, 0, 1]; } 182 | if (!scale) { scale = [1, 1, 1]; } 183 | if (!color) { color = [1, 0, 0, 1]; } 184 | 185 | mat4.fromRotationTranslationScale(this.modelMat, orientation, position, scale); 186 | this.gl.uniformMatrix4fv(this.program.uniform.modelMat, false, this.modelMat); 187 | this.gl.uniform4fv(this.program.uniform.color, color); 188 | }; 189 | 190 | DebugGeometry.prototype.drawCube = function(orientation, position, size, color) { 191 | var gl = this.gl; 192 | 193 | if (!size) { size = 1; } 194 | this._bindUniforms(orientation, position, [size, size, size], color); 195 | gl.drawElements(gl.TRIANGLES, this.cubeIndexCount, gl.UNSIGNED_SHORT, this.cubeIndexOffset * 2.0); 196 | }; 197 | 198 | DebugGeometry.prototype.drawBox = function(orientation, position, scale, color) { 199 | var gl = this.gl; 200 | 201 | this._bindUniforms(orientation, position, scale, color); 202 | gl.drawElements(gl.TRIANGLES, this.cubeIndexCount, gl.UNSIGNED_SHORT, this.cubeIndexOffset * 2.0); 203 | }; 204 | 205 | DebugGeometry.prototype.drawRect = function(x, y, width, height, color) { 206 | var gl = this.gl; 207 | 208 | this._bindUniforms(null, [x, y, -1], [width, height, 1], color); 209 | gl.drawElements(gl.LINE_STRIP, this.rectIndexCount, gl.UNSIGNED_SHORT, this.rectIndexOffset * 2.0); 210 | }; 211 | 212 | return DebugGeometry; 213 | })(); 214 | -------------------------------------------------------------------------------- /src/wglu-preserve-state.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2016, Brandon Jones. 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | /* 24 | Caches specified GL state, runs a callback, and restores the cached state when 25 | done. 26 | 27 | Example usage: 28 | 29 | var savedState = [ 30 | gl.ARRAY_BUFFER_BINDING, 31 | 32 | // TEXTURE_BINDING_2D or _CUBE_MAP must always be followed by the texure unit. 33 | gl.TEXTURE_BINDING_2D, gl.TEXTURE0, 34 | 35 | gl.CLEAR_COLOR, 36 | ]; 37 | // After this call the array buffer, texture unit 0, active texture, and clear 38 | // color will be restored. The viewport will remain changed, however, because 39 | // gl.VIEWPORT was not included in the savedState list. 40 | WGLUPreserveGLState(gl, savedState, function(gl) { 41 | gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight); 42 | 43 | gl.bindBuffer(gl.ARRAY_BUFFER, buffer); 44 | gl.bufferData(gl.ARRAY_BUFFER, ....); 45 | 46 | gl.activeTexture(gl.TEXTURE0); 47 | gl.bindTexture(gl.TEXTURE_2D, texture); 48 | gl.texImage2D(gl.TEXTURE_2D, ...); 49 | 50 | gl.clearColor(1, 0, 0, 1); 51 | gl.clear(gl.COLOR_BUFFER_BIT); 52 | }); 53 | 54 | Note that this is not intended to be fast. Managing state in your own code to 55 | avoid redundant state setting and querying will always be faster. This function 56 | is most useful for cases where you may not have full control over the WebGL 57 | calls being made, such as tooling or effect injectors. 58 | */ 59 | 60 | function WGLUPreserveGLState(gl, bindings, callback) { 61 | if (!bindings) { 62 | callback(gl); 63 | return; 64 | } 65 | 66 | var boundValues = []; 67 | 68 | var activeTexture = null; 69 | for (var i = 0; i < bindings.length; ++i) { 70 | var binding = bindings[i]; 71 | switch (binding) { 72 | case gl.TEXTURE_BINDING_2D: 73 | case gl.TEXTURE_BINDING_CUBE_MAP: 74 | var textureUnit = bindings[++i]; 75 | if (textureUnit < gl.TEXTURE0 || textureUnit > gl.TEXTURE31) { 76 | console.error("TEXTURE_BINDING_2D or TEXTURE_BINDING_CUBE_MAP must be followed by a valid texture unit"); 77 | boundValues.push(null, null); 78 | break; 79 | } 80 | if (!activeTexture) { 81 | activeTexture = gl.getParameter(gl.ACTIVE_TEXTURE); 82 | } 83 | gl.activeTexture(textureUnit); 84 | boundValues.push(gl.getParameter(binding), null); 85 | break; 86 | case gl.ACTIVE_TEXTURE: 87 | activeTexture = gl.getParameter(gl.ACTIVE_TEXTURE); 88 | boundValues.push(null); 89 | break; 90 | default: 91 | boundValues.push(gl.getParameter(binding)); 92 | break; 93 | } 94 | } 95 | 96 | callback(gl); 97 | 98 | for (var i = 0; i < bindings.length; ++i) { 99 | var binding = bindings[i]; 100 | var boundValue = boundValues[i]; 101 | switch (binding) { 102 | case gl.ACTIVE_TEXTURE: 103 | break; // Ignore this binding, since we special-case it to happen last. 104 | case gl.ARRAY_BUFFER_BINDING: 105 | gl.bindBuffer(gl.ARRAY_BUFFER, boundValue); 106 | break; 107 | case gl.COLOR_CLEAR_VALUE: 108 | gl.clearColor(boundValue[0], boundValue[1], boundValue[2], boundValue[3]); 109 | break; 110 | case gl.COLOR_WRITEMASK: 111 | gl.colorMask(boundValue[0], boundValue[1], boundValue[2], boundValue[3]); 112 | break; 113 | case gl.CURRENT_PROGRAM: 114 | gl.useProgram(boundValue); 115 | break; 116 | case gl.ELEMENT_ARRAY_BUFFER_BINDING: 117 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, boundValue); 118 | break; 119 | case gl.FRAMEBUFFER_BINDING: 120 | gl.bindFramebuffer(gl.FRAMEBUFFER, boundValue); 121 | break; 122 | case gl.RENDERBUFFER_BINDING: 123 | gl.bindRenderbuffer(gl.RENDERBUFFER, boundValue); 124 | break; 125 | case gl.TEXTURE_BINDING_2D: 126 | var textureUnit = bindings[++i]; 127 | if (textureUnit < gl.TEXTURE0 || textureUnit > gl.TEXTURE31) 128 | break; 129 | gl.activeTexture(textureUnit); 130 | gl.bindTexture(gl.TEXTURE_2D, boundValue); 131 | break; 132 | case gl.TEXTURE_BINDING_CUBE_MAP: 133 | var textureUnit = bindings[++i]; 134 | if (textureUnit < gl.TEXTURE0 || textureUnit > gl.TEXTURE31) 135 | break; 136 | gl.activeTexture(textureUnit); 137 | gl.bindTexture(gl.TEXTURE_CUBE_MAP, boundValue); 138 | break; 139 | case gl.VIEWPORT: 140 | gl.viewport(boundValue[0], boundValue[1], boundValue[2], boundValue[3]); 141 | break; 142 | case gl.BLEND: 143 | case gl.CULL_FACE: 144 | case gl.DEPTH_TEST: 145 | case gl.SCISSOR_TEST: 146 | case gl.STENCIL_TEST: 147 | if (boundValue) { 148 | gl.enable(binding); 149 | } else { 150 | gl.disable(binding); 151 | } 152 | break; 153 | default: 154 | console.log("No GL restore behavior for 0x" + binding.toString(16)); 155 | break; 156 | } 157 | 158 | if (activeTexture) { 159 | gl.activeTexture(activeTexture); 160 | } 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /src/wglu-program.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015, Brandon Jones. 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | /* 24 | Utility class to make loading shader programs easier. Does all the error 25 | checking you typically want, automatically queries uniform and attribute 26 | locations, and attempts to take advantage of some browser's ability to link 27 | asynchronously by not querying any information from the program until it's 28 | first use. 29 | */ 30 | var WGLUProgram = (function() { 31 | 32 | "use strict"; 33 | 34 | // Attempts to allow the browser to asynchronously compile and link 35 | var Program = function(gl) { 36 | this.gl = gl; 37 | this.program = gl.createProgram(); 38 | this.attrib = null; 39 | this.uniform = null; 40 | 41 | this._firstUse = true; 42 | this._vertexShader = null; 43 | this._fragmentShader = null; 44 | } 45 | 46 | Program.prototype.attachShaderSource = function(source, type) { 47 | var gl = this.gl; 48 | var shader; 49 | 50 | switch (type) { 51 | case gl.VERTEX_SHADER: 52 | this._vertexShader = gl.createShader(type); 53 | shader = this._vertexShader; 54 | break; 55 | case gl.FRAGMENT_SHADER: 56 | this._fragmentShader = gl.createShader(type); 57 | shader = this._fragmentShader; 58 | break; 59 | default: 60 | console.Error("Invalid Shader Type:", type); 61 | return; 62 | } 63 | 64 | gl.attachShader(this.program, shader); 65 | gl.shaderSource(shader, source); 66 | gl.compileShader(shader); 67 | } 68 | 69 | Program.prototype.attachShaderSourceFromXHR = function(url, type) { 70 | var self = this; 71 | return new Promise(function(resolve, reject) { 72 | var xhr = new XMLHttpRequest(); 73 | xhr.addEventListener("load", function (ev) { 74 | if (xhr.status == 200) { 75 | self.attachShaderSource(xhr.response, type); 76 | resolve(); 77 | } else { 78 | reject(xhr.statusText); 79 | } 80 | }, false); 81 | xhr.open("GET", url, true); 82 | xhr.send(null); 83 | }); 84 | } 85 | 86 | Program.prototype.attachShaderSourceFromTag = function(tagId, type) { 87 | var shaderTag = document.getElementById(tagId); 88 | if (!shaderTag) { 89 | console.error("Shader source tag not found:", tagId); 90 | return; 91 | } 92 | 93 | if (!type) { 94 | if (shaderTag.type == "x-shader/x-vertex") { 95 | type = this.gl.VERTEX_SHADER; 96 | } else if (shaderTag.type == "x-shader/x-fragment") { 97 | type = this.gl.FRAGMENT_SHADER; 98 | } else { 99 | console.error("Invalid Shader Type:", shaderTag.type); 100 | return; 101 | } 102 | } 103 | 104 | var src = ""; 105 | var k = shaderTag.firstChild; 106 | while (k) { 107 | if (k.nodeType == 3) { 108 | src += k.textContent; 109 | } 110 | k = k.nextSibling; 111 | } 112 | this.attachShaderSource(src, type); 113 | } 114 | 115 | Program.prototype.bindAttribLocation = function(attribLocationMap) { 116 | var gl = this.gl; 117 | 118 | if (attribLocationMap) { 119 | this.attrib = {}; 120 | for (var attribName in attribLocationMap) { 121 | gl.bindAttribLocation(this.program, attribLocationMap[attribName], attribName); 122 | this.attrib[attribName] = attribLocationMap[attribName]; 123 | } 124 | } 125 | } 126 | 127 | Program.prototype.transformFeedbackVaryings = function(varyings, type) { 128 | gl.transformFeedbackVaryings(this.program, varyings, type); 129 | } 130 | 131 | Program.prototype.link = function() { 132 | this.gl.linkProgram(this.program); 133 | } 134 | 135 | Program.prototype.use = function() { 136 | var gl = this.gl; 137 | 138 | // If this is the first time the program has been used do all the error checking and 139 | // attrib/uniform querying needed. 140 | if (this._firstUse) { 141 | if (!gl.getProgramParameter(this.program, gl.LINK_STATUS)) { 142 | if (this._vertexShader && !gl.getShaderParameter(this._vertexShader, gl.COMPILE_STATUS)) { 143 | console.error("Vertex shader compile error:", gl.getShaderInfoLog(this._vertexShader)); 144 | } else if (this._fragmentShader && !gl.getShaderParameter(this._fragmentShader, gl.COMPILE_STATUS)) { 145 | console.error("Fragment shader compile error:", gl.getShaderInfoLog(this._fragmentShader)); 146 | } else { 147 | console.error("Program link error:", gl.getProgramInfoLog(this.program)); 148 | } 149 | gl.deleteProgram(this.program); 150 | this.program = null; 151 | } else { 152 | if (!this.attrib) { 153 | this.attrib = {}; 154 | var attribCount = gl.getProgramParameter(this.program, gl.ACTIVE_ATTRIBUTES); 155 | for (var i = 0; i < attribCount; i++) { 156 | var attribInfo = gl.getActiveAttrib(this.program, i); 157 | this.attrib[attribInfo.name] = gl.getAttribLocation(this.program, attribInfo.name); 158 | } 159 | } 160 | 161 | this.uniform = {}; 162 | var uniformCount = gl.getProgramParameter(this.program, gl.ACTIVE_UNIFORMS); 163 | var uniformName = ""; 164 | for (var i = 0; i < uniformCount; i++) { 165 | var uniformInfo = gl.getActiveUniform(this.program, i); 166 | uniformName = uniformInfo.name.replace("[0]", ""); 167 | this.uniform[uniformName] = gl.getUniformLocation(this.program, uniformName); 168 | } 169 | } 170 | gl.deleteShader(this._vertexShader); 171 | gl.deleteShader(this._fragmentShader); 172 | this._firstUse = false; 173 | } 174 | 175 | gl.useProgram(this.program); 176 | } 177 | 178 | return Program; 179 | })(); 180 | -------------------------------------------------------------------------------- /src/wglu-stats.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2016, Brandon Jones. 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | /* 24 | Heavily inspired by Mr. Doobs stats.js, this FPS counter is rendered completely 25 | with WebGL, allowing it to be shown in cases where overlaid HTML elements aren't 26 | usable (like WebVR), or if you want the FPS counter to be rendered as part of 27 | your scene. 28 | 29 | See stats-test.html for basic usage. 30 | */ 31 | var WGLUStats = (function() { 32 | 33 | "use strict"; 34 | 35 | //-------------------- 36 | // glMatrix functions 37 | //-------------------- 38 | 39 | // These functions have been copied here from glMatrix (glmatrix.net) to allow 40 | // this file to run standalone. 41 | 42 | var mat4_identity = function(out) { 43 | out[0] = 1; 44 | out[1] = 0; 45 | out[2] = 0; 46 | out[3] = 0; 47 | out[4] = 0; 48 | out[5] = 1; 49 | out[6] = 0; 50 | out[7] = 0; 51 | out[8] = 0; 52 | out[9] = 0; 53 | out[10] = 1; 54 | out[11] = 0; 55 | out[12] = 0; 56 | out[13] = 0; 57 | out[14] = 0; 58 | out[15] = 1; 59 | return out; 60 | }; 61 | 62 | var mat4_multiply = function (out, a, b) { 63 | var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], 64 | a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], 65 | a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], 66 | a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15]; 67 | 68 | // Cache only the current line of the second matrix 69 | var b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3]; 70 | out[0] = b0*a00 + b1*a10 + b2*a20 + b3*a30; 71 | out[1] = b0*a01 + b1*a11 + b2*a21 + b3*a31; 72 | out[2] = b0*a02 + b1*a12 + b2*a22 + b3*a32; 73 | out[3] = b0*a03 + b1*a13 + b2*a23 + b3*a33; 74 | 75 | b0 = b[4]; b1 = b[5]; b2 = b[6]; b3 = b[7]; 76 | out[4] = b0*a00 + b1*a10 + b2*a20 + b3*a30; 77 | out[5] = b0*a01 + b1*a11 + b2*a21 + b3*a31; 78 | out[6] = b0*a02 + b1*a12 + b2*a22 + b3*a32; 79 | out[7] = b0*a03 + b1*a13 + b2*a23 + b3*a33; 80 | 81 | b0 = b[8]; b1 = b[9]; b2 = b[10]; b3 = b[11]; 82 | out[8] = b0*a00 + b1*a10 + b2*a20 + b3*a30; 83 | out[9] = b0*a01 + b1*a11 + b2*a21 + b3*a31; 84 | out[10] = b0*a02 + b1*a12 + b2*a22 + b3*a32; 85 | out[11] = b0*a03 + b1*a13 + b2*a23 + b3*a33; 86 | 87 | b0 = b[12]; b1 = b[13]; b2 = b[14]; b3 = b[15]; 88 | out[12] = b0*a00 + b1*a10 + b2*a20 + b3*a30; 89 | out[13] = b0*a01 + b1*a11 + b2*a21 + b3*a31; 90 | out[14] = b0*a02 + b1*a12 + b2*a22 + b3*a32; 91 | out[15] = b0*a03 + b1*a13 + b2*a23 + b3*a33; 92 | return out; 93 | }; 94 | 95 | var mat4_fromTranslation = function(out, v) { 96 | out[0] = 1; 97 | out[1] = 0; 98 | out[2] = 0; 99 | out[3] = 0; 100 | out[4] = 0; 101 | out[5] = 1; 102 | out[6] = 0; 103 | out[7] = 0; 104 | out[8] = 0; 105 | out[9] = 0; 106 | out[10] = 1; 107 | out[11] = 0; 108 | out[12] = v[0]; 109 | out[13] = v[1]; 110 | out[14] = v[2]; 111 | out[15] = 1; 112 | return out; 113 | }; 114 | 115 | var mat4_ortho = function (out, left, right, bottom, top, near, far) { 116 | var lr = 1 / (left - right), 117 | bt = 1 / (bottom - top), 118 | nf = 1 / (near - far); 119 | out[0] = -2 * lr; 120 | out[1] = 0; 121 | out[2] = 0; 122 | out[3] = 0; 123 | out[4] = 0; 124 | out[5] = -2 * bt; 125 | out[6] = 0; 126 | out[7] = 0; 127 | out[8] = 0; 128 | out[9] = 0; 129 | out[10] = 2 * nf; 130 | out[11] = 0; 131 | out[12] = (left + right) * lr; 132 | out[13] = (top + bottom) * bt; 133 | out[14] = (far + near) * nf; 134 | out[15] = 1; 135 | return out; 136 | }; 137 | 138 | var mat4_translate = function (out, a, v) { 139 | var x = v[0], y = v[1], z = v[2], 140 | a00, a01, a02, a03, 141 | a10, a11, a12, a13, 142 | a20, a21, a22, a23; 143 | 144 | if (a === out) { 145 | out[12] = a[0] * x + a[4] * y + a[8] * z + a[12]; 146 | out[13] = a[1] * x + a[5] * y + a[9] * z + a[13]; 147 | out[14] = a[2] * x + a[6] * y + a[10] * z + a[14]; 148 | out[15] = a[3] * x + a[7] * y + a[11] * z + a[15]; 149 | } else { 150 | a00 = a[0]; a01 = a[1]; a02 = a[2]; a03 = a[3]; 151 | a10 = a[4]; a11 = a[5]; a12 = a[6]; a13 = a[7]; 152 | a20 = a[8]; a21 = a[9]; a22 = a[10]; a23 = a[11]; 153 | 154 | out[0] = a00; out[1] = a01; out[2] = a02; out[3] = a03; 155 | out[4] = a10; out[5] = a11; out[6] = a12; out[7] = a13; 156 | out[8] = a20; out[9] = a21; out[10] = a22; out[11] = a23; 157 | 158 | out[12] = a00 * x + a10 * y + a20 * z + a[12]; 159 | out[13] = a01 * x + a11 * y + a21 * z + a[13]; 160 | out[14] = a02 * x + a12 * y + a22 * z + a[14]; 161 | out[15] = a03 * x + a13 * y + a23 * z + a[15]; 162 | } 163 | 164 | return out; 165 | }; 166 | 167 | var mat4_scale = function(out, a, v) { 168 | var x = v[0], y = v[1], z = v[2]; 169 | 170 | out[0] = a[0] * x; 171 | out[1] = a[1] * x; 172 | out[2] = a[2] * x; 173 | out[3] = a[3] * x; 174 | out[4] = a[4] * y; 175 | out[5] = a[5] * y; 176 | out[6] = a[6] * y; 177 | out[7] = a[7] * y; 178 | out[8] = a[8] * z; 179 | out[9] = a[9] * z; 180 | out[10] = a[10] * z; 181 | out[11] = a[11] * z; 182 | out[12] = a[12]; 183 | out[13] = a[13]; 184 | out[14] = a[14]; 185 | out[15] = a[15]; 186 | return out; 187 | }; 188 | 189 | //------------------- 190 | // Utility functions 191 | //------------------- 192 | 193 | function linkProgram(gl, vertexSource, fragmentSource, attribLocationMap) { 194 | // No error checking for brevity. 195 | var vertexShader = gl.createShader(gl.VERTEX_SHADER); 196 | gl.shaderSource(vertexShader, vertexSource); 197 | gl.compileShader(vertexShader); 198 | 199 | var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER); 200 | gl.shaderSource(fragmentShader, fragmentSource); 201 | gl.compileShader(fragmentShader); 202 | 203 | var program = gl.createProgram(); 204 | gl.attachShader(program, vertexShader); 205 | gl.attachShader(program, fragmentShader); 206 | 207 | for (var attribName in attribLocationMap) 208 | gl.bindAttribLocation(program, attribLocationMap[attribName], attribName); 209 | 210 | gl.linkProgram(program); 211 | 212 | gl.deleteShader(vertexShader); 213 | gl.deleteShader(fragmentShader); 214 | 215 | return program; 216 | } 217 | 218 | function getProgramUniforms(gl, program) { 219 | var uniforms = {}; 220 | var uniformCount = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS); 221 | var uniformName = ""; 222 | for (var i = 0; i < uniformCount; i++) { 223 | var uniformInfo = gl.getActiveUniform(program, i); 224 | uniformName = uniformInfo.name.replace("[0]", ""); 225 | uniforms[uniformName] = gl.getUniformLocation(program, uniformName); 226 | } 227 | return uniforms; 228 | } 229 | 230 | //---------------------------- 231 | // Seven-segment text display 232 | //---------------------------- 233 | 234 | var sevenSegmentVS = [ 235 | "uniform mat4 projectionMat;", 236 | "uniform mat4 modelViewMat;", 237 | "attribute vec2 position;", 238 | 239 | "void main() {", 240 | " gl_Position = projectionMat * modelViewMat * vec4( position, 0.0, 1.0 );", 241 | "}", 242 | ].join("\n"); 243 | 244 | var sevenSegmentFS = [ 245 | "precision mediump float;", 246 | "uniform vec4 color;", 247 | 248 | "void main() {", 249 | " gl_FragColor = color;", 250 | "}", 251 | ].join("\n"); 252 | 253 | var SevenSegmentText = function (gl) { 254 | this.gl = gl; 255 | 256 | this.attribs = { 257 | position: 0, 258 | color: 1 259 | }; 260 | 261 | this.program = linkProgram(gl, sevenSegmentVS, sevenSegmentFS, this.attribs); 262 | this.uniforms = getProgramUniforms(gl, this.program); 263 | 264 | var verts = []; 265 | var segmentIndices = {}; 266 | var indices = []; 267 | 268 | var width = 0.5; 269 | var thickness = 0.25; 270 | this.kerning = 2.0; 271 | 272 | this.matrix = new Float32Array(16); 273 | 274 | function defineSegment(id, left, top, right, bottom) { 275 | var idx = verts.length / 2; 276 | verts.push( 277 | left, top, 278 | right, top, 279 | right, bottom, 280 | left, bottom); 281 | 282 | segmentIndices[id] = [ 283 | idx, idx+2, idx+1, 284 | idx, idx+3, idx+2]; 285 | } 286 | 287 | var characters = {}; 288 | this.characters = characters; 289 | 290 | function defineCharacter(c, segments) { 291 | var character = { 292 | character: c, 293 | offset: indices.length * 2, 294 | count: 0 295 | }; 296 | 297 | for (var i = 0; i < segments.length; ++i) { 298 | var idx = segments[i]; 299 | var segment = segmentIndices[idx]; 300 | character.count += segment.length; 301 | indices.push.apply(indices, segment); 302 | } 303 | 304 | characters[c] = character; 305 | } 306 | 307 | /* Segment layout is as follows: 308 | 309 | |-0-| 310 | 3 4 311 | |-1-| 312 | 5 6 313 | |-2-| 314 | 315 | */ 316 | 317 | defineSegment(0, -1, 1, width, 1-thickness); 318 | defineSegment(1, -1, thickness*0.5, width, -thickness*0.5); 319 | defineSegment(2, -1, -1+thickness, width, -1); 320 | defineSegment(3, -1, 1, -1+thickness, -thickness*0.5); 321 | defineSegment(4, width-thickness, 1, width, -thickness*0.5); 322 | defineSegment(5, -1, thickness*0.5, -1+thickness, -1); 323 | defineSegment(6, width-thickness, thickness*0.5, width, -1); 324 | 325 | 326 | defineCharacter("0", [0, 2, 3, 4, 5, 6]); 327 | defineCharacter("1", [4, 6]); 328 | defineCharacter("2", [0, 1, 2, 4, 5]); 329 | defineCharacter("3", [0, 1, 2, 4, 6]); 330 | defineCharacter("4", [1, 3, 4, 6]); 331 | defineCharacter("5", [0, 1, 2, 3, 6]); 332 | defineCharacter("6", [0, 1, 2, 3, 5, 6]); 333 | defineCharacter("7", [0, 4, 6]); 334 | defineCharacter("8", [0, 1, 2, 3, 4, 5, 6]); 335 | defineCharacter("9", [0, 1, 2, 3, 4, 6]); 336 | defineCharacter("A", [0, 1, 3, 4, 5, 6]); 337 | defineCharacter("B", [1, 2, 3, 5, 6]); 338 | defineCharacter("C", [0, 2, 3, 5]); 339 | defineCharacter("D", [1, 2, 4, 5, 6]); 340 | defineCharacter("E", [0, 1, 2, 4, 6]); 341 | defineCharacter("F", [0, 1, 3, 5]); 342 | defineCharacter("P", [0, 1, 3, 4, 5]); 343 | defineCharacter("-", [1]); 344 | defineCharacter(" ", []); 345 | defineCharacter("_", [2]); // Used for undefined characters 346 | 347 | this.vertBuffer = gl.createBuffer(); 348 | gl.bindBuffer(gl.ARRAY_BUFFER, this.vertBuffer); 349 | gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(verts), gl.DYNAMIC_DRAW); 350 | 351 | this.indexBuffer = gl.createBuffer(); 352 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); 353 | gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW); 354 | }; 355 | 356 | SevenSegmentText.prototype.render = function(projectionMat, modelViewMat, text, r, g, b, a) { 357 | var gl = this.gl; 358 | 359 | if (r == undefined || g == undefined || b == undefined) { 360 | r = 0.0; 361 | g = 1.0; 362 | b = 0.0; 363 | } 364 | 365 | if (a == undefined) 366 | a = 1.0; 367 | 368 | gl.useProgram(this.program); 369 | 370 | gl.uniformMatrix4fv(this.uniforms.projectionMat, false, projectionMat); 371 | gl.uniform4f(this.uniforms.color, r, g, b, a); 372 | 373 | gl.bindBuffer(gl.ARRAY_BUFFER, this.vertBuffer); 374 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); 375 | 376 | gl.enableVertexAttribArray(this.attribs.position); 377 | gl.vertexAttribPointer(this.attribs.position, 2, gl.FLOAT, false, 8, 0); 378 | 379 | text = text.toUpperCase(); 380 | 381 | var offset = 0; 382 | 383 | for (var i = 0; i < text.length; ++i) { 384 | var c; 385 | if (text[i] in this.characters) { 386 | c = this.characters[text[i]]; 387 | } else { 388 | c = this.characters["_"]; 389 | } 390 | 391 | if (c.count != 0) { 392 | mat4_fromTranslation(this.matrix, [offset, 0, 0]); 393 | mat4_multiply(this.matrix, modelViewMat, this.matrix); 394 | 395 | gl.uniformMatrix4fv(this.uniforms.modelViewMat, false, this.matrix); 396 | gl.drawElements(gl.TRIANGLES, c.count, gl.UNSIGNED_SHORT, c.offset); 397 | 398 | } 399 | 400 | offset += this.kerning; 401 | } 402 | } 403 | 404 | //----------- 405 | // FPS Graph 406 | //----------- 407 | 408 | var statsVS = [ 409 | "uniform mat4 projectionMat;", 410 | "uniform mat4 modelViewMat;", 411 | "attribute vec3 position;", 412 | "attribute vec3 color;", 413 | "varying vec4 vColor;", 414 | 415 | "void main() {", 416 | " vColor = vec4(color, 1.0);", 417 | " gl_Position = projectionMat * modelViewMat * vec4( position, 1.0 );", 418 | "}", 419 | ].join("\n"); 420 | 421 | var statsFS = [ 422 | "precision mediump float;", 423 | "varying vec4 vColor;", 424 | 425 | "void main() {", 426 | " gl_FragColor = vColor;", 427 | "}", 428 | ].join("\n"); 429 | 430 | var segments = 30; 431 | var maxFPS = 90; 432 | 433 | function segmentToX(i) { 434 | return ((0.9/segments) * i) - 0.45; 435 | } 436 | 437 | function fpsToY(value) { 438 | return (Math.min(value, maxFPS) * (0.7 / maxFPS)) - 0.45; 439 | } 440 | 441 | function fpsToRGB(value) { 442 | return { 443 | r: Math.max(0.0, Math.min(1.0, 1.0 - (value/60))), 444 | g: Math.max(0.0, Math.min(1.0, ((value-15)/(maxFPS-15)))), 445 | b: Math.max(0.0, Math.min(1.0, ((value-15)/(maxFPS-15)))) 446 | }; 447 | } 448 | 449 | var now = (window.performance && performance.now) ? performance.now.bind(performance) : Date.now; 450 | 451 | var Stats = function(gl) { 452 | this.gl = gl; 453 | 454 | this.sevenSegmentText = new SevenSegmentText(gl); 455 | 456 | this.startTime = now(); 457 | this.prevFrameTime = this.startTime; 458 | this.prevGraphUpdateTime = this.startTime; 459 | this.frames = 0; 460 | this.fpsAverage = 0; 461 | this.fpsSum = 0; 462 | this.fpsMin = 0; 463 | 464 | this.orthoProjMatrix = new Float32Array(16); 465 | this.orthoViewMatrix = new Float32Array(16); 466 | this.modelViewMatrix = new Float32Array(16); 467 | 468 | // Hard coded because it doesn't change: 469 | // Scale by 0.075 in X and Y 470 | // Translate into upper left corner w/ z = 0.02 471 | this.textMatrix = new Float32Array([ 472 | 0.075, 0, 0, 0, 473 | 0, 0.075, 0, 0, 474 | 0, 0, 1, 0, 475 | -0.3625, 0.3625, 0.02, 1 476 | ]); 477 | 478 | this.lastSegment = 0; 479 | 480 | this.attribs = { 481 | position: 0, 482 | color: 1 483 | }; 484 | 485 | this.program = linkProgram(gl, statsVS, statsFS, this.attribs); 486 | this.uniforms = getProgramUniforms(gl, this.program); 487 | 488 | var fpsVerts = []; 489 | var fpsIndices = []; 490 | 491 | // Graph geometry 492 | for (var i = 0; i < segments; ++i) { 493 | // Bar top 494 | fpsVerts.push(segmentToX(i), fpsToY(0), 0.02, 0.0, 1.0, 1.0); 495 | fpsVerts.push(segmentToX(i+1), fpsToY(0), 0.02, 0.0, 1.0, 1.0); 496 | 497 | // Bar bottom 498 | fpsVerts.push(segmentToX(i), fpsToY(0), 0.02, 0.0, 1.0, 1.0); 499 | fpsVerts.push(segmentToX(i+1), fpsToY(0), 0.02, 0.0, 1.0, 1.0); 500 | 501 | var idx = i * 4; 502 | fpsIndices.push(idx, idx+3, idx+1, 503 | idx+3, idx, idx+2); 504 | } 505 | 506 | function addBGSquare(left, bottom, right, top, z, r, g, b) { 507 | var idx = fpsVerts.length / 6; 508 | 509 | fpsVerts.push(left, bottom, z, r, g, b); 510 | fpsVerts.push(right, top, z, r, g, b); 511 | fpsVerts.push(left, top, z, r, g, b); 512 | fpsVerts.push(right, bottom, z, r, g, b); 513 | 514 | fpsIndices.push(idx, idx+1, idx+2, 515 | idx, idx+3, idx+1); 516 | }; 517 | 518 | // Panel Background 519 | addBGSquare(-0.5, -0.5, 0.5, 0.5, 0.0, 0.0, 0.0, 0.125); 520 | 521 | // FPS Background 522 | addBGSquare(-0.45, -0.45, 0.45, 0.25, 0.01, 0.0, 0.0, 0.4); 523 | 524 | // 30 FPS line 525 | addBGSquare(-0.45, fpsToY(30), 0.45, fpsToY(32), 0.015, 0.5, 0.0, 0.5); 526 | 527 | // 60 FPS line 528 | addBGSquare(-0.45, fpsToY(60), 0.45, fpsToY(62), 0.015, 0.2, 0.0, 0.75); 529 | 530 | this.fpsVertBuffer = gl.createBuffer(); 531 | gl.bindBuffer(gl.ARRAY_BUFFER, this.fpsVertBuffer); 532 | gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(fpsVerts), gl.DYNAMIC_DRAW); 533 | 534 | this.fpsIndexBuffer = gl.createBuffer(); 535 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.fpsIndexBuffer); 536 | gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(fpsIndices), gl.STATIC_DRAW); 537 | 538 | this.fpsIndexCount = fpsIndices.length; 539 | }; 540 | 541 | Stats.prototype.begin = function() { 542 | this.startTime = now(); 543 | }; 544 | 545 | Stats.prototype.end = function() { 546 | var time = now(); 547 | 548 | var frameFps = 1000 / (time - this.prevFrameTime); 549 | this.prevFrameTime = time; 550 | this.fpsMin = this.frames ? Math.min(this.fpsMin, frameFps) : frameFps; 551 | this.fpsSum += frameFps; 552 | this.frames++; 553 | 554 | if (time > this.prevGraphUpdateTime + 250) { 555 | this.fpsAverage = Math.round(this.fpsSum / this.frames); 556 | 557 | // Draw both average and minimum FPS for this period 558 | // so that dropped frames are more clearly visible. 559 | this.updateGraph(this.fpsMin, this.fpsAverage); 560 | 561 | this.prevGraphUpdateTime = time; 562 | this.frames = 0; 563 | this.fpsMin = 0; 564 | this.fpsSum = 0; 565 | } 566 | }; 567 | 568 | Stats.prototype.updateGraph = function(valueLow, valueHigh) { 569 | var gl = this.gl; 570 | 571 | var color = fpsToRGB(valueLow); 572 | // Draw a range from the low to high value. Artificially widen the 573 | // range a bit to ensure that near-equal values still remain 574 | // visible - the logic here should match that used by the 575 | // "60 FPS line" setup below. Hitting 60fps consistently will 576 | // keep the top half of the 60fps background line visible. 577 | var y0 = fpsToY(valueLow - 1); 578 | var y1 = fpsToY(valueHigh + 1); 579 | 580 | gl.bindBuffer(gl.ARRAY_BUFFER, this.fpsVertBuffer); 581 | 582 | // Update the current segment with the new FPS value 583 | var updateVerts = [ 584 | segmentToX(this.lastSegment), y1, 0.02, color.r, color.g, color.b, 585 | segmentToX(this.lastSegment+1), y1, 0.02, color.r, color.g, color.b, 586 | segmentToX(this.lastSegment), y0, 0.02, color.r, color.g, color.b, 587 | segmentToX(this.lastSegment+1), y0, 0.02, color.r, color.g, color.b, 588 | ]; 589 | 590 | // Re-shape the next segment into the green "progress" line 591 | color.r = 0.2; 592 | color.g = 1.0; 593 | color.b = 0.2; 594 | 595 | if (this.lastSegment == segments - 1) { 596 | // If we're updating the last segment we need to do two bufferSubDatas 597 | // to update the segment and turn the first segment into the progress line. 598 | gl.bufferSubData(gl.ARRAY_BUFFER, this.lastSegment * 24 * 4, new Float32Array(updateVerts)); 599 | updateVerts = [ 600 | segmentToX(0), fpsToY(maxFPS), 0.02, color.r, color.g, color.b, 601 | segmentToX(.25), fpsToY(maxFPS), 0.02, color.r, color.g, color.b, 602 | segmentToX(0), fpsToY(0), 0.02, color.r, color.g, color.b, 603 | segmentToX(.25), fpsToY(0), 0.02, color.r, color.g, color.b 604 | ]; 605 | gl.bufferSubData(gl.ARRAY_BUFFER, 0, new Float32Array(updateVerts)); 606 | } else { 607 | updateVerts.push( 608 | segmentToX(this.lastSegment+1), fpsToY(maxFPS), 0.02, color.r, color.g, color.b, 609 | segmentToX(this.lastSegment+1.25), fpsToY(maxFPS), 0.02, color.r, color.g, color.b, 610 | segmentToX(this.lastSegment+1), fpsToY(0), 0.02, color.r, color.g, color.b, 611 | segmentToX(this.lastSegment+1.25), fpsToY(0), 0.02, color.r, color.g, color.b 612 | ); 613 | gl.bufferSubData(gl.ARRAY_BUFFER, this.lastSegment * 24 * 4, new Float32Array(updateVerts)); 614 | } 615 | 616 | this.lastSegment = (this.lastSegment+1) % segments; 617 | }; 618 | 619 | Stats.prototype.render = function(projectionMat, modelViewMat) { 620 | var gl = this.gl; 621 | 622 | gl.useProgram(this.program); 623 | 624 | gl.uniformMatrix4fv(this.uniforms.projectionMat, false, projectionMat); 625 | gl.uniformMatrix4fv(this.uniforms.modelViewMat, false, modelViewMat); 626 | 627 | gl.enableVertexAttribArray(this.attribs.position); 628 | gl.enableVertexAttribArray(this.attribs.color); 629 | 630 | gl.bindBuffer(gl.ARRAY_BUFFER, this.fpsVertBuffer); 631 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.fpsIndexBuffer); 632 | 633 | gl.vertexAttribPointer(this.attribs.position, 3, gl.FLOAT, false, 24, 0); 634 | gl.vertexAttribPointer(this.attribs.color, 3, gl.FLOAT, false, 24, 12); 635 | 636 | // Draw the graph and background in a single call 637 | gl.drawElements(gl.TRIANGLES, this.fpsIndexCount, gl.UNSIGNED_SHORT, 0); 638 | 639 | mat4_multiply(this.modelViewMatrix, modelViewMat, this.textMatrix); 640 | this.sevenSegmentText.render(projectionMat, this.modelViewMatrix, this.fpsAverage + " FP5"); 641 | } 642 | 643 | Stats.prototype.renderOrtho = function(x, y, width, height) { 644 | var canvas = this.gl.canvas; 645 | 646 | if (x == undefined || y == undefined) { 647 | x = 10 * window.devicePixelRatio; 648 | y = 10 * window.devicePixelRatio; 649 | } 650 | if (width == undefined || height == undefined) { 651 | width = 75 * window.devicePixelRatio; 652 | height = 75 * window.devicePixelRatio; 653 | } 654 | 655 | mat4_ortho(this.orthoProjMatrix, 0, canvas.width, 0, canvas.height, 0.1, 1024); 656 | 657 | mat4_identity(this.orthoViewMatrix); 658 | mat4_translate(this.orthoViewMatrix, this.orthoViewMatrix, [x, canvas.height - height - y, -1]); 659 | mat4_scale(this.orthoViewMatrix, this.orthoViewMatrix, [width, height, 1]); 660 | mat4_translate(this.orthoViewMatrix, this.orthoViewMatrix, [0.5, 0.5, 0]); 661 | 662 | this.render(this.orthoProjMatrix, this.orthoViewMatrix); 663 | } 664 | 665 | return Stats; 666 | })(); 667 | -------------------------------------------------------------------------------- /src/wglu-texture.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015, Brandon Jones. 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | /* 24 | Handles loading of textures of mutliple formats, tries to be efficent about it. 25 | 26 | Formats supported will vary by devices. Use the .supports() functions 27 | to determine if a format is supported. Most of the time you can just call 28 | loader.loadTexture("url"); and it will handle it based on the extension. 29 | If the extension can't be relied on use the corresponding 30 | .load("url") calls. 31 | */ 32 | var WGLUTextureLoader = (function() { 33 | 34 | "use strict"; 35 | 36 | //============================// 37 | // DXT constants and utilites // 38 | //============================// 39 | 40 | // Utility functions 41 | // Builds a numeric code for a given fourCC string 42 | function fourCCToInt32(value) { 43 | return value.charCodeAt(0) + 44 | (value.charCodeAt(1) << 8) + 45 | (value.charCodeAt(2) << 16) + 46 | (value.charCodeAt(3) << 24); 47 | } 48 | 49 | // Turns a fourCC numeric code into a string 50 | function int32ToFourCC(value) { 51 | return String.fromCharCode( 52 | value & 0xff, 53 | (value >> 8) & 0xff, 54 | (value >> 16) & 0xff, 55 | (value >> 24) & 0xff 56 | ); 57 | } 58 | 59 | // Calcualates the size of a compressed texture level in bytes 60 | function textureLevelSize(format, width, height) { 61 | switch (format) { 62 | case COMPRESSED_RGB_S3TC_DXT1_EXT: 63 | case COMPRESSED_RGB_ATC_WEBGL: 64 | case COMPRESSED_RGB_ETC1_WEBGL: 65 | return ((width + 3) >> 2) * ((height + 3) >> 2) * 8; 66 | 67 | case COMPRESSED_RGBA_S3TC_DXT3_EXT: 68 | case COMPRESSED_RGBA_S3TC_DXT5_EXT: 69 | case COMPRESSED_RGBA_ATC_EXPLICIT_ALPHA_WEBGL: 70 | case COMPRESSED_RGBA_ATC_INTERPOLATED_ALPHA_WEBGL: 71 | return ((width + 3) >> 2) * ((height + 3) >> 2) * 16; 72 | 73 | case COMPRESSED_RGB_PVRTC_4BPPV1_IMG: 74 | case COMPRESSED_RGBA_PVRTC_4BPPV1_IMG: 75 | return Math.floor((Math.max(width, 8) * Math.max(height, 8) * 4 + 7) / 8); 76 | 77 | case COMPRESSED_RGB_PVRTC_2BPPV1_IMG: 78 | case COMPRESSED_RGBA_PVRTC_2BPPV1_IMG: 79 | return Math.floor((Math.max(width, 16) * Math.max(height, 8) * 2 + 7) / 8); 80 | 81 | default: 82 | return 0; 83 | } 84 | } 85 | 86 | // DXT formats, from: 87 | // http://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_s3tc/ 88 | var COMPRESSED_RGB_S3TC_DXT1_EXT = 0x83F0; 89 | var COMPRESSED_RGBA_S3TC_DXT1_EXT = 0x83F1; 90 | var COMPRESSED_RGBA_S3TC_DXT3_EXT = 0x83F2; 91 | var COMPRESSED_RGBA_S3TC_DXT5_EXT = 0x83F3; 92 | 93 | // ATC formats, from: 94 | // http://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_atc/ 95 | var COMPRESSED_RGB_ATC_WEBGL = 0x8C92; 96 | var COMPRESSED_RGBA_ATC_EXPLICIT_ALPHA_WEBGL = 0x8C93; 97 | var COMPRESSED_RGBA_ATC_INTERPOLATED_ALPHA_WEBGL = 0x87EE; 98 | 99 | // DXT values and structures referenced from: 100 | // http://msdn.microsoft.com/en-us/library/bb943991.aspx/ 101 | var DDS_MAGIC = 0x20534444; 102 | var DDSD_MIPMAPCOUNT = 0x20000; 103 | var DDPF_FOURCC = 0x4; 104 | 105 | var DDS_HEADER_LENGTH = 31; // The header length in 32 bit ints. 106 | 107 | // Offsets into the header array. 108 | var DDS_HEADER_MAGIC = 0; 109 | 110 | var DDS_HEADER_SIZE = 1; 111 | var DDS_HEADER_FLAGS = 2; 112 | var DDS_HEADER_HEIGHT = 3; 113 | var DDS_HEADER_WIDTH = 4; 114 | 115 | var DDS_HEADER_MIPMAPCOUNT = 7; 116 | 117 | var DDS_HEADER_PF_FLAGS = 20; 118 | var DDS_HEADER_PF_FOURCC = 21; 119 | 120 | // FourCC format identifiers. 121 | var FOURCC_DXT1 = fourCCToInt32("DXT1"); 122 | var FOURCC_DXT3 = fourCCToInt32("DXT3"); 123 | var FOURCC_DXT5 = fourCCToInt32("DXT5"); 124 | 125 | var FOURCC_ATC = fourCCToInt32("ATC "); 126 | var FOURCC_ATCA = fourCCToInt32("ATCA"); 127 | var FOURCC_ATCI = fourCCToInt32("ATCI"); 128 | 129 | //==================// 130 | // Crunch constants // 131 | //==================// 132 | 133 | // Taken from crnlib.h 134 | var CRN_FORMAT = { 135 | cCRNFmtInvalid: -1, 136 | 137 | cCRNFmtDXT1: 0, 138 | // cCRNFmtDXT3 is not currently supported when writing to CRN - only DDS. 139 | cCRNFmtDXT3: 1, 140 | cCRNFmtDXT5: 2 141 | 142 | // Crunch supports more formats than this, but we can't use them here. 143 | }; 144 | 145 | // Mapping of Crunch formats to DXT formats. 146 | var DXT_FORMAT_MAP = {}; 147 | DXT_FORMAT_MAP[CRN_FORMAT.cCRNFmtDXT1] = COMPRESSED_RGB_S3TC_DXT1_EXT; 148 | DXT_FORMAT_MAP[CRN_FORMAT.cCRNFmtDXT3] = COMPRESSED_RGBA_S3TC_DXT3_EXT; 149 | DXT_FORMAT_MAP[CRN_FORMAT.cCRNFmtDXT5] = COMPRESSED_RGBA_S3TC_DXT5_EXT; 150 | 151 | //===============// 152 | // PVR constants // 153 | //===============// 154 | 155 | // PVR formats, from: 156 | // http://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_pvrtc/ 157 | var COMPRESSED_RGB_PVRTC_4BPPV1_IMG = 0x8C00; 158 | var COMPRESSED_RGB_PVRTC_2BPPV1_IMG = 0x8C01; 159 | var COMPRESSED_RGBA_PVRTC_4BPPV1_IMG = 0x8C02; 160 | var COMPRESSED_RGBA_PVRTC_2BPPV1_IMG = 0x8C03; 161 | 162 | // ETC1 format, from: 163 | // http://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_etc1/ 164 | var COMPRESSED_RGB_ETC1_WEBGL = 0x8D64; 165 | 166 | var PVR_FORMAT_2BPP_RGB = 0; 167 | var PVR_FORMAT_2BPP_RGBA = 1; 168 | var PVR_FORMAT_4BPP_RGB = 2; 169 | var PVR_FORMAT_4BPP_RGBA = 3; 170 | var PVR_FORMAT_ETC1 = 6; 171 | var PVR_FORMAT_DXT1 = 7; 172 | var PVR_FORMAT_DXT3 = 9; 173 | var PVR_FORMAT_DXT5 = 5; 174 | 175 | var PVR_HEADER_LENGTH = 13; // The header length in 32 bit ints. 176 | var PVR_MAGIC = 0x03525650; //0x50565203; 177 | 178 | // Offsets into the header array. 179 | var PVR_HEADER_MAGIC = 0; 180 | var PVR_HEADER_FORMAT = 2; 181 | var PVR_HEADER_HEIGHT = 6; 182 | var PVR_HEADER_WIDTH = 7; 183 | var PVR_HEADER_MIPMAPCOUNT = 11; 184 | var PVR_HEADER_METADATA = 12; 185 | 186 | //============// 187 | // Misc Utils // 188 | //============// 189 | 190 | // When an error occurs set the texture to a 1x1 black pixel 191 | // This prevents WebGL errors from attempting to use unrenderable textures 192 | // and clears out stale data if we're re-using a texture. 193 | function clearOnError(gl, error, texture, callback) { 194 | if (console) { 195 | console.error(error); 196 | } 197 | gl.bindTexture(gl.TEXTURE_2D, texture); 198 | gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, 1, 1, 0, gl.RGB, gl.UNSIGNED_BYTE, new Uint8Array([0, 0, 0])); 199 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); 200 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); 201 | 202 | // Notify the user that an error occurred and the texture is ready. 203 | if (callback) { callback(texture, error, null); } 204 | } 205 | 206 | function isPowerOfTwo(n) { 207 | return (n & (n - 1)) === 0; 208 | } 209 | 210 | function getExtension(gl, name) { 211 | var vendorPrefixes = ["", "WEBKIT_", "MOZ_"]; 212 | var ext = null; 213 | for (var i in vendorPrefixes) { 214 | ext = gl.getExtension(vendorPrefixes[i] + name); 215 | if (ext) { break; } 216 | } 217 | return ext; 218 | } 219 | 220 | //==================// 221 | // DDS File Reading // 222 | //==================// 223 | 224 | // Parse a DDS file and provide information about the raw DXT data it contains to the given callback. 225 | function parseDDS(arrayBuffer, callback, errorCallback) { 226 | // Callbacks must be provided. 227 | if (!callback || !errorCallback) { return; } 228 | 229 | // Get a view of the arrayBuffer that represents the DDS header. 230 | var header = new Int32Array(arrayBuffer, 0, DDS_HEADER_LENGTH); 231 | 232 | // Do some sanity checks to make sure this is a valid DDS file. 233 | if(header[DDS_HEADER_MAGIC] != DDS_MAGIC) { 234 | errorCallback("Invalid magic number in DDS header"); 235 | return 0; 236 | } 237 | 238 | if(!header[DDS_HEADER_PF_FLAGS] & DDPF_FOURCC) { 239 | errorCallback("Unsupported format, must contain a FourCC code"); 240 | return 0; 241 | } 242 | 243 | // Determine what type of compressed data the file contains. 244 | var fourCC = header[DDS_HEADER_PF_FOURCC]; 245 | var internalFormat; 246 | switch(fourCC) { 247 | case FOURCC_DXT1: 248 | internalFormat = COMPRESSED_RGB_S3TC_DXT1_EXT; 249 | break; 250 | 251 | case FOURCC_DXT3: 252 | internalFormat = COMPRESSED_RGBA_S3TC_DXT3_EXT; 253 | break; 254 | 255 | case FOURCC_DXT5: 256 | internalFormat = COMPRESSED_RGBA_S3TC_DXT5_EXT; 257 | break; 258 | 259 | case FOURCC_ATC: 260 | internalFormat = COMPRESSED_RGB_ATC_WEBGL; 261 | break; 262 | 263 | case FOURCC_ATCA: 264 | internalFormat = COMPRESSED_RGBA_ATC_EXPLICIT_ALPHA_WEBGL; 265 | break; 266 | 267 | case FOURCC_ATCI: 268 | internalFormat = COMPRESSED_RGBA_ATC_INTERPOLATED_ALPHA_WEBGL; 269 | break; 270 | 271 | 272 | default: 273 | errorCallback("Unsupported FourCC code: " + int32ToFourCC(fourCC)); 274 | return; 275 | } 276 | 277 | // Determine how many mipmap levels the file contains. 278 | var levels = 1; 279 | if(header[DDS_HEADER_FLAGS] & DDSD_MIPMAPCOUNT) { 280 | levels = Math.max(1, header[DDS_HEADER_MIPMAPCOUNT]); 281 | } 282 | 283 | // Gather other basic metrics and a view of the raw the DXT data. 284 | var width = header[DDS_HEADER_WIDTH]; 285 | var height = header[DDS_HEADER_HEIGHT]; 286 | var dataOffset = header[DDS_HEADER_SIZE] + 4; 287 | var dxtData = new Uint8Array(arrayBuffer, dataOffset); 288 | 289 | // Pass the DXT information to the callback for uploading. 290 | callback(dxtData, width, height, levels, internalFormat); 291 | } 292 | 293 | //==================// 294 | // PVR File Reading // 295 | //==================// 296 | 297 | // Parse a PVR file and provide information about the raw texture data it contains to the given callback. 298 | function parsePVR(arrayBuffer, callback, errorCallback) { 299 | // Callbacks must be provided. 300 | if (!callback || !errorCallback) { return; } 301 | 302 | // Get a view of the arrayBuffer that represents the DDS header. 303 | var header = new Int32Array(arrayBuffer, 0, PVR_HEADER_LENGTH); 304 | 305 | // Do some sanity checks to make sure this is a valid DDS file. 306 | if(header[PVR_HEADER_MAGIC] != PVR_MAGIC) { 307 | errorCallback("Invalid magic number in PVR header"); 308 | return 0; 309 | } 310 | 311 | // Determine what type of compressed data the file contains. 312 | var format = header[PVR_HEADER_FORMAT]; 313 | var internalFormat; 314 | switch(format) { 315 | case PVR_FORMAT_2BPP_RGB: 316 | internalFormat = COMPRESSED_RGB_PVRTC_2BPPV1_IMG; 317 | break; 318 | 319 | case PVR_FORMAT_2BPP_RGBA: 320 | internalFormat = COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; 321 | break; 322 | 323 | case PVR_FORMAT_4BPP_RGB: 324 | internalFormat = COMPRESSED_RGB_PVRTC_4BPPV1_IMG; 325 | break; 326 | 327 | case PVR_FORMAT_4BPP_RGBA: 328 | internalFormat = COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; 329 | break; 330 | 331 | case PVR_FORMAT_ETC1: 332 | internalFormat = COMPRESSED_RGB_ETC1_WEBGL; 333 | break; 334 | 335 | case PVR_FORMAT_DXT1: 336 | internalFormat = COMPRESSED_RGB_S3TC_DXT1_EXT; 337 | break; 338 | 339 | case PVR_FORMAT_DXT3: 340 | internalFormat = COMPRESSED_RGBA_S3TC_DXT3_EXT; 341 | break; 342 | 343 | case PVR_FORMAT_DXT5: 344 | internalFormat = COMPRESSED_RGBA_S3TC_DXT5_EXT; 345 | break; 346 | 347 | default: 348 | errorCallback("Unsupported PVR format: " + format); 349 | return; 350 | } 351 | 352 | // Gather other basic metrics and a view of the raw the DXT data. 353 | var width = header[PVR_HEADER_WIDTH]; 354 | var height = header[PVR_HEADER_HEIGHT]; 355 | var levels = header[PVR_HEADER_MIPMAPCOUNT]; 356 | var dataOffset = header[PVR_HEADER_METADATA] + 52; 357 | var pvrtcData = new Uint8Array(arrayBuffer, dataOffset); 358 | 359 | // Pass the PVRTC information to the callback for uploading. 360 | callback(pvrtcData, width, height, levels, internalFormat); 361 | } 362 | 363 | //=============// 364 | // IMG loading // 365 | //=============// 366 | 367 | /* 368 | This function provides a method for loading webgl textures using a pool of 369 | image elements, which has very low memory overhead. For more details see: 370 | http://blog.tojicode.com/2012/03/javascript-memory-optimization-and.html 371 | */ 372 | var loadImgTexture = (function createTextureLoader() { 373 | var MAX_CACHE_IMAGES = 16; 374 | 375 | var textureImageCache = new Array(MAX_CACHE_IMAGES); 376 | var cacheTop = 0; 377 | var remainingCacheImages = MAX_CACHE_IMAGES; 378 | var pendingTextureRequests = []; 379 | 380 | var TextureImageLoader = function(loadedCallback) { 381 | var self = this; 382 | var blackPixel = new Uint8Array([0, 0, 0]); 383 | 384 | this.gl = null; 385 | this.texture = null; 386 | this.callback = null; 387 | 388 | this.image = new Image(); 389 | this.image.addEventListener('load', function() { 390 | var gl = self.gl; 391 | gl.bindTexture(gl.TEXTURE_2D, self.texture); 392 | 393 | var startTime = Date.now(); 394 | gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, self.image); 395 | 396 | if (isPowerOfTwo(self.image.width) && isPowerOfTwo(self.image.height)) { 397 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); 398 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_NEAREST); 399 | gl.generateMipmap(gl.TEXTURE_2D); 400 | } else { 401 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); 402 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); 403 | } 404 | var uploadTime = Date.now() - startTime; 405 | 406 | if(self.callback) { 407 | var stats = { 408 | width: self.image.width, 409 | height: self.image.height, 410 | internalFormat: gl.RGBA, 411 | levelZeroSize: self.image.width * self.image.height * 4, 412 | uploadTime: uploadTime 413 | }; 414 | self.callback(self.texture, null, stats); 415 | } 416 | loadedCallback(self); 417 | }, false); 418 | this.image.addEventListener('error', function(ev) { 419 | clearOnError(self.gl, 'Image could not be loaded: ' + self.image.src, self.texture, self.callback); 420 | loadedCallback(self); 421 | }, false); 422 | }; 423 | 424 | TextureImageLoader.prototype.loadTexture = function(gl, src, texture, callback) { 425 | this.gl = gl; 426 | this.texture = texture; 427 | this.callback = callback; 428 | this.image.src = src; 429 | }; 430 | 431 | var PendingTextureRequest = function(gl, src, texture, callback) { 432 | this.gl = gl; 433 | this.src = src; 434 | this.texture = texture; 435 | this.callback = callback; 436 | }; 437 | 438 | function releaseTextureImageLoader(til) { 439 | var req; 440 | if(pendingTextureRequests.length) { 441 | req = pendingTextureRequests.shift(); 442 | til.loadTexture(req.gl, req.src, req.texture, req.callback); 443 | } else { 444 | textureImageCache[cacheTop++] = til; 445 | } 446 | } 447 | 448 | return function(gl, src, texture, callback) { 449 | var til; 450 | 451 | if(cacheTop) { 452 | til = textureImageCache[--cacheTop]; 453 | til.loadTexture(gl, src, texture, callback); 454 | } else if (remainingCacheImages) { 455 | til = new TextureImageLoader(releaseTextureImageLoader); 456 | til.loadTexture(gl, src, texture, callback); 457 | --remainingCacheImages; 458 | } else { 459 | pendingTextureRequests.push(new PendingTextureRequest(gl, src, texture, callback)); 460 | } 461 | 462 | return texture; 463 | }; 464 | })(); 465 | 466 | //=====================// 467 | // TextureLoader Class // 468 | //=====================// 469 | 470 | // This class is our public interface. 471 | var TextureLoader = function(gl) { 472 | this.gl = gl; 473 | 474 | // Load the compression format extensions, if available 475 | this.dxtExt = getExtension(gl, "WEBGL_compressed_texture_s3tc"); 476 | this.pvrtcExt = getExtension(gl, "WEBGL_compressed_texture_pvrtc"); 477 | this.atcExt = getExtension(gl, "WEBGL_compressed_texture_atc"); 478 | this.etc1Ext = getExtension(gl, "WEBGL_compressed_texture_etc1"); 479 | 480 | // Returns whether or not the compressed format is supported by the WebGL implementation 481 | TextureLoader.prototype._formatSupported = function(format) { 482 | switch (format) { 483 | case COMPRESSED_RGB_S3TC_DXT1_EXT: 484 | case COMPRESSED_RGBA_S3TC_DXT3_EXT: 485 | case COMPRESSED_RGBA_S3TC_DXT5_EXT: 486 | return !!this.dxtExt; 487 | 488 | case COMPRESSED_RGB_PVRTC_4BPPV1_IMG: 489 | case COMPRESSED_RGBA_PVRTC_4BPPV1_IMG: 490 | case COMPRESSED_RGB_PVRTC_2BPPV1_IMG: 491 | case COMPRESSED_RGBA_PVRTC_2BPPV1_IMG: 492 | return !!this.pvrtcExt; 493 | 494 | case COMPRESSED_RGB_ATC_WEBGL: 495 | case COMPRESSED_RGBA_ATC_EXPLICIT_ALPHA_WEBGL: 496 | case COMPRESSED_RGBA_ATC_INTERPOLATED_ALPHA_WEBGL: 497 | return !!this.atcExt; 498 | 499 | case COMPRESSED_RGB_ETC1_WEBGL: 500 | return !!this.etc1Ext; 501 | 502 | default: 503 | return false; 504 | } 505 | } 506 | 507 | // Uploads compressed texture data to the GPU. 508 | TextureLoader.prototype._uploadCompressedData = function(data, width, height, levels, internalFormat, texture, callback) { 509 | var gl = this.gl; 510 | gl.bindTexture(gl.TEXTURE_2D, texture); 511 | 512 | var offset = 0; 513 | 514 | var stats = { 515 | width: width, 516 | height: height, 517 | internalFormat: internalFormat, 518 | levelZeroSize: textureLevelSize(internalFormat, width, height), 519 | uploadTime: 0 520 | }; 521 | 522 | var startTime = Date.now(); 523 | // Loop through each mip level of compressed texture data provided and upload it to the given texture. 524 | for (var i = 0; i < levels; ++i) { 525 | // Determine how big this level of compressed texture data is in bytes. 526 | var levelSize = textureLevelSize(internalFormat, width, height); 527 | // Get a view of the bytes for this level of DXT data. 528 | var dxtLevel = new Uint8Array(data.buffer, data.byteOffset + offset, levelSize); 529 | // Upload! 530 | gl.compressedTexImage2D(gl.TEXTURE_2D, i, internalFormat, width, height, 0, dxtLevel); 531 | // The next mip level will be half the height and width of this one. 532 | width = width >> 1; 533 | height = height >> 1; 534 | // Advance the offset into the compressed texture data past the current mip level's data. 535 | offset += levelSize; 536 | } 537 | stats.uploadTime = Date.now() - startTime; 538 | 539 | // We can't use gl.generateMipmaps with compressed textures, so only use 540 | // mipmapped filtering if the compressed texture data contained mip levels. 541 | if (levels > 1) { 542 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); 543 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_NEAREST); 544 | } else { 545 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); 546 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); 547 | } 548 | 549 | // Notify the user that the texture is ready. 550 | if (callback) { callback(texture, null, stats); } 551 | } 552 | 553 | TextureLoader.prototype.supportsDXT = function() { 554 | return !!this.dxtExt; 555 | } 556 | 557 | TextureLoader.prototype.supportsPVRTC = function() { 558 | return !!this.pvrtcExt; 559 | } 560 | 561 | TextureLoader.prototype.supportsATC = function() { 562 | return !!this.atcExt; 563 | } 564 | 565 | TextureLoader.prototype.supportsETC1 = function() { 566 | return !!this.etc1Ext; 567 | } 568 | 569 | // Loads a image file into the given texture. 570 | // Supports any format that can be loaded into an img tag 571 | // If no texture is provided one is created and returned. 572 | TextureLoader.prototype.loadIMG = function(src, texture, callback) { 573 | if(!texture) { 574 | texture = this.gl.createTexture(); 575 | } 576 | 577 | loadImgTexture(gl, src, texture, callback); 578 | 579 | return texture; 580 | } 581 | 582 | // Loads a DDS file into the given texture. 583 | // If no texture is provided one is created and returned. 584 | TextureLoader.prototype.loadDDS = function(src, texture, callback) { 585 | var self = this; 586 | if (!texture) { 587 | texture = this.gl.createTexture(); 588 | } 589 | 590 | // Load the file via XHR. 591 | var xhr = new XMLHttpRequest(); 592 | xhr.addEventListener('load', function (ev) { 593 | if (xhr.status == 200) { 594 | // If the file loaded successfully parse it. 595 | parseDDS(xhr.response, function(dxtData, width, height, levels, internalFormat) { 596 | if (!self._formatSupported(internalFormat)) { 597 | clearOnError(self.gl, "Texture format not supported", texture, callback); 598 | return; 599 | } 600 | // Upload the parsed DXT data to the texture. 601 | self._uploadCompressedData(dxtData, width, height, levels, internalFormat, texture, callback); 602 | }, function(error) { 603 | clearOnError(self.gl, error, texture, callback); 604 | }); 605 | } else { 606 | clearOnError(self.gl, xhr.statusText, texture, callback); 607 | } 608 | }, false); 609 | xhr.open('GET', src, true); 610 | xhr.responseType = 'arraybuffer'; 611 | xhr.send(null); 612 | 613 | return texture; 614 | } 615 | 616 | // Loads a PVR file into the given texture. 617 | // If no texture is provided one is created and returned. 618 | TextureLoader.prototype.loadPVR = function(src, texture, callback) { 619 | var self = this; 620 | if(!texture) { 621 | texture = this.gl.createTexture(); 622 | } 623 | 624 | // Load the file via XHR. 625 | var xhr = new XMLHttpRequest(); 626 | xhr.addEventListener('load', function (ev) { 627 | if (xhr.status == 200) { 628 | // If the file loaded successfully parse it. 629 | parsePVR(xhr.response, function(dxtData, width, height, levels, internalFormat) { 630 | if (!self._formatSupported(internalFormat)) { 631 | clearOnError(self.gl, "Texture format not supported", texture, callback); 632 | return; 633 | } 634 | // Upload the parsed PVR data to the texture. 635 | self._uploadCompressedData(dxtData, width, height, levels, internalFormat, texture, callback); 636 | }, function(error) { 637 | clearOnError(self.gl, error, texture, callback); 638 | }); 639 | } else { 640 | clearOnError(self.gl, xhr.statusText, texture, callback); 641 | } 642 | }, false); 643 | xhr.open('GET', src, true); 644 | xhr.responseType = 'arraybuffer'; 645 | xhr.send(null); 646 | 647 | return texture; 648 | } 649 | 650 | // Loads a texture from a file. Guesses the type based on extension. 651 | // If no texture is provided one is created and returned. 652 | TextureLoader.prototype.loadTexture = function(src, texture, callback) { 653 | // Shamelessly lifted from StackOverflow :) 654 | // http://stackoverflow.com/questions/680929 655 | var re = /(?:\.([^.]+))?$/; 656 | var ext = re.exec(src)[1] || ''; 657 | ext = ext.toLowerCase(); 658 | 659 | switch(ext) { 660 | case 'dds': 661 | return this.loadDDS(src, texture, callback); 662 | case 'pvr': 663 | return this.loadPVR(src, texture, callback); 664 | default: 665 | return this.loadIMG(src, texture, callback); 666 | } 667 | } 668 | 669 | // Sets a texture to a solid RGBA color 670 | // If no texture is provided one is created and returned. 671 | TextureLoader.prototype.makeSolidColor = function(r, g, b, a, texture) { 672 | var gl = this.gl; 673 | var data = new Uint8Array([r, g, b, a]); 674 | if(!texture) { 675 | texture = gl.createTexture(); 676 | } 677 | gl.bindTexture(gl.TEXTURE_2D, texture); 678 | gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, data); 679 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); 680 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); 681 | return texture; 682 | } 683 | } 684 | 685 | return TextureLoader; 686 | })(); 687 | -------------------------------------------------------------------------------- /src/wglu-url.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015, Brandon Jones. 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | /* 24 | Provides a simple way to get values from the query string if they're present 25 | and use a default value if not. Not strictly a "WebGL" utility, but I use it 26 | frequently enough for debugging that I wanted to include it here. 27 | 28 | Example: 29 | For the URL http://example.com/index.html?particleCount=1000 30 | 31 | WGLUUrl.getInt("particleCount", 100); // URL overrides, returns 1000 32 | WGLUUrl.getInt("particleSize", 10); // Not in URL, returns default of 10 33 | */ 34 | var WGLUUrl = (function() { 35 | 36 | "use strict"; 37 | 38 | var urlArgs = null; 39 | 40 | function ensureArgsCached() { 41 | if (!urlArgs) { 42 | urlArgs = {}; 43 | var query = window.location.search.substring(1); 44 | var vars = query.split("&"); 45 | for (var i = 0; i < vars.length; i++) { 46 | var pair = vars[i].split("="); 47 | urlArgs[pair[0].toLowerCase()] = unescape(pair[1]); 48 | } 49 | } 50 | } 51 | 52 | function getString(name, defaultValue) { 53 | ensureArgsCached(); 54 | var lcaseName = name.toLowerCase(); 55 | if (lcaseName in urlArgs) { 56 | return urlArgs[lcaseName]; 57 | } 58 | return defaultValue; 59 | } 60 | 61 | function getInt(name, defaultValue) { 62 | ensureArgsCached(); 63 | var lcaseName = name.toLowerCase(); 64 | if (lcaseName in urlArgs) { 65 | return parseInt(urlArgs[lcaseName], 10); 66 | } 67 | return defaultValue; 68 | } 69 | 70 | function getFloat(name, defaultValue) { 71 | ensureArgsCached(); 72 | var lcaseName = name.toLowerCase(); 73 | if (lcaseName in urlArgs) { 74 | return parseFloat(urlArgs[lcaseName]); 75 | } 76 | return defaultValue; 77 | } 78 | 79 | function getBool(name, defaultValue) { 80 | ensureArgsCached(); 81 | var lcaseName = name.toLowerCase(); 82 | if (lcaseName in urlArgs) { 83 | return parseInt(urlArgs[lcaseName], 10) != 0; 84 | } 85 | return defaultValue; 86 | } 87 | 88 | return { 89 | getString: getString, 90 | getInt: getInt, 91 | getFloat: getFloat, 92 | getBool: getBool 93 | }; 94 | })(); 95 | -------------------------------------------------------------------------------- /stats-test.html: -------------------------------------------------------------------------------- 1 | 2 | 23 | 24 | 25 | 26 | 27 | 28 | WGLUStats Test Page 29 | 30 | 40 | 41 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /third-party/gl-matrix-min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview gl-matrix - High performance matrix and vector operations 3 | * @author Brandon Jones 4 | * @author Colin MacKenzie IV 5 | * @version 2.3.2 6 | */ 7 | 8 | /* Copyright (c) 2015, Brandon Jones, Colin MacKenzie IV. 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in 18 | all copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 26 | THE SOFTWARE. */ 27 | 28 | !function(t,a){if("object"==typeof exports&&"object"==typeof module)module.exports=a();else if("function"==typeof define&&define.amd)define(a);else{var n=a();for(var r in n)("object"==typeof exports?exports:t)[r]=n[r]}}(this,function(){return function(t){function a(r){if(n[r])return n[r].exports;var o=n[r]={exports:{},id:r,loaded:!1};return t[r].call(o.exports,o,o.exports,a),o.loaded=!0,o.exports}var n={};return a.m=t,a.c=n,a.p="",a(0)}([function(t,a,n){a.glMatrix=n(1),a.mat2=n(2),a.mat2d=n(3),a.mat3=n(4),a.mat4=n(5),a.quat=n(6),a.vec2=n(9),a.vec3=n(7),a.vec4=n(8)},function(t,a){var n={};n.EPSILON=1e-6,n.ARRAY_TYPE="undefined"!=typeof Float32Array?Float32Array:Array,n.RANDOM=Math.random,n.ENABLE_SIMD=!1,n.SIMD_AVAILABLE=n.ARRAY_TYPE===Float32Array&&"SIMD"in this,n.USE_SIMD=n.ENABLE_SIMD&&n.SIMD_AVAILABLE,n.setMatrixArrayType=function(t){n.ARRAY_TYPE=t};var r=Math.PI/180;n.toRadian=function(t){return t*r},t.exports=n},function(t,a,n){var r=n(1),o={};o.create=function(){var t=new r.ARRAY_TYPE(4);return t[0]=1,t[1]=0,t[2]=0,t[3]=1,t},o.clone=function(t){var a=new r.ARRAY_TYPE(4);return a[0]=t[0],a[1]=t[1],a[2]=t[2],a[3]=t[3],a},o.copy=function(t,a){return t[0]=a[0],t[1]=a[1],t[2]=a[2],t[3]=a[3],t},o.identity=function(t){return t[0]=1,t[1]=0,t[2]=0,t[3]=1,t},o.transpose=function(t,a){if(t===a){var n=a[1];t[1]=a[2],t[2]=n}else t[0]=a[0],t[1]=a[2],t[2]=a[1],t[3]=a[3];return t},o.invert=function(t,a){var n=a[0],r=a[1],o=a[2],l=a[3],u=n*l-o*r;return u?(u=1/u,t[0]=l*u,t[1]=-r*u,t[2]=-o*u,t[3]=n*u,t):null},o.adjoint=function(t,a){var n=a[0];return t[0]=a[3],t[1]=-a[1],t[2]=-a[2],t[3]=n,t},o.determinant=function(t){return t[0]*t[3]-t[2]*t[1]},o.multiply=function(t,a,n){var r=a[0],o=a[1],l=a[2],u=a[3],e=n[0],M=n[1],i=n[2],s=n[3];return t[0]=r*e+l*M,t[1]=o*e+u*M,t[2]=r*i+l*s,t[3]=o*i+u*s,t},o.mul=o.multiply,o.rotate=function(t,a,n){var r=a[0],o=a[1],l=a[2],u=a[3],e=Math.sin(n),M=Math.cos(n);return t[0]=r*M+l*e,t[1]=o*M+u*e,t[2]=r*-e+l*M,t[3]=o*-e+u*M,t},o.scale=function(t,a,n){var r=a[0],o=a[1],l=a[2],u=a[3],e=n[0],M=n[1];return t[0]=r*e,t[1]=o*e,t[2]=l*M,t[3]=u*M,t},o.fromRotation=function(t,a){var n=Math.sin(a),r=Math.cos(a);return t[0]=r,t[1]=n,t[2]=-n,t[3]=r,t},o.fromScaling=function(t,a){return t[0]=a[0],t[1]=0,t[2]=0,t[3]=a[1],t},o.str=function(t){return"mat2("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+")"},o.frob=function(t){return Math.sqrt(Math.pow(t[0],2)+Math.pow(t[1],2)+Math.pow(t[2],2)+Math.pow(t[3],2))},o.LDU=function(t,a,n,r){return t[2]=r[2]/r[0],n[0]=r[0],n[1]=r[1],n[3]=r[3]-t[2]*n[1],[t,a,n]},t.exports=o},function(t,a,n){var r=n(1),o={};o.create=function(){var t=new r.ARRAY_TYPE(6);return t[0]=1,t[1]=0,t[2]=0,t[3]=1,t[4]=0,t[5]=0,t},o.clone=function(t){var a=new r.ARRAY_TYPE(6);return a[0]=t[0],a[1]=t[1],a[2]=t[2],a[3]=t[3],a[4]=t[4],a[5]=t[5],a},o.copy=function(t,a){return t[0]=a[0],t[1]=a[1],t[2]=a[2],t[3]=a[3],t[4]=a[4],t[5]=a[5],t},o.identity=function(t){return t[0]=1,t[1]=0,t[2]=0,t[3]=1,t[4]=0,t[5]=0,t},o.invert=function(t,a){var n=a[0],r=a[1],o=a[2],l=a[3],u=a[4],e=a[5],M=n*l-r*o;return M?(M=1/M,t[0]=l*M,t[1]=-r*M,t[2]=-o*M,t[3]=n*M,t[4]=(o*e-l*u)*M,t[5]=(r*u-n*e)*M,t):null},o.determinant=function(t){return t[0]*t[3]-t[1]*t[2]},o.multiply=function(t,a,n){var r=a[0],o=a[1],l=a[2],u=a[3],e=a[4],M=a[5],i=n[0],s=n[1],c=n[2],D=n[3],S=n[4],I=n[5];return t[0]=r*i+l*s,t[1]=o*i+u*s,t[2]=r*c+l*D,t[3]=o*c+u*D,t[4]=r*S+l*I+e,t[5]=o*S+u*I+M,t},o.mul=o.multiply,o.rotate=function(t,a,n){var r=a[0],o=a[1],l=a[2],u=a[3],e=a[4],M=a[5],i=Math.sin(n),s=Math.cos(n);return t[0]=r*s+l*i,t[1]=o*s+u*i,t[2]=r*-i+l*s,t[3]=o*-i+u*s,t[4]=e,t[5]=M,t},o.scale=function(t,a,n){var r=a[0],o=a[1],l=a[2],u=a[3],e=a[4],M=a[5],i=n[0],s=n[1];return t[0]=r*i,t[1]=o*i,t[2]=l*s,t[3]=u*s,t[4]=e,t[5]=M,t},o.translate=function(t,a,n){var r=a[0],o=a[1],l=a[2],u=a[3],e=a[4],M=a[5],i=n[0],s=n[1];return t[0]=r,t[1]=o,t[2]=l,t[3]=u,t[4]=r*i+l*s+e,t[5]=o*i+u*s+M,t},o.fromRotation=function(t,a){var n=Math.sin(a),r=Math.cos(a);return t[0]=r,t[1]=n,t[2]=-n,t[3]=r,t[4]=0,t[5]=0,t},o.fromScaling=function(t,a){return t[0]=a[0],t[1]=0,t[2]=0,t[3]=a[1],t[4]=0,t[5]=0,t},o.fromTranslation=function(t,a){return t[0]=1,t[1]=0,t[2]=0,t[3]=1,t[4]=a[0],t[5]=a[1],t},o.str=function(t){return"mat2d("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+", "+t[4]+", "+t[5]+")"},o.frob=function(t){return Math.sqrt(Math.pow(t[0],2)+Math.pow(t[1],2)+Math.pow(t[2],2)+Math.pow(t[3],2)+Math.pow(t[4],2)+Math.pow(t[5],2)+1)},t.exports=o},function(t,a,n){var r=n(1),o={};o.create=function(){var t=new r.ARRAY_TYPE(9);return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=1,t[5]=0,t[6]=0,t[7]=0,t[8]=1,t},o.fromMat4=function(t,a){return t[0]=a[0],t[1]=a[1],t[2]=a[2],t[3]=a[4],t[4]=a[5],t[5]=a[6],t[6]=a[8],t[7]=a[9],t[8]=a[10],t},o.clone=function(t){var a=new r.ARRAY_TYPE(9);return a[0]=t[0],a[1]=t[1],a[2]=t[2],a[3]=t[3],a[4]=t[4],a[5]=t[5],a[6]=t[6],a[7]=t[7],a[8]=t[8],a},o.copy=function(t,a){return t[0]=a[0],t[1]=a[1],t[2]=a[2],t[3]=a[3],t[4]=a[4],t[5]=a[5],t[6]=a[6],t[7]=a[7],t[8]=a[8],t},o.identity=function(t){return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=1,t[5]=0,t[6]=0,t[7]=0,t[8]=1,t},o.transpose=function(t,a){if(t===a){var n=a[1],r=a[2],o=a[5];t[1]=a[3],t[2]=a[6],t[3]=n,t[5]=a[7],t[6]=r,t[7]=o}else t[0]=a[0],t[1]=a[3],t[2]=a[6],t[3]=a[1],t[4]=a[4],t[5]=a[7],t[6]=a[2],t[7]=a[5],t[8]=a[8];return t},o.invert=function(t,a){var n=a[0],r=a[1],o=a[2],l=a[3],u=a[4],e=a[5],M=a[6],i=a[7],s=a[8],c=s*u-e*i,D=-s*l+e*M,S=i*l-u*M,I=n*c+r*D+o*S;return I?(I=1/I,t[0]=c*I,t[1]=(-s*r+o*i)*I,t[2]=(e*r-o*u)*I,t[3]=D*I,t[4]=(s*n-o*M)*I,t[5]=(-e*n+o*l)*I,t[6]=S*I,t[7]=(-i*n+r*M)*I,t[8]=(u*n-r*l)*I,t):null},o.adjoint=function(t,a){var n=a[0],r=a[1],o=a[2],l=a[3],u=a[4],e=a[5],M=a[6],i=a[7],s=a[8];return t[0]=u*s-e*i,t[1]=o*i-r*s,t[2]=r*e-o*u,t[3]=e*M-l*s,t[4]=n*s-o*M,t[5]=o*l-n*e,t[6]=l*i-u*M,t[7]=r*M-n*i,t[8]=n*u-r*l,t},o.determinant=function(t){var a=t[0],n=t[1],r=t[2],o=t[3],l=t[4],u=t[5],e=t[6],M=t[7],i=t[8];return a*(i*l-u*M)+n*(-i*o+u*e)+r*(M*o-l*e)},o.multiply=function(t,a,n){var r=a[0],o=a[1],l=a[2],u=a[3],e=a[4],M=a[5],i=a[6],s=a[7],c=a[8],D=n[0],S=n[1],I=n[2],x=n[3],f=n[4],F=n[5],m=n[6],h=n[7],d=n[8];return t[0]=D*r+S*u+I*i,t[1]=D*o+S*e+I*s,t[2]=D*l+S*M+I*c,t[3]=x*r+f*u+F*i,t[4]=x*o+f*e+F*s,t[5]=x*l+f*M+F*c,t[6]=m*r+h*u+d*i,t[7]=m*o+h*e+d*s,t[8]=m*l+h*M+d*c,t},o.mul=o.multiply,o.translate=function(t,a,n){var r=a[0],o=a[1],l=a[2],u=a[3],e=a[4],M=a[5],i=a[6],s=a[7],c=a[8],D=n[0],S=n[1];return t[0]=r,t[1]=o,t[2]=l,t[3]=u,t[4]=e,t[5]=M,t[6]=D*r+S*u+i,t[7]=D*o+S*e+s,t[8]=D*l+S*M+c,t},o.rotate=function(t,a,n){var r=a[0],o=a[1],l=a[2],u=a[3],e=a[4],M=a[5],i=a[6],s=a[7],c=a[8],D=Math.sin(n),S=Math.cos(n);return t[0]=S*r+D*u,t[1]=S*o+D*e,t[2]=S*l+D*M,t[3]=S*u-D*r,t[4]=S*e-D*o,t[5]=S*M-D*l,t[6]=i,t[7]=s,t[8]=c,t},o.scale=function(t,a,n){var r=n[0],o=n[1];return t[0]=r*a[0],t[1]=r*a[1],t[2]=r*a[2],t[3]=o*a[3],t[4]=o*a[4],t[5]=o*a[5],t[6]=a[6],t[7]=a[7],t[8]=a[8],t},o.fromTranslation=function(t,a){return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=1,t[5]=0,t[6]=a[0],t[7]=a[1],t[8]=1,t},o.fromRotation=function(t,a){var n=Math.sin(a),r=Math.cos(a);return t[0]=r,t[1]=n,t[2]=0,t[3]=-n,t[4]=r,t[5]=0,t[6]=0,t[7]=0,t[8]=1,t},o.fromScaling=function(t,a){return t[0]=a[0],t[1]=0,t[2]=0,t[3]=0,t[4]=a[1],t[5]=0,t[6]=0,t[7]=0,t[8]=1,t},o.fromMat2d=function(t,a){return t[0]=a[0],t[1]=a[1],t[2]=0,t[3]=a[2],t[4]=a[3],t[5]=0,t[6]=a[4],t[7]=a[5],t[8]=1,t},o.fromQuat=function(t,a){var n=a[0],r=a[1],o=a[2],l=a[3],u=n+n,e=r+r,M=o+o,i=n*u,s=r*u,c=r*e,D=o*u,S=o*e,I=o*M,x=l*u,f=l*e,F=l*M;return t[0]=1-c-I,t[3]=s-F,t[6]=D+f,t[1]=s+F,t[4]=1-i-I,t[7]=S-x,t[2]=D-f,t[5]=S+x,t[8]=1-i-c,t},o.normalFromMat4=function(t,a){var n=a[0],r=a[1],o=a[2],l=a[3],u=a[4],e=a[5],M=a[6],i=a[7],s=a[8],c=a[9],D=a[10],S=a[11],I=a[12],x=a[13],f=a[14],F=a[15],m=n*e-r*u,h=n*M-o*u,d=n*i-l*u,v=r*M-o*e,z=r*i-l*e,p=o*i-l*M,w=s*x-c*I,A=s*f-D*I,R=s*F-S*I,b=c*f-D*x,Y=c*F-S*x,q=D*F-S*f,y=m*q-h*Y+d*b+v*R-z*A+p*w;return y?(y=1/y,t[0]=(e*q-M*Y+i*b)*y,t[1]=(M*R-u*q-i*A)*y,t[2]=(u*Y-e*R+i*w)*y,t[3]=(o*Y-r*q-l*b)*y,t[4]=(n*q-o*R+l*A)*y,t[5]=(r*R-n*Y-l*w)*y,t[6]=(x*p-f*z+F*v)*y,t[7]=(f*d-I*p-F*h)*y,t[8]=(I*z-x*d+F*m)*y,t):null},o.str=function(t){return"mat3("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+", "+t[4]+", "+t[5]+", "+t[6]+", "+t[7]+", "+t[8]+")"},o.frob=function(t){return Math.sqrt(Math.pow(t[0],2)+Math.pow(t[1],2)+Math.pow(t[2],2)+Math.pow(t[3],2)+Math.pow(t[4],2)+Math.pow(t[5],2)+Math.pow(t[6],2)+Math.pow(t[7],2)+Math.pow(t[8],2))},t.exports=o},function(t,a,n){var r=n(1),o={scalar:{},SIMD:{}};o.create=function(){var t=new r.ARRAY_TYPE(16);return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=1,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[10]=1,t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,t},o.clone=function(t){var a=new r.ARRAY_TYPE(16);return a[0]=t[0],a[1]=t[1],a[2]=t[2],a[3]=t[3],a[4]=t[4],a[5]=t[5],a[6]=t[6],a[7]=t[7],a[8]=t[8],a[9]=t[9],a[10]=t[10],a[11]=t[11],a[12]=t[12],a[13]=t[13],a[14]=t[14],a[15]=t[15],a},o.copy=function(t,a){return t[0]=a[0],t[1]=a[1],t[2]=a[2],t[3]=a[3],t[4]=a[4],t[5]=a[5],t[6]=a[6],t[7]=a[7],t[8]=a[8],t[9]=a[9],t[10]=a[10],t[11]=a[11],t[12]=a[12],t[13]=a[13],t[14]=a[14],t[15]=a[15],t},o.identity=function(t){return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=1,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[10]=1,t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,t},o.scalar.transpose=function(t,a){if(t===a){var n=a[1],r=a[2],o=a[3],l=a[6],u=a[7],e=a[11];t[1]=a[4],t[2]=a[8],t[3]=a[12],t[4]=n,t[6]=a[9],t[7]=a[13],t[8]=r,t[9]=l,t[11]=a[14],t[12]=o,t[13]=u,t[14]=e}else t[0]=a[0],t[1]=a[4],t[2]=a[8],t[3]=a[12],t[4]=a[1],t[5]=a[5],t[6]=a[9],t[7]=a[13],t[8]=a[2],t[9]=a[6],t[10]=a[10],t[11]=a[14],t[12]=a[3],t[13]=a[7],t[14]=a[11],t[15]=a[15];return t},o.SIMD.transpose=function(t,a){var n,r,o,l,u,e,M,i,s,c;return n=SIMD.Float32x4.load(a,0),r=SIMD.Float32x4.load(a,4),o=SIMD.Float32x4.load(a,8),l=SIMD.Float32x4.load(a,12),u=SIMD.Float32x4.shuffle(n,r,0,1,4,5),e=SIMD.Float32x4.shuffle(o,l,0,1,4,5),M=SIMD.Float32x4.shuffle(u,e,0,2,4,6),i=SIMD.Float32x4.shuffle(u,e,1,3,5,7),SIMD.Float32x4.store(t,0,M),SIMD.Float32x4.store(t,4,i),u=SIMD.Float32x4.shuffle(n,r,2,3,6,7),e=SIMD.Float32x4.shuffle(o,l,2,3,6,7),s=SIMD.Float32x4.shuffle(u,e,0,2,4,6),c=SIMD.Float32x4.shuffle(u,e,1,3,5,7),SIMD.Float32x4.store(t,8,s),SIMD.Float32x4.store(t,12,c),t},o.transpose=r.USE_SIMD?o.SIMD.transpose:o.scalar.transpose,o.scalar.invert=function(t,a){var n=a[0],r=a[1],o=a[2],l=a[3],u=a[4],e=a[5],M=a[6],i=a[7],s=a[8],c=a[9],D=a[10],S=a[11],I=a[12],x=a[13],f=a[14],F=a[15],m=n*e-r*u,h=n*M-o*u,d=n*i-l*u,v=r*M-o*e,z=r*i-l*e,p=o*i-l*M,w=s*x-c*I,A=s*f-D*I,R=s*F-S*I,b=c*f-D*x,Y=c*F-S*x,q=D*F-S*f,y=m*q-h*Y+d*b+v*R-z*A+p*w;return y?(y=1/y,t[0]=(e*q-M*Y+i*b)*y,t[1]=(o*Y-r*q-l*b)*y,t[2]=(x*p-f*z+F*v)*y,t[3]=(D*z-c*p-S*v)*y,t[4]=(M*R-u*q-i*A)*y,t[5]=(n*q-o*R+l*A)*y,t[6]=(f*d-I*p-F*h)*y,t[7]=(s*p-D*d+S*h)*y,t[8]=(u*Y-e*R+i*w)*y,t[9]=(r*R-n*Y-l*w)*y,t[10]=(I*z-x*d+F*m)*y,t[11]=(c*d-s*z-S*m)*y,t[12]=(e*A-u*b-M*w)*y,t[13]=(n*b-r*A+o*w)*y,t[14]=(x*h-I*v-f*m)*y,t[15]=(s*v-c*h+D*m)*y,t):null},o.SIMD.invert=function(t,a){var n,r,o,l,u,e,M,i,s,c,D=SIMD.Float32x4.load(a,0),S=SIMD.Float32x4.load(a,4),I=SIMD.Float32x4.load(a,8),x=SIMD.Float32x4.load(a,12);return u=SIMD.Float32x4.shuffle(D,S,0,1,4,5),r=SIMD.Float32x4.shuffle(I,x,0,1,4,5),n=SIMD.Float32x4.shuffle(u,r,0,2,4,6),r=SIMD.Float32x4.shuffle(r,u,1,3,5,7),u=SIMD.Float32x4.shuffle(D,S,2,3,6,7),l=SIMD.Float32x4.shuffle(I,x,2,3,6,7),o=SIMD.Float32x4.shuffle(u,l,0,2,4,6),l=SIMD.Float32x4.shuffle(l,u,1,3,5,7),u=SIMD.Float32x4.mul(o,l),u=SIMD.Float32x4.swizzle(u,1,0,3,2),e=SIMD.Float32x4.mul(r,u),M=SIMD.Float32x4.mul(n,u),u=SIMD.Float32x4.swizzle(u,2,3,0,1),e=SIMD.Float32x4.sub(SIMD.Float32x4.mul(r,u),e),M=SIMD.Float32x4.sub(SIMD.Float32x4.mul(n,u),M),M=SIMD.Float32x4.swizzle(M,2,3,0,1),u=SIMD.Float32x4.mul(r,o),u=SIMD.Float32x4.swizzle(u,1,0,3,2),e=SIMD.Float32x4.add(SIMD.Float32x4.mul(l,u),e),s=SIMD.Float32x4.mul(n,u),u=SIMD.Float32x4.swizzle(u,2,3,0,1),e=SIMD.Float32x4.sub(e,SIMD.Float32x4.mul(l,u)),s=SIMD.Float32x4.sub(SIMD.Float32x4.mul(n,u),s),s=SIMD.Float32x4.swizzle(s,2,3,0,1),u=SIMD.Float32x4.mul(SIMD.Float32x4.swizzle(r,2,3,0,1),l),u=SIMD.Float32x4.swizzle(u,1,0,3,2),o=SIMD.Float32x4.swizzle(o,2,3,0,1),e=SIMD.Float32x4.add(SIMD.Float32x4.mul(o,u),e),i=SIMD.Float32x4.mul(n,u),u=SIMD.Float32x4.swizzle(u,2,3,0,1),e=SIMD.Float32x4.sub(e,SIMD.Float32x4.mul(o,u)),i=SIMD.Float32x4.sub(SIMD.Float32x4.mul(n,u),i),i=SIMD.Float32x4.swizzle(i,2,3,0,1),u=SIMD.Float32x4.mul(n,r),u=SIMD.Float32x4.swizzle(u,1,0,3,2),i=SIMD.Float32x4.add(SIMD.Float32x4.mul(l,u),i),s=SIMD.Float32x4.sub(SIMD.Float32x4.mul(o,u),s),u=SIMD.Float32x4.swizzle(u,2,3,0,1),i=SIMD.Float32x4.sub(SIMD.Float32x4.mul(l,u),i),s=SIMD.Float32x4.sub(s,SIMD.Float32x4.mul(o,u)),u=SIMD.Float32x4.mul(n,l),u=SIMD.Float32x4.swizzle(u,1,0,3,2),M=SIMD.Float32x4.sub(M,SIMD.Float32x4.mul(o,u)),i=SIMD.Float32x4.add(SIMD.Float32x4.mul(r,u),i),u=SIMD.Float32x4.swizzle(u,2,3,0,1),M=SIMD.Float32x4.add(SIMD.Float32x4.mul(o,u),M),i=SIMD.Float32x4.sub(i,SIMD.Float32x4.mul(r,u)),u=SIMD.Float32x4.mul(n,o),u=SIMD.Float32x4.swizzle(u,1,0,3,2),M=SIMD.Float32x4.add(SIMD.Float32x4.mul(l,u),M),s=SIMD.Float32x4.sub(s,SIMD.Float32x4.mul(r,u)),u=SIMD.Float32x4.swizzle(u,2,3,0,1),M=SIMD.Float32x4.sub(M,SIMD.Float32x4.mul(l,u)),s=SIMD.Float32x4.add(SIMD.Float32x4.mul(r,u),s),c=SIMD.Float32x4.mul(n,e),c=SIMD.Float32x4.add(SIMD.Float32x4.swizzle(c,2,3,0,1),c),c=SIMD.Float32x4.add(SIMD.Float32x4.swizzle(c,1,0,3,2),c),u=SIMD.Float32x4.reciprocalApproximation(c),c=SIMD.Float32x4.sub(SIMD.Float32x4.add(u,u),SIMD.Float32x4.mul(c,SIMD.Float32x4.mul(u,u))),(c=SIMD.Float32x4.swizzle(c,0,0,0,0))?(SIMD.Float32x4.store(t,0,SIMD.Float32x4.mul(c,e)),SIMD.Float32x4.store(t,4,SIMD.Float32x4.mul(c,M)),SIMD.Float32x4.store(t,8,SIMD.Float32x4.mul(c,i)),SIMD.Float32x4.store(t,12,SIMD.Float32x4.mul(c,s)),t):null},o.invert=r.USE_SIMD?o.SIMD.invert:o.scalar.invert,o.scalar.adjoint=function(t,a){var n=a[0],r=a[1],o=a[2],l=a[3],u=a[4],e=a[5],M=a[6],i=a[7],s=a[8],c=a[9],D=a[10],S=a[11],I=a[12],x=a[13],f=a[14],F=a[15];return t[0]=e*(D*F-S*f)-c*(M*F-i*f)+x*(M*S-i*D),t[1]=-(r*(D*F-S*f)-c*(o*F-l*f)+x*(o*S-l*D)),t[2]=r*(M*F-i*f)-e*(o*F-l*f)+x*(o*i-l*M),t[3]=-(r*(M*S-i*D)-e*(o*S-l*D)+c*(o*i-l*M)),t[4]=-(u*(D*F-S*f)-s*(M*F-i*f)+I*(M*S-i*D)),t[5]=n*(D*F-S*f)-s*(o*F-l*f)+I*(o*S-l*D),t[6]=-(n*(M*F-i*f)-u*(o*F-l*f)+I*(o*i-l*M)),t[7]=n*(M*S-i*D)-u*(o*S-l*D)+s*(o*i-l*M),t[8]=u*(c*F-S*x)-s*(e*F-i*x)+I*(e*S-i*c),t[9]=-(n*(c*F-S*x)-s*(r*F-l*x)+I*(r*S-l*c)),t[10]=n*(e*F-i*x)-u*(r*F-l*x)+I*(r*i-l*e),t[11]=-(n*(e*S-i*c)-u*(r*S-l*c)+s*(r*i-l*e)),t[12]=-(u*(c*f-D*x)-s*(e*f-M*x)+I*(e*D-M*c)),t[13]=n*(c*f-D*x)-s*(r*f-o*x)+I*(r*D-o*c),t[14]=-(n*(e*f-M*x)-u*(r*f-o*x)+I*(r*M-o*e)),t[15]=n*(e*D-M*c)-u*(r*D-o*c)+s*(r*M-o*e),t},o.SIMD.adjoint=function(t,a){var n,r,o,l,u,e,M,i,s,c,D,S,I,n=SIMD.Float32x4.load(a,0),r=SIMD.Float32x4.load(a,4),o=SIMD.Float32x4.load(a,8),l=SIMD.Float32x4.load(a,12);return s=SIMD.Float32x4.shuffle(n,r,0,1,4,5),e=SIMD.Float32x4.shuffle(o,l,0,1,4,5),u=SIMD.Float32x4.shuffle(s,e,0,2,4,6),e=SIMD.Float32x4.shuffle(e,s,1,3,5,7),s=SIMD.Float32x4.shuffle(n,r,2,3,6,7),i=SIMD.Float32x4.shuffle(o,l,2,3,6,7),M=SIMD.Float32x4.shuffle(s,i,0,2,4,6),i=SIMD.Float32x4.shuffle(i,s,1,3,5,7),s=SIMD.Float32x4.mul(M,i),s=SIMD.Float32x4.swizzle(s,1,0,3,2),c=SIMD.Float32x4.mul(e,s),D=SIMD.Float32x4.mul(u,s),s=SIMD.Float32x4.swizzle(s,2,3,0,1),c=SIMD.Float32x4.sub(SIMD.Float32x4.mul(e,s),c),D=SIMD.Float32x4.sub(SIMD.Float32x4.mul(u,s),D),D=SIMD.Float32x4.swizzle(D,2,3,0,1),s=SIMD.Float32x4.mul(e,M),s=SIMD.Float32x4.swizzle(s,1,0,3,2),c=SIMD.Float32x4.add(SIMD.Float32x4.mul(i,s),c),I=SIMD.Float32x4.mul(u,s),s=SIMD.Float32x4.swizzle(s,2,3,0,1),c=SIMD.Float32x4.sub(c,SIMD.Float32x4.mul(i,s)),I=SIMD.Float32x4.sub(SIMD.Float32x4.mul(u,s),I),I=SIMD.Float32x4.swizzle(I,2,3,0,1),s=SIMD.Float32x4.mul(SIMD.Float32x4.swizzle(e,2,3,0,1),i),s=SIMD.Float32x4.swizzle(s,1,0,3,2),M=SIMD.Float32x4.swizzle(M,2,3,0,1),c=SIMD.Float32x4.add(SIMD.Float32x4.mul(M,s),c),S=SIMD.Float32x4.mul(u,s),s=SIMD.Float32x4.swizzle(s,2,3,0,1),c=SIMD.Float32x4.sub(c,SIMD.Float32x4.mul(M,s)),S=SIMD.Float32x4.sub(SIMD.Float32x4.mul(u,s),S),S=SIMD.Float32x4.swizzle(S,2,3,0,1),s=SIMD.Float32x4.mul(u,e),s=SIMD.Float32x4.swizzle(s,1,0,3,2),S=SIMD.Float32x4.add(SIMD.Float32x4.mul(i,s),S),I=SIMD.Float32x4.sub(SIMD.Float32x4.mul(M,s),I),s=SIMD.Float32x4.swizzle(s,2,3,0,1),S=SIMD.Float32x4.sub(SIMD.Float32x4.mul(i,s),S),I=SIMD.Float32x4.sub(I,SIMD.Float32x4.mul(M,s)),s=SIMD.Float32x4.mul(u,i),s=SIMD.Float32x4.swizzle(s,1,0,3,2),D=SIMD.Float32x4.sub(D,SIMD.Float32x4.mul(M,s)),S=SIMD.Float32x4.add(SIMD.Float32x4.mul(e,s),S),s=SIMD.Float32x4.swizzle(s,2,3,0,1),D=SIMD.Float32x4.add(SIMD.Float32x4.mul(M,s),D),S=SIMD.Float32x4.sub(S,SIMD.Float32x4.mul(e,s)),s=SIMD.Float32x4.mul(u,M),s=SIMD.Float32x4.swizzle(s,1,0,3,2),D=SIMD.Float32x4.add(SIMD.Float32x4.mul(i,s),D),I=SIMD.Float32x4.sub(I,SIMD.Float32x4.mul(e,s)),s=SIMD.Float32x4.swizzle(s,2,3,0,1),D=SIMD.Float32x4.sub(D,SIMD.Float32x4.mul(i,s)),I=SIMD.Float32x4.add(SIMD.Float32x4.mul(e,s),I),SIMD.Float32x4.store(t,0,c),SIMD.Float32x4.store(t,4,D),SIMD.Float32x4.store(t,8,S),SIMD.Float32x4.store(t,12,I),t},o.adjoint=r.USE_SIMD?o.SIMD.adjoint:o.scalar.adjoint,o.determinant=function(t){var a=t[0],n=t[1],r=t[2],o=t[3],l=t[4],u=t[5],e=t[6],M=t[7],i=t[8],s=t[9],c=t[10],D=t[11],S=t[12],I=t[13],x=t[14],f=t[15],F=a*u-n*l,m=a*e-r*l,h=a*M-o*l,d=n*e-r*u,v=n*M-o*u,z=r*M-o*e,p=i*I-s*S,w=i*x-c*S,A=i*f-D*S,R=s*x-c*I,b=s*f-D*I,Y=c*f-D*x;return F*Y-m*b+h*R+d*A-v*w+z*p},o.SIMD.multiply=function(t,a,n){var r=SIMD.Float32x4.load(a,0),o=SIMD.Float32x4.load(a,4),l=SIMD.Float32x4.load(a,8),u=SIMD.Float32x4.load(a,12),e=SIMD.Float32x4.load(n,0),M=SIMD.Float32x4.add(SIMD.Float32x4.mul(SIMD.Float32x4.swizzle(e,0,0,0,0),r),SIMD.Float32x4.add(SIMD.Float32x4.mul(SIMD.Float32x4.swizzle(e,1,1,1,1),o),SIMD.Float32x4.add(SIMD.Float32x4.mul(SIMD.Float32x4.swizzle(e,2,2,2,2),l),SIMD.Float32x4.mul(SIMD.Float32x4.swizzle(e,3,3,3,3),u))));SIMD.Float32x4.store(t,0,M);var i=SIMD.Float32x4.load(n,4),s=SIMD.Float32x4.add(SIMD.Float32x4.mul(SIMD.Float32x4.swizzle(i,0,0,0,0),r),SIMD.Float32x4.add(SIMD.Float32x4.mul(SIMD.Float32x4.swizzle(i,1,1,1,1),o),SIMD.Float32x4.add(SIMD.Float32x4.mul(SIMD.Float32x4.swizzle(i,2,2,2,2),l),SIMD.Float32x4.mul(SIMD.Float32x4.swizzle(i,3,3,3,3),u))));SIMD.Float32x4.store(t,4,s);var c=SIMD.Float32x4.load(n,8),D=SIMD.Float32x4.add(SIMD.Float32x4.mul(SIMD.Float32x4.swizzle(c,0,0,0,0),r),SIMD.Float32x4.add(SIMD.Float32x4.mul(SIMD.Float32x4.swizzle(c,1,1,1,1),o),SIMD.Float32x4.add(SIMD.Float32x4.mul(SIMD.Float32x4.swizzle(c,2,2,2,2),l),SIMD.Float32x4.mul(SIMD.Float32x4.swizzle(c,3,3,3,3),u))));SIMD.Float32x4.store(t,8,D);var S=SIMD.Float32x4.load(n,12),I=SIMD.Float32x4.add(SIMD.Float32x4.mul(SIMD.Float32x4.swizzle(S,0,0,0,0),r),SIMD.Float32x4.add(SIMD.Float32x4.mul(SIMD.Float32x4.swizzle(S,1,1,1,1),o),SIMD.Float32x4.add(SIMD.Float32x4.mul(SIMD.Float32x4.swizzle(S,2,2,2,2),l),SIMD.Float32x4.mul(SIMD.Float32x4.swizzle(S,3,3,3,3),u))));return SIMD.Float32x4.store(t,12,I),t},o.scalar.multiply=function(t,a,n){var r=a[0],o=a[1],l=a[2],u=a[3],e=a[4],M=a[5],i=a[6],s=a[7],c=a[8],D=a[9],S=a[10],I=a[11],x=a[12],f=a[13],F=a[14],m=a[15],h=n[0],d=n[1],v=n[2],z=n[3];return t[0]=h*r+d*e+v*c+z*x,t[1]=h*o+d*M+v*D+z*f,t[2]=h*l+d*i+v*S+z*F,t[3]=h*u+d*s+v*I+z*m,h=n[4],d=n[5],v=n[6],z=n[7],t[4]=h*r+d*e+v*c+z*x,t[5]=h*o+d*M+v*D+z*f,t[6]=h*l+d*i+v*S+z*F,t[7]=h*u+d*s+v*I+z*m,h=n[8],d=n[9],v=n[10],z=n[11],t[8]=h*r+d*e+v*c+z*x,t[9]=h*o+d*M+v*D+z*f,t[10]=h*l+d*i+v*S+z*F,t[11]=h*u+d*s+v*I+z*m,h=n[12],d=n[13],v=n[14],z=n[15],t[12]=h*r+d*e+v*c+z*x,t[13]=h*o+d*M+v*D+z*f,t[14]=h*l+d*i+v*S+z*F,t[15]=h*u+d*s+v*I+z*m,t},o.multiply=r.USE_SIMD?o.SIMD.multiply:o.scalar.multiply,o.mul=o.multiply,o.scalar.translate=function(t,a,n){var r,o,l,u,e,M,i,s,c,D,S,I,x=n[0],f=n[1],F=n[2];return a===t?(t[12]=a[0]*x+a[4]*f+a[8]*F+a[12],t[13]=a[1]*x+a[5]*f+a[9]*F+a[13],t[14]=a[2]*x+a[6]*f+a[10]*F+a[14],t[15]=a[3]*x+a[7]*f+a[11]*F+a[15]):(r=a[0],o=a[1],l=a[2],u=a[3],e=a[4],M=a[5],i=a[6],s=a[7],c=a[8],D=a[9],S=a[10],I=a[11],t[0]=r,t[1]=o,t[2]=l,t[3]=u,t[4]=e,t[5]=M,t[6]=i,t[7]=s,t[8]=c,t[9]=D,t[10]=S,t[11]=I,t[12]=r*x+e*f+c*F+a[12],t[13]=o*x+M*f+D*F+a[13],t[14]=l*x+i*f+S*F+a[14],t[15]=u*x+s*f+I*F+a[15]),t},o.SIMD.translate=function(t,a,n){var r=SIMD.Float32x4.load(a,0),o=SIMD.Float32x4.load(a,4),l=SIMD.Float32x4.load(a,8),u=SIMD.Float32x4.load(a,12),e=SIMD.Float32x4(n[0],n[1],n[2],0);a!==t&&(t[0]=a[0],t[1]=a[1],t[2]=a[2],t[3]=a[3],t[4]=a[4],t[5]=a[5],t[6]=a[6],t[7]=a[7],t[8]=a[8],t[9]=a[9],t[10]=a[10],t[11]=a[11]),r=SIMD.Float32x4.mul(r,SIMD.Float32x4.swizzle(e,0,0,0,0)),o=SIMD.Float32x4.mul(o,SIMD.Float32x4.swizzle(e,1,1,1,1)),l=SIMD.Float32x4.mul(l,SIMD.Float32x4.swizzle(e,2,2,2,2));var M=SIMD.Float32x4.add(r,SIMD.Float32x4.add(o,SIMD.Float32x4.add(l,u)));return SIMD.Float32x4.store(t,12,M),t},o.translate=r.USE_SIMD?o.SIMD.translate:o.scalar.translate,o.scalar.scale=function(t,a,n){var r=n[0],o=n[1],l=n[2];return t[0]=a[0]*r,t[1]=a[1]*r,t[2]=a[2]*r,t[3]=a[3]*r,t[4]=a[4]*o,t[5]=a[5]*o,t[6]=a[6]*o,t[7]=a[7]*o,t[8]=a[8]*l,t[9]=a[9]*l,t[10]=a[10]*l,t[11]=a[11]*l,t[12]=a[12],t[13]=a[13],t[14]=a[14],t[15]=a[15],t},o.SIMD.scale=function(t,a,n){var r,o,l,u=SIMD.Float32x4(n[0],n[1],n[2],0);return r=SIMD.Float32x4.load(a,0),SIMD.Float32x4.store(t,0,SIMD.Float32x4.mul(r,SIMD.Float32x4.swizzle(u,0,0,0,0))),o=SIMD.Float32x4.load(a,4),SIMD.Float32x4.store(t,4,SIMD.Float32x4.mul(o,SIMD.Float32x4.swizzle(u,1,1,1,1))),l=SIMD.Float32x4.load(a,8),SIMD.Float32x4.store(t,8,SIMD.Float32x4.mul(l,SIMD.Float32x4.swizzle(u,2,2,2,2))),t[12]=a[12],t[13]=a[13],t[14]=a[14],t[15]=a[15],t},o.scale=r.USE_SIMD?o.SIMD.scale:o.scalar.scale,o.rotate=function(t,a,n,o){var l,u,e,M,i,s,c,D,S,I,x,f,F,m,h,d,v,z,p,w,A,R,b,Y,q=o[0],y=o[1],E=o[2],g=Math.sqrt(q*q+y*y+E*E);return Math.abs(g)M?(l.cross(t,a,o),l.length(t)<1e-6&&l.cross(t,n,o),l.normalize(t,t),e.setAxisAngle(r,t,Math.PI),r):M>.999999?(r[0]=0,r[1]=0,r[2]=0,r[3]=1,r):(l.cross(t,o,u),r[0]=t[0],r[1]=t[1],r[2]=t[2],r[3]=1+M,e.normalize(r,r))}}(),e.setAxes=function(){var t=o.create();return function(a,n,r,o){return t[0]=r[0],t[3]=r[1],t[6]=r[2],t[1]=o[0],t[4]=o[1],t[7]=o[2],t[2]=-n[0],t[5]=-n[1],t[8]=-n[2],e.normalize(a,e.fromMat3(a,t))}}(),e.clone=u.clone,e.fromValues=u.fromValues,e.copy=u.copy,e.set=u.set,e.identity=function(t){return t[0]=0,t[1]=0,t[2]=0,t[3]=1,t},e.setAxisAngle=function(t,a,n){n=.5*n;var r=Math.sin(n);return t[0]=r*a[0],t[1]=r*a[1],t[2]=r*a[2],t[3]=Math.cos(n),t},e.add=u.add,e.multiply=function(t,a,n){var r=a[0],o=a[1],l=a[2],u=a[3],e=n[0],M=n[1],i=n[2],s=n[3];return t[0]=r*s+u*e+o*i-l*M,t[1]=o*s+u*M+l*e-r*i,t[2]=l*s+u*i+r*M-o*e,t[3]=u*s-r*e-o*M-l*i,t},e.mul=e.multiply,e.scale=u.scale,e.rotateX=function(t,a,n){n*=.5;var r=a[0],o=a[1],l=a[2],u=a[3],e=Math.sin(n),M=Math.cos(n);return t[0]=r*M+u*e,t[1]=o*M+l*e,t[2]=l*M-o*e,t[3]=u*M-r*e,t},e.rotateY=function(t,a,n){n*=.5;var r=a[0],o=a[1],l=a[2],u=a[3],e=Math.sin(n),M=Math.cos(n);return t[0]=r*M-l*e,t[1]=o*M+u*e,t[2]=l*M+r*e,t[3]=u*M-o*e,t},e.rotateZ=function(t,a,n){n*=.5;var r=a[0],o=a[1],l=a[2],u=a[3],e=Math.sin(n),M=Math.cos(n);return t[0]=r*M+o*e,t[1]=o*M-r*e,t[2]=l*M+u*e,t[3]=u*M-l*e,t},e.calculateW=function(t,a){var n=a[0],r=a[1],o=a[2];return t[0]=n,t[1]=r,t[2]=o,t[3]=Math.sqrt(Math.abs(1-n*n-r*r-o*o)),t},e.dot=u.dot,e.lerp=u.lerp,e.slerp=function(t,a,n,r){var o,l,u,e,M,i=a[0],s=a[1],c=a[2],D=a[3],S=n[0],I=n[1],x=n[2],f=n[3];return l=i*S+s*I+c*x+D*f,0>l&&(l=-l,S=-S,I=-I,x=-x,f=-f),1-l>1e-6?(o=Math.acos(l),u=Math.sin(o),e=Math.sin((1-r)*o)/u,M=Math.sin(r*o)/u):(e=1-r,M=r),t[0]=e*i+M*S,t[1]=e*s+M*I,t[2]=e*c+M*x,t[3]=e*D+M*f,t},e.sqlerp=function(){var t=e.create(),a=e.create();return function(n,r,o,l,u,M){return e.slerp(t,r,u,M),e.slerp(a,o,l,M),e.slerp(n,t,a,2*M*(1-M)),n}}(),e.invert=function(t,a){var n=a[0],r=a[1],o=a[2],l=a[3],u=n*n+r*r+o*o+l*l,e=u?1/u:0; 29 | return t[0]=-n*e,t[1]=-r*e,t[2]=-o*e,t[3]=l*e,t},e.conjugate=function(t,a){return t[0]=-a[0],t[1]=-a[1],t[2]=-a[2],t[3]=a[3],t},e.length=u.length,e.len=e.length,e.squaredLength=u.squaredLength,e.sqrLen=e.squaredLength,e.normalize=u.normalize,e.fromMat3=function(t,a){var n,r=a[0]+a[4]+a[8];if(r>0)n=Math.sqrt(r+1),t[3]=.5*n,n=.5/n,t[0]=(a[5]-a[7])*n,t[1]=(a[6]-a[2])*n,t[2]=(a[1]-a[3])*n;else{var o=0;a[4]>a[0]&&(o=1),a[8]>a[3*o+o]&&(o=2);var l=(o+1)%3,u=(o+2)%3;n=Math.sqrt(a[3*o+o]-a[3*l+l]-a[3*u+u]+1),t[o]=.5*n,n=.5/n,t[3]=(a[3*l+u]-a[3*u+l])*n,t[l]=(a[3*l+o]+a[3*o+l])*n,t[u]=(a[3*u+o]+a[3*o+u])*n}return t},e.str=function(t){return"quat("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+")"},t.exports=e},function(t,a,n){var r=n(1),o={};o.create=function(){var t=new r.ARRAY_TYPE(3);return t[0]=0,t[1]=0,t[2]=0,t},o.clone=function(t){var a=new r.ARRAY_TYPE(3);return a[0]=t[0],a[1]=t[1],a[2]=t[2],a},o.fromValues=function(t,a,n){var o=new r.ARRAY_TYPE(3);return o[0]=t,o[1]=a,o[2]=n,o},o.copy=function(t,a){return t[0]=a[0],t[1]=a[1],t[2]=a[2],t},o.set=function(t,a,n,r){return t[0]=a,t[1]=n,t[2]=r,t},o.add=function(t,a,n){return t[0]=a[0]+n[0],t[1]=a[1]+n[1],t[2]=a[2]+n[2],t},o.subtract=function(t,a,n){return t[0]=a[0]-n[0],t[1]=a[1]-n[1],t[2]=a[2]-n[2],t},o.sub=o.subtract,o.multiply=function(t,a,n){return t[0]=a[0]*n[0],t[1]=a[1]*n[1],t[2]=a[2]*n[2],t},o.mul=o.multiply,o.divide=function(t,a,n){return t[0]=a[0]/n[0],t[1]=a[1]/n[1],t[2]=a[2]/n[2],t},o.div=o.divide,o.min=function(t,a,n){return t[0]=Math.min(a[0],n[0]),t[1]=Math.min(a[1],n[1]),t[2]=Math.min(a[2],n[2]),t},o.max=function(t,a,n){return t[0]=Math.max(a[0],n[0]),t[1]=Math.max(a[1],n[1]),t[2]=Math.max(a[2],n[2]),t},o.scale=function(t,a,n){return t[0]=a[0]*n,t[1]=a[1]*n,t[2]=a[2]*n,t},o.scaleAndAdd=function(t,a,n,r){return t[0]=a[0]+n[0]*r,t[1]=a[1]+n[1]*r,t[2]=a[2]+n[2]*r,t},o.distance=function(t,a){var n=a[0]-t[0],r=a[1]-t[1],o=a[2]-t[2];return Math.sqrt(n*n+r*r+o*o)},o.dist=o.distance,o.squaredDistance=function(t,a){var n=a[0]-t[0],r=a[1]-t[1],o=a[2]-t[2];return n*n+r*r+o*o},o.sqrDist=o.squaredDistance,o.length=function(t){var a=t[0],n=t[1],r=t[2];return Math.sqrt(a*a+n*n+r*r)},o.len=o.length,o.squaredLength=function(t){var a=t[0],n=t[1],r=t[2];return a*a+n*n+r*r},o.sqrLen=o.squaredLength,o.negate=function(t,a){return t[0]=-a[0],t[1]=-a[1],t[2]=-a[2],t},o.inverse=function(t,a){return t[0]=1/a[0],t[1]=1/a[1],t[2]=1/a[2],t},o.normalize=function(t,a){var n=a[0],r=a[1],o=a[2],l=n*n+r*r+o*o;return l>0&&(l=1/Math.sqrt(l),t[0]=a[0]*l,t[1]=a[1]*l,t[2]=a[2]*l),t},o.dot=function(t,a){return t[0]*a[0]+t[1]*a[1]+t[2]*a[2]},o.cross=function(t,a,n){var r=a[0],o=a[1],l=a[2],u=n[0],e=n[1],M=n[2];return t[0]=o*M-l*e,t[1]=l*u-r*M,t[2]=r*e-o*u,t},o.lerp=function(t,a,n,r){var o=a[0],l=a[1],u=a[2];return t[0]=o+r*(n[0]-o),t[1]=l+r*(n[1]-l),t[2]=u+r*(n[2]-u),t},o.hermite=function(t,a,n,r,o,l){var u=l*l,e=u*(2*l-3)+1,M=u*(l-2)+l,i=u*(l-1),s=u*(3-2*l);return t[0]=a[0]*e+n[0]*M+r[0]*i+o[0]*s,t[1]=a[1]*e+n[1]*M+r[1]*i+o[1]*s,t[2]=a[2]*e+n[2]*M+r[2]*i+o[2]*s,t},o.bezier=function(t,a,n,r,o,l){var u=1-l,e=u*u,M=l*l,i=e*u,s=3*l*e,c=3*M*u,D=M*l;return t[0]=a[0]*i+n[0]*s+r[0]*c+o[0]*D,t[1]=a[1]*i+n[1]*s+r[1]*c+o[1]*D,t[2]=a[2]*i+n[2]*s+r[2]*c+o[2]*D,t},o.random=function(t,a){a=a||1;var n=2*r.RANDOM()*Math.PI,o=2*r.RANDOM()-1,l=Math.sqrt(1-o*o)*a;return t[0]=Math.cos(n)*l,t[1]=Math.sin(n)*l,t[2]=o*a,t},o.transformMat4=function(t,a,n){var r=a[0],o=a[1],l=a[2],u=n[3]*r+n[7]*o+n[11]*l+n[15];return u=u||1,t[0]=(n[0]*r+n[4]*o+n[8]*l+n[12])/u,t[1]=(n[1]*r+n[5]*o+n[9]*l+n[13])/u,t[2]=(n[2]*r+n[6]*o+n[10]*l+n[14])/u,t},o.transformMat3=function(t,a,n){var r=a[0],o=a[1],l=a[2];return t[0]=r*n[0]+o*n[3]+l*n[6],t[1]=r*n[1]+o*n[4]+l*n[7],t[2]=r*n[2]+o*n[5]+l*n[8],t},o.transformQuat=function(t,a,n){var r=a[0],o=a[1],l=a[2],u=n[0],e=n[1],M=n[2],i=n[3],s=i*r+e*l-M*o,c=i*o+M*r-u*l,D=i*l+u*o-e*r,S=-u*r-e*o-M*l;return t[0]=s*i+S*-u+c*-M-D*-e,t[1]=c*i+S*-e+D*-u-s*-M,t[2]=D*i+S*-M+s*-e-c*-u,t},o.rotateX=function(t,a,n,r){var o=[],l=[];return o[0]=a[0]-n[0],o[1]=a[1]-n[1],o[2]=a[2]-n[2],l[0]=o[0],l[1]=o[1]*Math.cos(r)-o[2]*Math.sin(r),l[2]=o[1]*Math.sin(r)+o[2]*Math.cos(r),t[0]=l[0]+n[0],t[1]=l[1]+n[1],t[2]=l[2]+n[2],t},o.rotateY=function(t,a,n,r){var o=[],l=[];return o[0]=a[0]-n[0],o[1]=a[1]-n[1],o[2]=a[2]-n[2],l[0]=o[2]*Math.sin(r)+o[0]*Math.cos(r),l[1]=o[1],l[2]=o[2]*Math.cos(r)-o[0]*Math.sin(r),t[0]=l[0]+n[0],t[1]=l[1]+n[1],t[2]=l[2]+n[2],t},o.rotateZ=function(t,a,n,r){var o=[],l=[];return o[0]=a[0]-n[0],o[1]=a[1]-n[1],o[2]=a[2]-n[2],l[0]=o[0]*Math.cos(r)-o[1]*Math.sin(r),l[1]=o[0]*Math.sin(r)+o[1]*Math.cos(r),l[2]=o[2],t[0]=l[0]+n[0],t[1]=l[1]+n[1],t[2]=l[2]+n[2],t},o.forEach=function(){var t=o.create();return function(a,n,r,o,l,u){var e,M;for(n||(n=3),r||(r=0),M=o?Math.min(o*n+r,a.length):a.length,e=r;M>e;e+=n)t[0]=a[e],t[1]=a[e+1],t[2]=a[e+2],l(t,t,u),a[e]=t[0],a[e+1]=t[1],a[e+2]=t[2];return a}}(),o.angle=function(t,a){var n=o.fromValues(t[0],t[1],t[2]),r=o.fromValues(a[0],a[1],a[2]);o.normalize(n,n),o.normalize(r,r);var l=o.dot(n,r);return l>1?0:Math.acos(l)},o.str=function(t){return"vec3("+t[0]+", "+t[1]+", "+t[2]+")"},t.exports=o},function(t,a,n){var r=n(1),o={};o.create=function(){var t=new r.ARRAY_TYPE(4);return t[0]=0,t[1]=0,t[2]=0,t[3]=0,t},o.clone=function(t){var a=new r.ARRAY_TYPE(4);return a[0]=t[0],a[1]=t[1],a[2]=t[2],a[3]=t[3],a},o.fromValues=function(t,a,n,o){var l=new r.ARRAY_TYPE(4);return l[0]=t,l[1]=a,l[2]=n,l[3]=o,l},o.copy=function(t,a){return t[0]=a[0],t[1]=a[1],t[2]=a[2],t[3]=a[3],t},o.set=function(t,a,n,r,o){return t[0]=a,t[1]=n,t[2]=r,t[3]=o,t},o.add=function(t,a,n){return t[0]=a[0]+n[0],t[1]=a[1]+n[1],t[2]=a[2]+n[2],t[3]=a[3]+n[3],t},o.subtract=function(t,a,n){return t[0]=a[0]-n[0],t[1]=a[1]-n[1],t[2]=a[2]-n[2],t[3]=a[3]-n[3],t},o.sub=o.subtract,o.multiply=function(t,a,n){return t[0]=a[0]*n[0],t[1]=a[1]*n[1],t[2]=a[2]*n[2],t[3]=a[3]*n[3],t},o.mul=o.multiply,o.divide=function(t,a,n){return t[0]=a[0]/n[0],t[1]=a[1]/n[1],t[2]=a[2]/n[2],t[3]=a[3]/n[3],t},o.div=o.divide,o.min=function(t,a,n){return t[0]=Math.min(a[0],n[0]),t[1]=Math.min(a[1],n[1]),t[2]=Math.min(a[2],n[2]),t[3]=Math.min(a[3],n[3]),t},o.max=function(t,a,n){return t[0]=Math.max(a[0],n[0]),t[1]=Math.max(a[1],n[1]),t[2]=Math.max(a[2],n[2]),t[3]=Math.max(a[3],n[3]),t},o.scale=function(t,a,n){return t[0]=a[0]*n,t[1]=a[1]*n,t[2]=a[2]*n,t[3]=a[3]*n,t},o.scaleAndAdd=function(t,a,n,r){return t[0]=a[0]+n[0]*r,t[1]=a[1]+n[1]*r,t[2]=a[2]+n[2]*r,t[3]=a[3]+n[3]*r,t},o.distance=function(t,a){var n=a[0]-t[0],r=a[1]-t[1],o=a[2]-t[2],l=a[3]-t[3];return Math.sqrt(n*n+r*r+o*o+l*l)},o.dist=o.distance,o.squaredDistance=function(t,a){var n=a[0]-t[0],r=a[1]-t[1],o=a[2]-t[2],l=a[3]-t[3];return n*n+r*r+o*o+l*l},o.sqrDist=o.squaredDistance,o.length=function(t){var a=t[0],n=t[1],r=t[2],o=t[3];return Math.sqrt(a*a+n*n+r*r+o*o)},o.len=o.length,o.squaredLength=function(t){var a=t[0],n=t[1],r=t[2],o=t[3];return a*a+n*n+r*r+o*o},o.sqrLen=o.squaredLength,o.negate=function(t,a){return t[0]=-a[0],t[1]=-a[1],t[2]=-a[2],t[3]=-a[3],t},o.inverse=function(t,a){return t[0]=1/a[0],t[1]=1/a[1],t[2]=1/a[2],t[3]=1/a[3],t},o.normalize=function(t,a){var n=a[0],r=a[1],o=a[2],l=a[3],u=n*n+r*r+o*o+l*l;return u>0&&(u=1/Math.sqrt(u),t[0]=n*u,t[1]=r*u,t[2]=o*u,t[3]=l*u),t},o.dot=function(t,a){return t[0]*a[0]+t[1]*a[1]+t[2]*a[2]+t[3]*a[3]},o.lerp=function(t,a,n,r){var o=a[0],l=a[1],u=a[2],e=a[3];return t[0]=o+r*(n[0]-o),t[1]=l+r*(n[1]-l),t[2]=u+r*(n[2]-u),t[3]=e+r*(n[3]-e),t},o.random=function(t,a){return a=a||1,t[0]=r.RANDOM(),t[1]=r.RANDOM(),t[2]=r.RANDOM(),t[3]=r.RANDOM(),o.normalize(t,t),o.scale(t,t,a),t},o.transformMat4=function(t,a,n){var r=a[0],o=a[1],l=a[2],u=a[3];return t[0]=n[0]*r+n[4]*o+n[8]*l+n[12]*u,t[1]=n[1]*r+n[5]*o+n[9]*l+n[13]*u,t[2]=n[2]*r+n[6]*o+n[10]*l+n[14]*u,t[3]=n[3]*r+n[7]*o+n[11]*l+n[15]*u,t},o.transformQuat=function(t,a,n){var r=a[0],o=a[1],l=a[2],u=n[0],e=n[1],M=n[2],i=n[3],s=i*r+e*l-M*o,c=i*o+M*r-u*l,D=i*l+u*o-e*r,S=-u*r-e*o-M*l;return t[0]=s*i+S*-u+c*-M-D*-e,t[1]=c*i+S*-e+D*-u-s*-M,t[2]=D*i+S*-M+s*-e-c*-u,t[3]=a[3],t},o.forEach=function(){var t=o.create();return function(a,n,r,o,l,u){var e,M;for(n||(n=4),r||(r=0),M=o?Math.min(o*n+r,a.length):a.length,e=r;M>e;e+=n)t[0]=a[e],t[1]=a[e+1],t[2]=a[e+2],t[3]=a[e+3],l(t,t,u),a[e]=t[0],a[e+1]=t[1],a[e+2]=t[2],a[e+3]=t[3];return a}}(),o.str=function(t){return"vec4("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+")"},t.exports=o},function(t,a,n){var r=n(1),o={};o.create=function(){var t=new r.ARRAY_TYPE(2);return t[0]=0,t[1]=0,t},o.clone=function(t){var a=new r.ARRAY_TYPE(2);return a[0]=t[0],a[1]=t[1],a},o.fromValues=function(t,a){var n=new r.ARRAY_TYPE(2);return n[0]=t,n[1]=a,n},o.copy=function(t,a){return t[0]=a[0],t[1]=a[1],t},o.set=function(t,a,n){return t[0]=a,t[1]=n,t},o.add=function(t,a,n){return t[0]=a[0]+n[0],t[1]=a[1]+n[1],t},o.subtract=function(t,a,n){return t[0]=a[0]-n[0],t[1]=a[1]-n[1],t},o.sub=o.subtract,o.multiply=function(t,a,n){return t[0]=a[0]*n[0],t[1]=a[1]*n[1],t},o.mul=o.multiply,o.divide=function(t,a,n){return t[0]=a[0]/n[0],t[1]=a[1]/n[1],t},o.div=o.divide,o.min=function(t,a,n){return t[0]=Math.min(a[0],n[0]),t[1]=Math.min(a[1],n[1]),t},o.max=function(t,a,n){return t[0]=Math.max(a[0],n[0]),t[1]=Math.max(a[1],n[1]),t},o.scale=function(t,a,n){return t[0]=a[0]*n,t[1]=a[1]*n,t},o.scaleAndAdd=function(t,a,n,r){return t[0]=a[0]+n[0]*r,t[1]=a[1]+n[1]*r,t},o.distance=function(t,a){var n=a[0]-t[0],r=a[1]-t[1];return Math.sqrt(n*n+r*r)},o.dist=o.distance,o.squaredDistance=function(t,a){var n=a[0]-t[0],r=a[1]-t[1];return n*n+r*r},o.sqrDist=o.squaredDistance,o.length=function(t){var a=t[0],n=t[1];return Math.sqrt(a*a+n*n)},o.len=o.length,o.squaredLength=function(t){var a=t[0],n=t[1];return a*a+n*n},o.sqrLen=o.squaredLength,o.negate=function(t,a){return t[0]=-a[0],t[1]=-a[1],t},o.inverse=function(t,a){return t[0]=1/a[0],t[1]=1/a[1],t},o.normalize=function(t,a){var n=a[0],r=a[1],o=n*n+r*r;return o>0&&(o=1/Math.sqrt(o),t[0]=a[0]*o,t[1]=a[1]*o),t},o.dot=function(t,a){return t[0]*a[0]+t[1]*a[1]},o.cross=function(t,a,n){var r=a[0]*n[1]-a[1]*n[0];return t[0]=t[1]=0,t[2]=r,t},o.lerp=function(t,a,n,r){var o=a[0],l=a[1];return t[0]=o+r*(n[0]-o),t[1]=l+r*(n[1]-l),t},o.random=function(t,a){a=a||1;var n=2*r.RANDOM()*Math.PI;return t[0]=Math.cos(n)*a,t[1]=Math.sin(n)*a,t},o.transformMat2=function(t,a,n){var r=a[0],o=a[1];return t[0]=n[0]*r+n[2]*o,t[1]=n[1]*r+n[3]*o,t},o.transformMat2d=function(t,a,n){var r=a[0],o=a[1];return t[0]=n[0]*r+n[2]*o+n[4],t[1]=n[1]*r+n[3]*o+n[5],t},o.transformMat3=function(t,a,n){var r=a[0],o=a[1];return t[0]=n[0]*r+n[3]*o+n[6],t[1]=n[1]*r+n[4]*o+n[7],t},o.transformMat4=function(t,a,n){var r=a[0],o=a[1];return t[0]=n[0]*r+n[4]*o+n[12],t[1]=n[1]*r+n[5]*o+n[13],t},o.forEach=function(){var t=o.create();return function(a,n,r,o,l,u){var e,M;for(n||(n=2),r||(r=0),M=o?Math.min(o*n+r,a.length):a.length,e=r;M>e;e+=n)t[0]=a[e],t[1]=a[e+1],l(t,t,u),a[e]=t[0],a[e+1]=t[1];return a}}(),o.str=function(t){return"vec2("+t[0]+", "+t[1]+")"},t.exports=o}])}); --------------------------------------------------------------------------------