From d84f59738c242124ff6f5459943b12248865de13 Mon Sep 17 00:00:00 2001
From: parksanghyuk <zola8138@ajou.ac.kr>
Date: Mon, 21 Jun 2021 05:59:27 +0900
Subject: [PATCH] Initial Commit

---
 LICENSE                |   21 +
 README.md              |   38 +-
 Script.js              |  278 ++
 gl-matrix.js           | 7611 ++++++++++++++++++++++++++++++++++++++++
 img/FragmentShader.png |  Bin 0 -> 12026 bytes
 img/InitializeGL.png   |  Bin 0 -> 26383 bytes
 img/VertexShader.png   |  Bin 0 -> 14815 bytes
 index.html             |   71 +
 8 files changed, 8018 insertions(+), 1 deletion(-)
 create mode 100644 LICENSE
 create mode 100644 Script.js
 create mode 100644 gl-matrix.js
 create mode 100644 img/FragmentShader.png
 create mode 100644 img/InitializeGL.png
 create mode 100644 img/VertexShader.png
 create mode 100644 index.html

diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..04bc01a
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2021 PARK Sanghyuk
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
\ No newline at end of file
diff --git a/README.md b/README.md
index 447b547..43ceafe 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,39 @@
 # WebGL Tutorial
 
-Project for 2021-Spring Computer Graphics final assignment
\ No newline at end of file
+Project for 2021-Spring Computer Graphics final assignment
+
+# Overview
+This document is to describe the basic method of changing the WebGL1-based document to WebGL2.
+WebGL2 is nearly 100% back compatible with WebGL1. In this document, there will be description of very basic way to change the WebGL1 script to WebGL2.
+Very brief comments are here, so read the source code to see the details.
+
+# Initializing
+<img src=img/InitializeGL.png width=50%>
+First, you use "webgl2" instead of "webgl", on initializing canvas.
+Note that there is no "experimental-webgl2". Many WebGL1 extensions are a standard part of WebGL2.
+
+# Switching the Shader Version to GLSL 3.00 ES
+Add "#version 300 es" to the very first line of the shader source code.
+CAUTION: NO BLANKS AND LINES ARE ALLOWED BEFORE THIS DECLARATION.
+
+# Some Changes in Shader Code
+Add "#version 300 es" to the very first line of the shader source code.
+CAUTION: NO BLANKS AND LINES ARE ALLOWED BEFORE THIS DECLARATION.
+
+No more gl_fragColor. In WebGL1, your fragment shader would set the specific variable gl_fragColor" to set the output value.
+You can now use any name for the output, except for those starting with "gl_".
+
+Change "varying" to "in" or "out", and "attribute" to "in".
+The output of the vertex shader would be the input of the fragment shader. We call them varying" values.
+Simply change them to "out" in the vertex shader, and "in" in the fragment shader.
+
+# Shader Code Example
+<img src=img/FragmentShader.png width=50%>
+Fragment shader
+<img src=img/VertexShader.png width=50%>
+Vertex shader
+
+# References
+ * This page is modified from Lab 06 of the Computer Graphics course.
+ * WebGL1 to WebGL2: https://webgl2fundamentals.org/webgl/lessons/webgl1-to-webgl2.html
+ * WebGL2 Fundamentals: https://webgl2fundamentals.org/webgl/lessons/ko/webgl-fundamentals.html
\ No newline at end of file
diff --git a/Script.js b/Script.js
new file mode 100644
index 0000000..a845f03
--- /dev/null
+++ b/Script.js
@@ -0,0 +1,278 @@
+// Change WebGL 1 based document to WebGL 2
+// Reference: https://webgl2fundamentals.org/webgl/lessons/webgl1-to-webgl2.html
+//            https://webgl2fundamentals.org/webgl/lessons/ko/webgl-fundamentals.html
+
+
+var gl;
+
+const {mat2, mat3, mat4, vec2, vec3, vec4} = glMatrix;  
+// Now we can use function without glMatrix.~~~
+
+function testGLError(functionLastCalled) {
+    /* gl.getError returns the last error that occurred using WebGL for debugging */ 
+    var lastError = gl.getError();
+
+    if (lastError != gl.NO_ERROR) {
+        alert(functionLastCalled + " failed (" + lastError + ")");
+        return false;
+    }
+    return true;
+}
+
+function initialiseGL(canvas) {
+    try {
+        // Try to grab the standard context. If it fails, fallback to experimental
+        // gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl"); // in WebGL1
+        gl = canvas.getContext("webgl2");
+        // No experimental-webgl2
+        gl.viewport(0, 0, canvas.width, canvas.height);
+    }
+    catch (e) {
+    }
+
+    if (!gl) {
+        alert("Unable to initialise WebGL. Your browser may not support it");
+        return false;
+    }
+    return true;
+}
+
+var shaderProgram;
+
+var vertexData = [
+		// Backface (RED/WHITE) -> z = 0.5
+        // x     y     z     r    g    b    a
+        -0.5, -0.5, -0.5,  1.0, 0.0, 0.0, 1.0,
+         0.5,  0.5, -0.5,  1.0, 0.0, 0.0, 1.0,
+         0.5, -0.5, -0.5,  1.0, 0.0, 0.0, 1.0,
+        -0.5, -0.5, -0.5,  1.0, 0.0, 0.0, 1.0,
+        -0.5,  0.5, -0.5,  1.0, 0.0, 0.0, 1.0,
+         0.5,  0.5, -0.5,  1.0, 1.0, 1.0, 1.0, 
+		// Front (BLUE/WHITE) -> z = 0.5
+        -0.5, -0.5,  0.5,  0.0, 0.0, 1.0, 1.0,
+         0.5,  0.5,  0.5,  0.0, 0.0, 1.0, 1.0,
+         0.5, -0.5,  0.5,  0.0, 0.0, 1.0, 1.0,
+        -0.5, -0.5,  0.5,  0.0, 0.0, 1.0, 1.0,
+        -0.5,  0.5,  0.5,  0.0, 0.0, 1.0, 1.0,
+         0.5,  0.5,  0.5,  1.0, 1.0, 1.0, 1.0, 
+		// LEFT (GREEN/WHITE) -> z = 0.5
+        -0.5, -0.5, -0.5,  0.0, 1.0, 0.0, 1.0,
+        -0.5,  0.5,  0.5,  0.0, 1.0, 0.0, 1.0,
+        -0.5,  0.5, -0.5,  0.0, 1.0, 0.0, 1.0,
+        -0.5, -0.5, -0.5,  0.0, 1.0, 0.0, 1.0,
+        -0.5, -0.5,  0.5,  0.0, 1.0, 0.0, 1.0,
+        -0.5,  0.5,  0.5,  0.0, 1.0, 1.0, 1.0, 
+		// RIGHT (YELLOW/WHITE) -> z = 0.5
+         0.5, -0.5, -0.5,  1.0, 1.0, 0.0, 1.0,
+         0.5,  0.5,  0.5,  1.0, 1.0, 0.0, 1.0,
+         0.5,  0.5, -0.5,  1.0, 1.0, 0.0, 1.0,
+         0.5, -0.5, -0.5,  1.0, 1.0, 0.0, 1.0,
+         0.5, -0.5,  0.5,  1.0, 1.0, 0.0, 1.0,
+         0.5,  0.5,  0.5,  1.0, 1.0, 1.0, 1.0, 
+		// BOTTON (MAGENTA/WHITE) -> z = 0.5
+        -0.5, -0.5, -0.5,  1.0, 0.0, 1.0, 1.0,
+         0.5, -0.5,  0.5,  1.0, 0.0, 1.0, 1.0,
+         0.5, -0.5, -0.5,  1.0, 0.0, 1.0, 1.0,
+        -0.5, -0.5, -0.5,  1.0, 0.0, 1.0, 1.0,
+        -0.5, -0.5,  0.5,  1.0, 0.0, 1.0, 1.0,
+         0.5, -0.5,  0.5,  1.0, 1.0, 1.0, 1.0, 
+		// TOP (CYAN/WHITE) -> z = 0.5
+        -0.5,  0.5, -0.5,  0.0, 1.0, 1.0, 1.0,
+         0.5,  0.5,  0.5,  0.0, 1.0, 1.0, 1.0,
+         0.5,  0.5, -0.5,  0.0, 1.0, 1.0, 1.0,
+        -0.5,  0.5, -0.5,  0.0, 1.0, 1.0, 1.0,
+        -0.5,  0.5,  0.5,  0.0, 1.0, 1.0, 1.0,
+         0.5,  0.5,  0.5,  1.0, 1.0, 1.0, 1.0 
+];
+
+function initialiseBuffer() {
+
+    gl.vertexBuffer = gl.createBuffer();
+    gl.bindBuffer(gl.ARRAY_BUFFER, gl.vertexBuffer);
+    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertexData), gl.STATIC_DRAW);
+
+    return testGLError("initialiseBuffers");
+}
+
+function initialiseShaders() {
+
+    // Define "#version 300 es" at the very first line of the shader code
+    // Neither blanks nor lines before this context are allowed.
+
+    // No more gl_fragColor
+    // Change "varying" to "in" or "out", and "attribute" to "in".
+    // You can make any name except for those starting with "gl_".
+
+    var fragmentShaderSource = `#version 300 es
+            // varying highp vec4 col; // in WebGL1
+            // You cannot use the name starting with "gl_"
+
+			in highp vec4 col;
+            out highp vec4 outFragColor;
+
+			void main(void) 
+			{ 
+				outFragColor = col;
+			}`;
+
+    gl.fragShader = gl.createShader(gl.FRAGMENT_SHADER);
+    gl.shaderSource(gl.fragShader, fragmentShaderSource);
+    gl.compileShader(gl.fragShader);
+    // Check if compilation succeeded
+    if (!gl.getShaderParameter(gl.fragShader, gl.COMPILE_STATUS)) {
+        alert("Failed to compile the fragment shader.\n" + gl.getShaderInfoLog(gl.fragShader));
+        return false;
+    }
+
+    // Vertex shader code
+    var vertexShaderSource = `#version 300 es
+            // change attribute to in
+			in highp vec4 myVertex; 
+			in highp vec4 myColor; 
+			uniform mediump mat4 transformationMatrix; 
+			out highp vec4 col;
+			void main(void)  
+			{ 
+				gl_Position = transformationMatrix * myVertex;
+                gl_PointSize = 5.0; 
+				col = myColor; 
+			}`;
+
+    gl.vertexShader = gl.createShader(gl.VERTEX_SHADER);
+    gl.shaderSource(gl.vertexShader, vertexShaderSource);
+    gl.compileShader(gl.vertexShader);
+    // Check if compilation succeeded
+    if (!gl.getShaderParameter(gl.vertexShader, gl.COMPILE_STATUS)) {
+        alert("Failed to compile the vertex shader.\n" + gl.getShaderInfoLog(gl.vertexShader));
+        return false;
+    }
+
+    // Create the shader program
+    gl.programObject = gl.createProgram();
+    // Attach the fragment and vertex shaders to it
+    gl.attachShader(gl.programObject, gl.fragShader);
+    gl.attachShader(gl.programObject, gl.vertexShader);
+    // Bind the custom vertex attribute "myVertex" to location 0
+    gl.bindAttribLocation(gl.programObject, 0, "myVertex");
+    gl.bindAttribLocation(gl.programObject, 1, "myColor");
+    // Link the program
+    gl.linkProgram(gl.programObject);
+    // Check if linking succeeded in a similar way we checked for compilation errors
+    if (!gl.getProgramParameter(gl.programObject, gl.LINK_STATUS)) {
+        alert("Failed to link the program.\n" + gl.getProgramInfoLog(gl.programObject));
+        return false;
+    }
+
+    gl.useProgram(gl.programObject);
+
+    return testGLError("initialiseShaders");
+}
+
+var xRot = 0.0;
+var yRot = 0.0;
+var zRot = 0.0;
+var speedRot = 0.01;
+
+flag_animation = 0; 
+function toggleAnimation()
+{
+	flag_animation ^= 1; 
+	console.log("flag_animation=", flag_animation);
+}
+
+function speed_plus(){
+    speedRot *= 1.1;
+}
+
+function speed_minus(){
+    speedRot /= 1.1;
+}
+
+var draw_mode = 4;
+
+function draw_mode_change(a){
+    draw_mode = a;
+}
+
+function renderScene() {
+
+    gl.clearColor(0.0, 0.0, 0.0, 1.0);
+	gl.clearDepth(1.0);										// Added for depth Test 
+	gl.clear(gl.COLOR_BUFFER_BIT);
+    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);		// Added for depth Test 
+	gl.enable(gl.DEPTH_TEST);								// Added for depth Test 
+
+    var matrixLocation = gl.getUniformLocation(gl.programObject, "transformationMatrix");
+    var transformationMatrix = mat4.create();
+    mat4.rotateX(transformationMatrix, transformationMatrix, xRot);
+    mat4.rotateY(transformationMatrix, transformationMatrix, yRot);
+    mat4.rotateZ(transformationMatrix, transformationMatrix, zRot);
+
+    xRot += (speedRot * flag_animation);
+    yRot += (speedRot * flag_animation);
+    zRot += (speedRot * flag_animation);
+
+    gl.uniformMatrix4fv(matrixLocation, gl.FALSE, transformationMatrix );
+
+    if (!testGLError("gl.uniformMatrix4fv")) {
+        return false;
+    }
+
+    gl.bindBuffer(gl.ARRAY_BUFFER, gl.vertexBuffer);
+    gl.enableVertexAttribArray(0);
+    gl.vertexAttribPointer(0, 3, gl.FLOAT, gl.FALSE, 28, 0);
+    gl.enableVertexAttribArray(1);
+    gl.vertexAttribPointer(1, 4, gl.FLOAT, gl.FALSE, 28, 12);
+
+    if (!testGLError("gl.vertexAttribPointer")) {
+        return false;
+    }
+
+	//gl.drawArrays(gl.TRIANGLES, 0, 36); 
+	//gl.drawArrays(gl.LINE_STRIP, 0, 36);
+
+    // Draw Mode
+    // 0: points
+    // 2: lines
+    // 4: triangles
+
+    gl.drawArrays(draw_mode, 0, 36); 
+
+    if (!testGLError("gl.drawArrays")) {
+        return false;
+    }
+
+    return true;
+}
+
+function main() {
+    var canvas = document.getElementById("helloapicanvas");
+
+    if (!initialiseGL(canvas)) {
+        return;
+    }
+
+    if (!initialiseBuffer()) {
+        return;
+    }
+
+    if (!initialiseShaders()) {
+        return;
+    }
+
+	// renderScene();
+    // Render loop
+    requestAnimFrame = (function () {
+        return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame ||
+			function (callback) {
+			    window.setTimeout(callback, 1000, 60);
+			};
+    })();
+
+    (function renderLoop() {
+        if (renderScene()) {
+            // Everything was successful, request that we redraw our scene again in the future
+            requestAnimFrame(renderLoop);
+        }
+    })();
+}
diff --git a/gl-matrix.js b/gl-matrix.js
new file mode 100644
index 0000000..4553f9e
--- /dev/null
+++ b/gl-matrix.js
@@ -0,0 +1,7611 @@
+
+/*!
+@fileoverview gl-matrix - High performance matrix and vector operations
+@author Brandon Jones
+@author Colin MacKenzie IV
+@version 3.3.0
+
+Copyright (c) 2015-2020, Brandon Jones, Colin MacKenzie IV.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+*/
+(function (global, factory) {
+  typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
+  typeof define === 'function' && define.amd ? define(['exports'], factory) :
+  (global = global || self, factory(global.glMatrix = {}));
+}(this, (function (exports) { 'use strict';
+
+  /**
+   * Common utilities
+   * @module glMatrix
+   */
+  // Configuration Constants
+  var EPSILON = 0.000001;
+  var ARRAY_TYPE = typeof Float32Array !== 'undefined' ? Float32Array : Array;
+  var RANDOM = Math.random;
+  /**
+   * Sets the type of array used when creating new vectors and matrices
+   *
+   * @param {Float32ArrayConstructor | ArrayConstructor} type Array type, such as Float32Array or Array
+   */
+
+  function setMatrixArrayType(type) {
+    ARRAY_TYPE = type;
+  }
+  var degree = Math.PI / 180;
+  /**
+   * Convert Degree To Radian
+   *
+   * @param {Number} a Angle in Degrees
+   */
+
+  function toRadian(a) {
+    return a * degree;
+  }
+  /**
+   * Tests whether or not the arguments have approximately the same value, within an absolute
+   * or relative tolerance of glMatrix.EPSILON (an absolute tolerance is used for values less
+   * than or equal to 1.0, and a relative tolerance is used for larger values)
+   *
+   * @param {Number} a The first number to test.
+   * @param {Number} b The second number to test.
+   * @returns {Boolean} True if the numbers are approximately equal, false otherwise.
+   */
+
+  function equals(a, b) {
+    return Math.abs(a - b) <= EPSILON * Math.max(1.0, Math.abs(a), Math.abs(b));
+  }
+  if (!Math.hypot) Math.hypot = function () {
+    var y = 0,
+        i = arguments.length;
+
+    while (i--) {
+      y += arguments[i] * arguments[i];
+    }
+
+    return Math.sqrt(y);
+  };
+
+  var common = /*#__PURE__*/Object.freeze({
+    __proto__: null,
+    EPSILON: EPSILON,
+    get ARRAY_TYPE () { return ARRAY_TYPE; },
+    RANDOM: RANDOM,
+    setMatrixArrayType: setMatrixArrayType,
+    toRadian: toRadian,
+    equals: equals
+  });
+
+  /**
+   * 2x2 Matrix
+   * @module mat2
+   */
+
+  /**
+   * Creates a new identity mat2
+   *
+   * @returns {mat2} a new 2x2 matrix
+   */
+
+  function create() {
+    var out = new ARRAY_TYPE(4);
+
+    if (ARRAY_TYPE != Float32Array) {
+      out[1] = 0;
+      out[2] = 0;
+    }
+
+    out[0] = 1;
+    out[3] = 1;
+    return out;
+  }
+  /**
+   * Creates a new mat2 initialized with values from an existing matrix
+   *
+   * @param {ReadonlyMat2} a matrix to clone
+   * @returns {mat2} a new 2x2 matrix
+   */
+
+  function clone(a) {
+    var out = new ARRAY_TYPE(4);
+    out[0] = a[0];
+    out[1] = a[1];
+    out[2] = a[2];
+    out[3] = a[3];
+    return out;
+  }
+  /**
+   * Copy the values from one mat2 to another
+   *
+   * @param {mat2} out the receiving matrix
+   * @param {ReadonlyMat2} a the source matrix
+   * @returns {mat2} out
+   */
+
+  function copy(out, a) {
+    out[0] = a[0];
+    out[1] = a[1];
+    out[2] = a[2];
+    out[3] = a[3];
+    return out;
+  }
+  /**
+   * Set a mat2 to the identity matrix
+   *
+   * @param {mat2} out the receiving matrix
+   * @returns {mat2} out
+   */
+
+  function identity(out) {
+    out[0] = 1;
+    out[1] = 0;
+    out[2] = 0;
+    out[3] = 1;
+    return out;
+  }
+  /**
+   * Create a new mat2 with the given values
+   *
+   * @param {Number} m00 Component in column 0, row 0 position (index 0)
+   * @param {Number} m01 Component in column 0, row 1 position (index 1)
+   * @param {Number} m10 Component in column 1, row 0 position (index 2)
+   * @param {Number} m11 Component in column 1, row 1 position (index 3)
+   * @returns {mat2} out A new 2x2 matrix
+   */
+
+  function fromValues(m00, m01, m10, m11) {
+    var out = new ARRAY_TYPE(4);
+    out[0] = m00;
+    out[1] = m01;
+    out[2] = m10;
+    out[3] = m11;
+    return out;
+  }
+  /**
+   * Set the components of a mat2 to the given values
+   *
+   * @param {mat2} out the receiving matrix
+   * @param {Number} m00 Component in column 0, row 0 position (index 0)
+   * @param {Number} m01 Component in column 0, row 1 position (index 1)
+   * @param {Number} m10 Component in column 1, row 0 position (index 2)
+   * @param {Number} m11 Component in column 1, row 1 position (index 3)
+   * @returns {mat2} out
+   */
+
+  function set(out, m00, m01, m10, m11) {
+    out[0] = m00;
+    out[1] = m01;
+    out[2] = m10;
+    out[3] = m11;
+    return out;
+  }
+  /**
+   * Transpose the values of a mat2
+   *
+   * @param {mat2} out the receiving matrix
+   * @param {ReadonlyMat2} a the source matrix
+   * @returns {mat2} out
+   */
+
+  function transpose(out, a) {
+    // If we are transposing ourselves we can skip a few steps but have to cache
+    // some values
+    if (out === a) {
+      var a1 = a[1];
+      out[1] = a[2];
+      out[2] = a1;
+    } else {
+      out[0] = a[0];
+      out[1] = a[2];
+      out[2] = a[1];
+      out[3] = a[3];
+    }
+
+    return out;
+  }
+  /**
+   * Inverts a mat2
+   *
+   * @param {mat2} out the receiving matrix
+   * @param {ReadonlyMat2} a the source matrix
+   * @returns {mat2} out
+   */
+
+  function invert(out, a) {
+    var a0 = a[0],
+        a1 = a[1],
+        a2 = a[2],
+        a3 = a[3]; // Calculate the determinant
+
+    var det = a0 * a3 - a2 * a1;
+
+    if (!det) {
+      return null;
+    }
+
+    det = 1.0 / det;
+    out[0] = a3 * det;
+    out[1] = -a1 * det;
+    out[2] = -a2 * det;
+    out[3] = a0 * det;
+    return out;
+  }
+  /**
+   * Calculates the adjugate of a mat2
+   *
+   * @param {mat2} out the receiving matrix
+   * @param {ReadonlyMat2} a the source matrix
+   * @returns {mat2} out
+   */
+
+  function adjoint(out, a) {
+    // Caching this value is nessecary if out == a
+    var a0 = a[0];
+    out[0] = a[3];
+    out[1] = -a[1];
+    out[2] = -a[2];
+    out[3] = a0;
+    return out;
+  }
+  /**
+   * Calculates the determinant of a mat2
+   *
+   * @param {ReadonlyMat2} a the source matrix
+   * @returns {Number} determinant of a
+   */
+
+  function determinant(a) {
+    return a[0] * a[3] - a[2] * a[1];
+  }
+  /**
+   * Multiplies two mat2's
+   *
+   * @param {mat2} out the receiving matrix
+   * @param {ReadonlyMat2} a the first operand
+   * @param {ReadonlyMat2} b the second operand
+   * @returns {mat2} out
+   */
+
+  function multiply(out, a, b) {
+    var a0 = a[0],
+        a1 = a[1],
+        a2 = a[2],
+        a3 = a[3];
+    var b0 = b[0],
+        b1 = b[1],
+        b2 = b[2],
+        b3 = b[3];
+    out[0] = a0 * b0 + a2 * b1;
+    out[1] = a1 * b0 + a3 * b1;
+    out[2] = a0 * b2 + a2 * b3;
+    out[3] = a1 * b2 + a3 * b3;
+    return out;
+  }
+  /**
+   * Rotates a mat2 by the given angle
+   *
+   * @param {mat2} out the receiving matrix
+   * @param {ReadonlyMat2} a the matrix to rotate
+   * @param {Number} rad the angle to rotate the matrix by
+   * @returns {mat2} out
+   */
+
+  function rotate(out, a, rad) {
+    var a0 = a[0],
+        a1 = a[1],
+        a2 = a[2],
+        a3 = a[3];
+    var s = Math.sin(rad);
+    var c = Math.cos(rad);
+    out[0] = a0 * c + a2 * s;
+    out[1] = a1 * c + a3 * s;
+    out[2] = a0 * -s + a2 * c;
+    out[3] = a1 * -s + a3 * c;
+    return out;
+  }
+  /**
+   * Scales the mat2 by the dimensions in the given vec2
+   *
+   * @param {mat2} out the receiving matrix
+   * @param {ReadonlyMat2} a the matrix to rotate
+   * @param {ReadonlyVec2} v the vec2 to scale the matrix by
+   * @returns {mat2} out
+   **/
+
+  function scale(out, a, v) {
+    var a0 = a[0],
+        a1 = a[1],
+        a2 = a[2],
+        a3 = a[3];
+    var v0 = v[0],
+        v1 = v[1];
+    out[0] = a0 * v0;
+    out[1] = a1 * v0;
+    out[2] = a2 * v1;
+    out[3] = a3 * v1;
+    return out;
+  }
+  /**
+   * Creates a matrix from a given angle
+   * This is equivalent to (but much faster than):
+   *
+   *     mat2.identity(dest);
+   *     mat2.rotate(dest, dest, rad);
+   *
+   * @param {mat2} out mat2 receiving operation result
+   * @param {Number} rad the angle to rotate the matrix by
+   * @returns {mat2} out
+   */
+
+  function fromRotation(out, rad) {
+    var s = Math.sin(rad);
+    var c = Math.cos(rad);
+    out[0] = c;
+    out[1] = s;
+    out[2] = -s;
+    out[3] = c;
+    return out;
+  }
+  /**
+   * Creates a matrix from a vector scaling
+   * This is equivalent to (but much faster than):
+   *
+   *     mat2.identity(dest);
+   *     mat2.scale(dest, dest, vec);
+   *
+   * @param {mat2} out mat2 receiving operation result
+   * @param {ReadonlyVec2} v Scaling vector
+   * @returns {mat2} out
+   */
+
+  function fromScaling(out, v) {
+    out[0] = v[0];
+    out[1] = 0;
+    out[2] = 0;
+    out[3] = v[1];
+    return out;
+  }
+  /**
+   * Returns a string representation of a mat2
+   *
+   * @param {ReadonlyMat2} a matrix to represent as a string
+   * @returns {String} string representation of the matrix
+   */
+
+  function str(a) {
+    return "mat2(" + a[0] + ", " + a[1] + ", " + a[2] + ", " + a[3] + ")";
+  }
+  /**
+   * Returns Frobenius norm of a mat2
+   *
+   * @param {ReadonlyMat2} a the matrix to calculate Frobenius norm of
+   * @returns {Number} Frobenius norm
+   */
+
+  function frob(a) {
+    return Math.hypot(a[0], a[1], a[2], a[3]);
+  }
+  /**
+   * Returns L, D and U matrices (Lower triangular, Diagonal and Upper triangular) by factorizing the input matrix
+   * @param {ReadonlyMat2} L the lower triangular matrix
+   * @param {ReadonlyMat2} D the diagonal matrix
+   * @param {ReadonlyMat2} U the upper triangular matrix
+   * @param {ReadonlyMat2} a the input matrix to factorize
+   */
+
+  function LDU(L, D, U, a) {
+    L[2] = a[2] / a[0];
+    U[0] = a[0];
+    U[1] = a[1];
+    U[3] = a[3] - L[2] * U[1];
+    return [L, D, U];
+  }
+  /**
+   * Adds two mat2's
+   *
+   * @param {mat2} out the receiving matrix
+   * @param {ReadonlyMat2} a the first operand
+   * @param {ReadonlyMat2} b the second operand
+   * @returns {mat2} out
+   */
+
+  function add(out, a, b) {
+    out[0] = a[0] + b[0];
+    out[1] = a[1] + b[1];
+    out[2] = a[2] + b[2];
+    out[3] = a[3] + b[3];
+    return out;
+  }
+  /**
+   * Subtracts matrix b from matrix a
+   *
+   * @param {mat2} out the receiving matrix
+   * @param {ReadonlyMat2} a the first operand
+   * @param {ReadonlyMat2} b the second operand
+   * @returns {mat2} out
+   */
+
+  function subtract(out, a, b) {
+    out[0] = a[0] - b[0];
+    out[1] = a[1] - b[1];
+    out[2] = a[2] - b[2];
+    out[3] = a[3] - b[3];
+    return out;
+  }
+  /**
+   * Returns whether or not the matrices have exactly the same elements in the same position (when compared with ===)
+   *
+   * @param {ReadonlyMat2} a The first matrix.
+   * @param {ReadonlyMat2} b The second matrix.
+   * @returns {Boolean} True if the matrices are equal, false otherwise.
+   */
+
+  function exactEquals(a, b) {
+    return a[0] === b[0] && a[1] === b[1] && a[2] === b[2] && a[3] === b[3];
+  }
+  /**
+   * Returns whether or not the matrices have approximately the same elements in the same position.
+   *
+   * @param {ReadonlyMat2} a The first matrix.
+   * @param {ReadonlyMat2} b The second matrix.
+   * @returns {Boolean} True if the matrices are equal, false otherwise.
+   */
+
+  function equals$1(a, b) {
+    var a0 = a[0],
+        a1 = a[1],
+        a2 = a[2],
+        a3 = a[3];
+    var b0 = b[0],
+        b1 = b[1],
+        b2 = b[2],
+        b3 = b[3];
+    return Math.abs(a0 - b0) <= EPSILON * Math.max(1.0, Math.abs(a0), Math.abs(b0)) && Math.abs(a1 - b1) <= EPSILON * Math.max(1.0, Math.abs(a1), Math.abs(b1)) && Math.abs(a2 - b2) <= EPSILON * Math.max(1.0, Math.abs(a2), Math.abs(b2)) && Math.abs(a3 - b3) <= EPSILON * Math.max(1.0, Math.abs(a3), Math.abs(b3));
+  }
+  /**
+   * Multiply each element of the matrix by a scalar.
+   *
+   * @param {mat2} out the receiving matrix
+   * @param {ReadonlyMat2} a the matrix to scale
+   * @param {Number} b amount to scale the matrix's elements by
+   * @returns {mat2} out
+   */
+
+  function multiplyScalar(out, a, b) {
+    out[0] = a[0] * b;
+    out[1] = a[1] * b;
+    out[2] = a[2] * b;
+    out[3] = a[3] * b;
+    return out;
+  }
+  /**
+   * Adds two mat2's after multiplying each element of the second operand by a scalar value.
+   *
+   * @param {mat2} out the receiving vector
+   * @param {ReadonlyMat2} a the first operand
+   * @param {ReadonlyMat2} b the second operand
+   * @param {Number} scale the amount to scale b's elements by before adding
+   * @returns {mat2} out
+   */
+
+  function multiplyScalarAndAdd(out, a, b, scale) {
+    out[0] = a[0] + b[0] * scale;
+    out[1] = a[1] + b[1] * scale;
+    out[2] = a[2] + b[2] * scale;
+    out[3] = a[3] + b[3] * scale;
+    return out;
+  }
+  /**
+   * Alias for {@link mat2.multiply}
+   * @function
+   */
+
+  var mul = multiply;
+  /**
+   * Alias for {@link mat2.subtract}
+   * @function
+   */
+
+  var sub = subtract;
+
+  var mat2 = /*#__PURE__*/Object.freeze({
+    __proto__: null,
+    create: create,
+    clone: clone,
+    copy: copy,
+    identity: identity,
+    fromValues: fromValues,
+    set: set,
+    transpose: transpose,
+    invert: invert,
+    adjoint: adjoint,
+    determinant: determinant,
+    multiply: multiply,
+    rotate: rotate,
+    scale: scale,
+    fromRotation: fromRotation,
+    fromScaling: fromScaling,
+    str: str,
+    frob: frob,
+    LDU: LDU,
+    add: add,
+    subtract: subtract,
+    exactEquals: exactEquals,
+    equals: equals$1,
+    multiplyScalar: multiplyScalar,
+    multiplyScalarAndAdd: multiplyScalarAndAdd,
+    mul: mul,
+    sub: sub
+  });
+
+  /**
+   * 2x3 Matrix
+   * @module mat2d
+   * @description
+   * A mat2d contains six elements defined as:
+   * <pre>
+   * [a, b,
+   *  c, d,
+   *  tx, ty]
+   * </pre>
+   * This is a short form for the 3x3 matrix:
+   * <pre>
+   * [a, b, 0,
+   *  c, d, 0,
+   *  tx, ty, 1]
+   * </pre>
+   * The last column is ignored so the array is shorter and operations are faster.
+   */
+
+  /**
+   * Creates a new identity mat2d
+   *
+   * @returns {mat2d} a new 2x3 matrix
+   */
+
+  function create$1() {
+    var out = new ARRAY_TYPE(6);
+
+    if (ARRAY_TYPE != Float32Array) {
+      out[1] = 0;
+      out[2] = 0;
+      out[4] = 0;
+      out[5] = 0;
+    }
+
+    out[0] = 1;
+    out[3] = 1;
+    return out;
+  }
+  /**
+   * Creates a new mat2d initialized with values from an existing matrix
+   *
+   * @param {ReadonlyMat2d} a matrix to clone
+   * @returns {mat2d} a new 2x3 matrix
+   */
+
+  function clone$1(a) {
+    var out = new ARRAY_TYPE(6);
+    out[0] = a[0];
+    out[1] = a[1];
+    out[2] = a[2];
+    out[3] = a[3];
+    out[4] = a[4];
+    out[5] = a[5];
+    return out;
+  }
+  /**
+   * Copy the values from one mat2d to another
+   *
+   * @param {mat2d} out the receiving matrix
+   * @param {ReadonlyMat2d} a the source matrix
+   * @returns {mat2d} out
+   */
+
+  function copy$1(out, a) {
+    out[0] = a[0];
+    out[1] = a[1];
+    out[2] = a[2];
+    out[3] = a[3];
+    out[4] = a[4];
+    out[5] = a[5];
+    return out;
+  }
+  /**
+   * Set a mat2d to the identity matrix
+   *
+   * @param {mat2d} out the receiving matrix
+   * @returns {mat2d} out
+   */
+
+  function identity$1(out) {
+    out[0] = 1;
+    out[1] = 0;
+    out[2] = 0;
+    out[3] = 1;
+    out[4] = 0;
+    out[5] = 0;
+    return out;
+  }
+  /**
+   * Create a new mat2d with the given values
+   *
+   * @param {Number} a Component A (index 0)
+   * @param {Number} b Component B (index 1)
+   * @param {Number} c Component C (index 2)
+   * @param {Number} d Component D (index 3)
+   * @param {Number} tx Component TX (index 4)
+   * @param {Number} ty Component TY (index 5)
+   * @returns {mat2d} A new mat2d
+   */
+
+  function fromValues$1(a, b, c, d, tx, ty) {
+    var out = new ARRAY_TYPE(6);
+    out[0] = a;
+    out[1] = b;
+    out[2] = c;
+    out[3] = d;
+    out[4] = tx;
+    out[5] = ty;
+    return out;
+  }
+  /**
+   * Set the components of a mat2d to the given values
+   *
+   * @param {mat2d} out the receiving matrix
+   * @param {Number} a Component A (index 0)
+   * @param {Number} b Component B (index 1)
+   * @param {Number} c Component C (index 2)
+   * @param {Number} d Component D (index 3)
+   * @param {Number} tx Component TX (index 4)
+   * @param {Number} ty Component TY (index 5)
+   * @returns {mat2d} out
+   */
+
+  function set$1(out, a, b, c, d, tx, ty) {
+    out[0] = a;
+    out[1] = b;
+    out[2] = c;
+    out[3] = d;
+    out[4] = tx;
+    out[5] = ty;
+    return out;
+  }
+  /**
+   * Inverts a mat2d
+   *
+   * @param {mat2d} out the receiving matrix
+   * @param {ReadonlyMat2d} a the source matrix
+   * @returns {mat2d} out
+   */
+
+  function invert$1(out, a) {
+    var aa = a[0],
+        ab = a[1],
+        ac = a[2],
+        ad = a[3];
+    var atx = a[4],
+        aty = a[5];
+    var det = aa * ad - ab * ac;
+
+    if (!det) {
+      return null;
+    }
+
+    det = 1.0 / det;
+    out[0] = ad * det;
+    out[1] = -ab * det;
+    out[2] = -ac * det;
+    out[3] = aa * det;
+    out[4] = (ac * aty - ad * atx) * det;
+    out[5] = (ab * atx - aa * aty) * det;
+    return out;
+  }
+  /**
+   * Calculates the determinant of a mat2d
+   *
+   * @param {ReadonlyMat2d} a the source matrix
+   * @returns {Number} determinant of a
+   */
+
+  function determinant$1(a) {
+    return a[0] * a[3] - a[1] * a[2];
+  }
+  /**
+   * Multiplies two mat2d's
+   *
+   * @param {mat2d} out the receiving matrix
+   * @param {ReadonlyMat2d} a the first operand
+   * @param {ReadonlyMat2d} b the second operand
+   * @returns {mat2d} out
+   */
+
+  function multiply$1(out, a, b) {
+    var a0 = a[0],
+        a1 = a[1],
+        a2 = a[2],
+        a3 = a[3],
+        a4 = a[4],
+        a5 = a[5];
+    var b0 = b[0],
+        b1 = b[1],
+        b2 = b[2],
+        b3 = b[3],
+        b4 = b[4],
+        b5 = b[5];
+    out[0] = a0 * b0 + a2 * b1;
+    out[1] = a1 * b0 + a3 * b1;
+    out[2] = a0 * b2 + a2 * b3;
+    out[3] = a1 * b2 + a3 * b3;
+    out[4] = a0 * b4 + a2 * b5 + a4;
+    out[5] = a1 * b4 + a3 * b5 + a5;
+    return out;
+  }
+  /**
+   * Rotates a mat2d by the given angle
+   *
+   * @param {mat2d} out the receiving matrix
+   * @param {ReadonlyMat2d} a the matrix to rotate
+   * @param {Number} rad the angle to rotate the matrix by
+   * @returns {mat2d} out
+   */
+
+  function rotate$1(out, a, rad) {
+    var a0 = a[0],
+        a1 = a[1],
+        a2 = a[2],
+        a3 = a[3],
+        a4 = a[4],
+        a5 = a[5];
+    var s = Math.sin(rad);
+    var c = Math.cos(rad);
+    out[0] = a0 * c + a2 * s;
+    out[1] = a1 * c + a3 * s;
+    out[2] = a0 * -s + a2 * c;
+    out[3] = a1 * -s + a3 * c;
+    out[4] = a4;
+    out[5] = a5;
+    return out;
+  }
+  /**
+   * Scales the mat2d by the dimensions in the given vec2
+   *
+   * @param {mat2d} out the receiving matrix
+   * @param {ReadonlyMat2d} a the matrix to translate
+   * @param {ReadonlyVec2} v the vec2 to scale the matrix by
+   * @returns {mat2d} out
+   **/
+
+  function scale$1(out, a, v) {
+    var a0 = a[0],
+        a1 = a[1],
+        a2 = a[2],
+        a3 = a[3],
+        a4 = a[4],
+        a5 = a[5];
+    var v0 = v[0],
+        v1 = v[1];
+    out[0] = a0 * v0;
+    out[1] = a1 * v0;
+    out[2] = a2 * v1;
+    out[3] = a3 * v1;
+    out[4] = a4;
+    out[5] = a5;
+    return out;
+  }
+  /**
+   * Translates the mat2d by the dimensions in the given vec2
+   *
+   * @param {mat2d} out the receiving matrix
+   * @param {ReadonlyMat2d} a the matrix to translate
+   * @param {ReadonlyVec2} v the vec2 to translate the matrix by
+   * @returns {mat2d} out
+   **/
+
+  function translate(out, a, v) {
+    var a0 = a[0],
+        a1 = a[1],
+        a2 = a[2],
+        a3 = a[3],
+        a4 = a[4],
+        a5 = a[5];
+    var v0 = v[0],
+        v1 = v[1];
+    out[0] = a0;
+    out[1] = a1;
+    out[2] = a2;
+    out[3] = a3;
+    out[4] = a0 * v0 + a2 * v1 + a4;
+    out[5] = a1 * v0 + a3 * v1 + a5;
+    return out;
+  }
+  /**
+   * Creates a matrix from a given angle
+   * This is equivalent to (but much faster than):
+   *
+   *     mat2d.identity(dest);
+   *     mat2d.rotate(dest, dest, rad);
+   *
+   * @param {mat2d} out mat2d receiving operation result
+   * @param {Number} rad the angle to rotate the matrix by
+   * @returns {mat2d} out
+   */
+
+  function fromRotation$1(out, rad) {
+    var s = Math.sin(rad),
+        c = Math.cos(rad);
+    out[0] = c;
+    out[1] = s;
+    out[2] = -s;
+    out[3] = c;
+    out[4] = 0;
+    out[5] = 0;
+    return out;
+  }
+  /**
+   * Creates a matrix from a vector scaling
+   * This is equivalent to (but much faster than):
+   *
+   *     mat2d.identity(dest);
+   *     mat2d.scale(dest, dest, vec);
+   *
+   * @param {mat2d} out mat2d receiving operation result
+   * @param {ReadonlyVec2} v Scaling vector
+   * @returns {mat2d} out
+   */
+
+  function fromScaling$1(out, v) {
+    out[0] = v[0];
+    out[1] = 0;
+    out[2] = 0;
+    out[3] = v[1];
+    out[4] = 0;
+    out[5] = 0;
+    return out;
+  }
+  /**
+   * Creates a matrix from a vector translation
+   * This is equivalent to (but much faster than):
+   *
+   *     mat2d.identity(dest);
+   *     mat2d.translate(dest, dest, vec);
+   *
+   * @param {mat2d} out mat2d receiving operation result
+   * @param {ReadonlyVec2} v Translation vector
+   * @returns {mat2d} out
+   */
+
+  function fromTranslation(out, v) {
+    out[0] = 1;
+    out[1] = 0;
+    out[2] = 0;
+    out[3] = 1;
+    out[4] = v[0];
+    out[5] = v[1];
+    return out;
+  }
+  /**
+   * Returns a string representation of a mat2d
+   *
+   * @param {ReadonlyMat2d} a matrix to represent as a string
+   * @returns {String} string representation of the matrix
+   */
+
+  function str$1(a) {
+    return "mat2d(" + a[0] + ", " + a[1] + ", " + a[2] + ", " + a[3] + ", " + a[4] + ", " + a[5] + ")";
+  }
+  /**
+   * Returns Frobenius norm of a mat2d
+   *
+   * @param {ReadonlyMat2d} a the matrix to calculate Frobenius norm of
+   * @returns {Number} Frobenius norm
+   */
+
+  function frob$1(a) {
+    return Math.hypot(a[0], a[1], a[2], a[3], a[4], a[5], 1);
+  }
+  /**
+   * Adds two mat2d's
+   *
+   * @param {mat2d} out the receiving matrix
+   * @param {ReadonlyMat2d} a the first operand
+   * @param {ReadonlyMat2d} b the second operand
+   * @returns {mat2d} out
+   */
+
+  function add$1(out, a, b) {
+    out[0] = a[0] + b[0];
+    out[1] = a[1] + b[1];
+    out[2] = a[2] + b[2];
+    out[3] = a[3] + b[3];
+    out[4] = a[4] + b[4];
+    out[5] = a[5] + b[5];
+    return out;
+  }
+  /**
+   * Subtracts matrix b from matrix a
+   *
+   * @param {mat2d} out the receiving matrix
+   * @param {ReadonlyMat2d} a the first operand
+   * @param {ReadonlyMat2d} b the second operand
+   * @returns {mat2d} out
+   */
+
+  function subtract$1(out, a, b) {
+    out[0] = a[0] - b[0];
+    out[1] = a[1] - b[1];
+    out[2] = a[2] - b[2];
+    out[3] = a[3] - b[3];
+    out[4] = a[4] - b[4];
+    out[5] = a[5] - b[5];
+    return out;
+  }
+  /**
+   * Multiply each element of the matrix by a scalar.
+   *
+   * @param {mat2d} out the receiving matrix
+   * @param {ReadonlyMat2d} a the matrix to scale
+   * @param {Number} b amount to scale the matrix's elements by
+   * @returns {mat2d} out
+   */
+
+  function multiplyScalar$1(out, a, b) {
+    out[0] = a[0] * b;
+    out[1] = a[1] * b;
+    out[2] = a[2] * b;
+    out[3] = a[3] * b;
+    out[4] = a[4] * b;
+    out[5] = a[5] * b;
+    return out;
+  }
+  /**
+   * Adds two mat2d's after multiplying each element of the second operand by a scalar value.
+   *
+   * @param {mat2d} out the receiving vector
+   * @param {ReadonlyMat2d} a the first operand
+   * @param {ReadonlyMat2d} b the second operand
+   * @param {Number} scale the amount to scale b's elements by before adding
+   * @returns {mat2d} out
+   */
+
+  function multiplyScalarAndAdd$1(out, a, b, scale) {
+    out[0] = a[0] + b[0] * scale;
+    out[1] = a[1] + b[1] * scale;
+    out[2] = a[2] + b[2] * scale;
+    out[3] = a[3] + b[3] * scale;
+    out[4] = a[4] + b[4] * scale;
+    out[5] = a[5] + b[5] * scale;
+    return out;
+  }
+  /**
+   * Returns whether or not the matrices have exactly the same elements in the same position (when compared with ===)
+   *
+   * @param {ReadonlyMat2d} a The first matrix.
+   * @param {ReadonlyMat2d} b The second matrix.
+   * @returns {Boolean} True if the matrices are equal, false otherwise.
+   */
+
+  function exactEquals$1(a, b) {
+    return a[0] === b[0] && a[1] === b[1] && a[2] === b[2] && a[3] === b[3] && a[4] === b[4] && a[5] === b[5];
+  }
+  /**
+   * Returns whether or not the matrices have approximately the same elements in the same position.
+   *
+   * @param {ReadonlyMat2d} a The first matrix.
+   * @param {ReadonlyMat2d} b The second matrix.
+   * @returns {Boolean} True if the matrices are equal, false otherwise.
+   */
+
+  function equals$2(a, b) {
+    var a0 = a[0],
+        a1 = a[1],
+        a2 = a[2],
+        a3 = a[3],
+        a4 = a[4],
+        a5 = a[5];
+    var b0 = b[0],
+        b1 = b[1],
+        b2 = b[2],
+        b3 = b[3],
+        b4 = b[4],
+        b5 = b[5];
+    return Math.abs(a0 - b0) <= EPSILON * Math.max(1.0, Math.abs(a0), Math.abs(b0)) && Math.abs(a1 - b1) <= EPSILON * Math.max(1.0, Math.abs(a1), Math.abs(b1)) && Math.abs(a2 - b2) <= EPSILON * Math.max(1.0, Math.abs(a2), Math.abs(b2)) && Math.abs(a3 - b3) <= EPSILON * Math.max(1.0, Math.abs(a3), Math.abs(b3)) && Math.abs(a4 - b4) <= EPSILON * Math.max(1.0, Math.abs(a4), Math.abs(b4)) && Math.abs(a5 - b5) <= EPSILON * Math.max(1.0, Math.abs(a5), Math.abs(b5));
+  }
+  /**
+   * Alias for {@link mat2d.multiply}
+   * @function
+   */
+
+  var mul$1 = multiply$1;
+  /**
+   * Alias for {@link mat2d.subtract}
+   * @function
+   */
+
+  var sub$1 = subtract$1;
+
+  var mat2d = /*#__PURE__*/Object.freeze({
+    __proto__: null,
+    create: create$1,
+    clone: clone$1,
+    copy: copy$1,
+    identity: identity$1,
+    fromValues: fromValues$1,
+    set: set$1,
+    invert: invert$1,
+    determinant: determinant$1,
+    multiply: multiply$1,
+    rotate: rotate$1,
+    scale: scale$1,
+    translate: translate,
+    fromRotation: fromRotation$1,
+    fromScaling: fromScaling$1,
+    fromTranslation: fromTranslation,
+    str: str$1,
+    frob: frob$1,
+    add: add$1,
+    subtract: subtract$1,
+    multiplyScalar: multiplyScalar$1,
+    multiplyScalarAndAdd: multiplyScalarAndAdd$1,
+    exactEquals: exactEquals$1,
+    equals: equals$2,
+    mul: mul$1,
+    sub: sub$1
+  });
+
+  /**
+   * 3x3 Matrix
+   * @module mat3
+   */
+
+  /**
+   * Creates a new identity mat3
+   *
+   * @returns {mat3} a new 3x3 matrix
+   */
+
+  function create$2() {
+    var out = new ARRAY_TYPE(9);
+
+    if (ARRAY_TYPE != Float32Array) {
+      out[1] = 0;
+      out[2] = 0;
+      out[3] = 0;
+      out[5] = 0;
+      out[6] = 0;
+      out[7] = 0;
+    }
+
+    out[0] = 1;
+    out[4] = 1;
+    out[8] = 1;
+    return out;
+  }
+  /**
+   * Copies the upper-left 3x3 values into the given mat3.
+   *
+   * @param {mat3} out the receiving 3x3 matrix
+   * @param {ReadonlyMat4} a   the source 4x4 matrix
+   * @returns {mat3} out
+   */
+
+  function fromMat4(out, a) {
+    out[0] = a[0];
+    out[1] = a[1];
+    out[2] = a[2];
+    out[3] = a[4];
+    out[4] = a[5];
+    out[5] = a[6];
+    out[6] = a[8];
+    out[7] = a[9];
+    out[8] = a[10];
+    return out;
+  }
+  /**
+   * Creates a new mat3 initialized with values from an existing matrix
+   *
+   * @param {ReadonlyMat3} a matrix to clone
+   * @returns {mat3} a new 3x3 matrix
+   */
+
+  function clone$2(a) {
+    var out = new ARRAY_TYPE(9);
+    out[0] = a[0];
+    out[1] = a[1];
+    out[2] = a[2];
+    out[3] = a[3];
+    out[4] = a[4];
+    out[5] = a[5];
+    out[6] = a[6];
+    out[7] = a[7];
+    out[8] = a[8];
+    return out;
+  }
+  /**
+   * Copy the values from one mat3 to another
+   *
+   * @param {mat3} out the receiving matrix
+   * @param {ReadonlyMat3} a the source matrix
+   * @returns {mat3} out
+   */
+
+  function copy$2(out, a) {
+    out[0] = a[0];
+    out[1] = a[1];
+    out[2] = a[2];
+    out[3] = a[3];
+    out[4] = a[4];
+    out[5] = a[5];
+    out[6] = a[6];
+    out[7] = a[7];
+    out[8] = a[8];
+    return out;
+  }
+  /**
+   * Create a new mat3 with the given values
+   *
+   * @param {Number} m00 Component in column 0, row 0 position (index 0)
+   * @param {Number} m01 Component in column 0, row 1 position (index 1)
+   * @param {Number} m02 Component in column 0, row 2 position (index 2)
+   * @param {Number} m10 Component in column 1, row 0 position (index 3)
+   * @param {Number} m11 Component in column 1, row 1 position (index 4)
+   * @param {Number} m12 Component in column 1, row 2 position (index 5)
+   * @param {Number} m20 Component in column 2, row 0 position (index 6)
+   * @param {Number} m21 Component in column 2, row 1 position (index 7)
+   * @param {Number} m22 Component in column 2, row 2 position (index 8)
+   * @returns {mat3} A new mat3
+   */
+
+  function fromValues$2(m00, m01, m02, m10, m11, m12, m20, m21, m22) {
+    var out = new ARRAY_TYPE(9);
+    out[0] = m00;
+    out[1] = m01;
+    out[2] = m02;
+    out[3] = m10;
+    out[4] = m11;
+    out[5] = m12;
+    out[6] = m20;
+    out[7] = m21;
+    out[8] = m22;
+    return out;
+  }
+  /**
+   * Set the components of a mat3 to the given values
+   *
+   * @param {mat3} out the receiving matrix
+   * @param {Number} m00 Component in column 0, row 0 position (index 0)
+   * @param {Number} m01 Component in column 0, row 1 position (index 1)
+   * @param {Number} m02 Component in column 0, row 2 position (index 2)
+   * @param {Number} m10 Component in column 1, row 0 position (index 3)
+   * @param {Number} m11 Component in column 1, row 1 position (index 4)
+   * @param {Number} m12 Component in column 1, row 2 position (index 5)
+   * @param {Number} m20 Component in column 2, row 0 position (index 6)
+   * @param {Number} m21 Component in column 2, row 1 position (index 7)
+   * @param {Number} m22 Component in column 2, row 2 position (index 8)
+   * @returns {mat3} out
+   */
+
+  function set$2(out, m00, m01, m02, m10, m11, m12, m20, m21, m22) {
+    out[0] = m00;
+    out[1] = m01;
+    out[2] = m02;
+    out[3] = m10;
+    out[4] = m11;
+    out[5] = m12;
+    out[6] = m20;
+    out[7] = m21;
+    out[8] = m22;
+    return out;
+  }
+  /**
+   * Set a mat3 to the identity matrix
+   *
+   * @param {mat3} out the receiving matrix
+   * @returns {mat3} out
+   */
+
+  function identity$2(out) {
+    out[0] = 1;
+    out[1] = 0;
+    out[2] = 0;
+    out[3] = 0;
+    out[4] = 1;
+    out[5] = 0;
+    out[6] = 0;
+    out[7] = 0;
+    out[8] = 1;
+    return out;
+  }
+  /**
+   * Transpose the values of a mat3
+   *
+   * @param {mat3} out the receiving matrix
+   * @param {ReadonlyMat3} a the source matrix
+   * @returns {mat3} out
+   */
+
+  function transpose$1(out, a) {
+    // If we are transposing ourselves we can skip a few steps but have to cache some values
+    if (out === a) {
+      var a01 = a[1],
+          a02 = a[2],
+          a12 = a[5];
+      out[1] = a[3];
+      out[2] = a[6];
+      out[3] = a01;
+      out[5] = a[7];
+      out[6] = a02;
+      out[7] = a12;
+    } else {
+      out[0] = a[0];
+      out[1] = a[3];
+      out[2] = a[6];
+      out[3] = a[1];
+      out[4] = a[4];
+      out[5] = a[7];
+      out[6] = a[2];
+      out[7] = a[5];
+      out[8] = a[8];
+    }
+
+    return out;
+  }
+  /**
+   * Inverts a mat3
+   *
+   * @param {mat3} out the receiving matrix
+   * @param {ReadonlyMat3} a the source matrix
+   * @returns {mat3} out
+   */
+
+  function invert$2(out, a) {
+    var a00 = a[0],
+        a01 = a[1],
+        a02 = a[2];
+    var a10 = a[3],
+        a11 = a[4],
+        a12 = a[5];
+    var a20 = a[6],
+        a21 = a[7],
+        a22 = a[8];
+    var b01 = a22 * a11 - a12 * a21;
+    var b11 = -a22 * a10 + a12 * a20;
+    var b21 = a21 * a10 - a11 * a20; // Calculate the determinant
+
+    var det = a00 * b01 + a01 * b11 + a02 * b21;
+
+    if (!det) {
+      return null;
+    }
+
+    det = 1.0 / det;
+    out[0] = b01 * det;
+    out[1] = (-a22 * a01 + a02 * a21) * det;
+    out[2] = (a12 * a01 - a02 * a11) * det;
+    out[3] = b11 * det;
+    out[4] = (a22 * a00 - a02 * a20) * det;
+    out[5] = (-a12 * a00 + a02 * a10) * det;
+    out[6] = b21 * det;
+    out[7] = (-a21 * a00 + a01 * a20) * det;
+    out[8] = (a11 * a00 - a01 * a10) * det;
+    return out;
+  }
+  /**
+   * Calculates the adjugate of a mat3
+   *
+   * @param {mat3} out the receiving matrix
+   * @param {ReadonlyMat3} a the source matrix
+   * @returns {mat3} out
+   */
+
+  function adjoint$1(out, a) {
+    var a00 = a[0],
+        a01 = a[1],
+        a02 = a[2];
+    var a10 = a[3],
+        a11 = a[4],
+        a12 = a[5];
+    var a20 = a[6],
+        a21 = a[7],
+        a22 = a[8];
+    out[0] = a11 * a22 - a12 * a21;
+    out[1] = a02 * a21 - a01 * a22;
+    out[2] = a01 * a12 - a02 * a11;
+    out[3] = a12 * a20 - a10 * a22;
+    out[4] = a00 * a22 - a02 * a20;
+    out[5] = a02 * a10 - a00 * a12;
+    out[6] = a10 * a21 - a11 * a20;
+    out[7] = a01 * a20 - a00 * a21;
+    out[8] = a00 * a11 - a01 * a10;
+    return out;
+  }
+  /**
+   * Calculates the determinant of a mat3
+   *
+   * @param {ReadonlyMat3} a the source matrix
+   * @returns {Number} determinant of a
+   */
+
+  function determinant$2(a) {
+    var a00 = a[0],
+        a01 = a[1],
+        a02 = a[2];
+    var a10 = a[3],
+        a11 = a[4],
+        a12 = a[5];
+    var a20 = a[6],
+        a21 = a[7],
+        a22 = a[8];
+    return a00 * (a22 * a11 - a12 * a21) + a01 * (-a22 * a10 + a12 * a20) + a02 * (a21 * a10 - a11 * a20);
+  }
+  /**
+   * Multiplies two mat3's
+   *
+   * @param {mat3} out the receiving matrix
+   * @param {ReadonlyMat3} a the first operand
+   * @param {ReadonlyMat3} b the second operand
+   * @returns {mat3} out
+   */
+
+  function multiply$2(out, a, b) {
+    var a00 = a[0],
+        a01 = a[1],
+        a02 = a[2];
+    var a10 = a[3],
+        a11 = a[4],
+        a12 = a[5];
+    var a20 = a[6],
+        a21 = a[7],
+        a22 = a[8];
+    var b00 = b[0],
+        b01 = b[1],
+        b02 = b[2];
+    var b10 = b[3],
+        b11 = b[4],
+        b12 = b[5];
+    var b20 = b[6],
+        b21 = b[7],
+        b22 = b[8];
+    out[0] = b00 * a00 + b01 * a10 + b02 * a20;
+    out[1] = b00 * a01 + b01 * a11 + b02 * a21;
+    out[2] = b00 * a02 + b01 * a12 + b02 * a22;
+    out[3] = b10 * a00 + b11 * a10 + b12 * a20;
+    out[4] = b10 * a01 + b11 * a11 + b12 * a21;
+    out[5] = b10 * a02 + b11 * a12 + b12 * a22;
+    out[6] = b20 * a00 + b21 * a10 + b22 * a20;
+    out[7] = b20 * a01 + b21 * a11 + b22 * a21;
+    out[8] = b20 * a02 + b21 * a12 + b22 * a22;
+    return out;
+  }
+  /**
+   * Translate a mat3 by the given vector
+   *
+   * @param {mat3} out the receiving matrix
+   * @param {ReadonlyMat3} a the matrix to translate
+   * @param {ReadonlyVec2} v vector to translate by
+   * @returns {mat3} out
+   */
+
+  function translate$1(out, a, v) {
+    var a00 = a[0],
+        a01 = a[1],
+        a02 = a[2],
+        a10 = a[3],
+        a11 = a[4],
+        a12 = a[5],
+        a20 = a[6],
+        a21 = a[7],
+        a22 = a[8],
+        x = v[0],
+        y = v[1];
+    out[0] = a00;
+    out[1] = a01;
+    out[2] = a02;
+    out[3] = a10;
+    out[4] = a11;
+    out[5] = a12;
+    out[6] = x * a00 + y * a10 + a20;
+    out[7] = x * a01 + y * a11 + a21;
+    out[8] = x * a02 + y * a12 + a22;
+    return out;
+  }
+  /**
+   * Rotates a mat3 by the given angle
+   *
+   * @param {mat3} out the receiving matrix
+   * @param {ReadonlyMat3} a the matrix to rotate
+   * @param {Number} rad the angle to rotate the matrix by
+   * @returns {mat3} out
+   */
+
+  function rotate$2(out, a, rad) {
+    var a00 = a[0],
+        a01 = a[1],
+        a02 = a[2],
+        a10 = a[3],
+        a11 = a[4],
+        a12 = a[5],
+        a20 = a[6],
+        a21 = a[7],
+        a22 = a[8],
+        s = Math.sin(rad),
+        c = Math.cos(rad);
+    out[0] = c * a00 + s * a10;
+    out[1] = c * a01 + s * a11;
+    out[2] = c * a02 + s * a12;
+    out[3] = c * a10 - s * a00;
+    out[4] = c * a11 - s * a01;
+    out[5] = c * a12 - s * a02;
+    out[6] = a20;
+    out[7] = a21;
+    out[8] = a22;
+    return out;
+  }
+  /**
+   * Scales the mat3 by the dimensions in the given vec2
+   *
+   * @param {mat3} out the receiving matrix
+   * @param {ReadonlyMat3} a the matrix to rotate
+   * @param {ReadonlyVec2} v the vec2 to scale the matrix by
+   * @returns {mat3} out
+   **/
+
+  function scale$2(out, a, v) {
+    var x = v[0],
+        y = v[1];
+    out[0] = x * a[0];
+    out[1] = x * a[1];
+    out[2] = x * a[2];
+    out[3] = y * a[3];
+    out[4] = y * a[4];
+    out[5] = y * a[5];
+    out[6] = a[6];
+    out[7] = a[7];
+    out[8] = a[8];
+    return out;
+  }
+  /**
+   * Creates a matrix from a vector translation
+   * This is equivalent to (but much faster than):
+   *
+   *     mat3.identity(dest);
+   *     mat3.translate(dest, dest, vec);
+   *
+   * @param {mat3} out mat3 receiving operation result
+   * @param {ReadonlyVec2} v Translation vector
+   * @returns {mat3} out
+   */
+
+  function fromTranslation$1(out, v) {
+    out[0] = 1;
+    out[1] = 0;
+    out[2] = 0;
+    out[3] = 0;
+    out[4] = 1;
+    out[5] = 0;
+    out[6] = v[0];
+    out[7] = v[1];
+    out[8] = 1;
+    return out;
+  }
+  /**
+   * Creates a matrix from a given angle
+   * This is equivalent to (but much faster than):
+   *
+   *     mat3.identity(dest);
+   *     mat3.rotate(dest, dest, rad);
+   *
+   * @param {mat3} out mat3 receiving operation result
+   * @param {Number} rad the angle to rotate the matrix by
+   * @returns {mat3} out
+   */
+
+  function fromRotation$2(out, rad) {
+    var s = Math.sin(rad),
+        c = Math.cos(rad);
+    out[0] = c;
+    out[1] = s;
+    out[2] = 0;
+    out[3] = -s;
+    out[4] = c;
+    out[5] = 0;
+    out[6] = 0;
+    out[7] = 0;
+    out[8] = 1;
+    return out;
+  }
+  /**
+   * Creates a matrix from a vector scaling
+   * This is equivalent to (but much faster than):
+   *
+   *     mat3.identity(dest);
+   *     mat3.scale(dest, dest, vec);
+   *
+   * @param {mat3} out mat3 receiving operation result
+   * @param {ReadonlyVec2} v Scaling vector
+   * @returns {mat3} out
+   */
+
+  function fromScaling$2(out, v) {
+    out[0] = v[0];
+    out[1] = 0;
+    out[2] = 0;
+    out[3] = 0;
+    out[4] = v[1];
+    out[5] = 0;
+    out[6] = 0;
+    out[7] = 0;
+    out[8] = 1;
+    return out;
+  }
+  /**
+   * Copies the values from a mat2d into a mat3
+   *
+   * @param {mat3} out the receiving matrix
+   * @param {ReadonlyMat2d} a the matrix to copy
+   * @returns {mat3} out
+   **/
+
+  function fromMat2d(out, a) {
+    out[0] = a[0];
+    out[1] = a[1];
+    out[2] = 0;
+    out[3] = a[2];
+    out[4] = a[3];
+    out[5] = 0;
+    out[6] = a[4];
+    out[7] = a[5];
+    out[8] = 1;
+    return out;
+  }
+  /**
+   * Calculates a 3x3 matrix from the given quaternion
+   *
+   * @param {mat3} out mat3 receiving operation result
+   * @param {ReadonlyQuat} q Quaternion to create matrix from
+   *
+   * @returns {mat3} out
+   */
+
+  function fromQuat(out, q) {
+    var x = q[0],
+        y = q[1],
+        z = q[2],
+        w = q[3];
+    var x2 = x + x;
+    var y2 = y + y;
+    var z2 = z + z;
+    var xx = x * x2;
+    var yx = y * x2;
+    var yy = y * y2;
+    var zx = z * x2;
+    var zy = z * y2;
+    var zz = z * z2;
+    var wx = w * x2;
+    var wy = w * y2;
+    var wz = w * z2;
+    out[0] = 1 - yy - zz;
+    out[3] = yx - wz;
+    out[6] = zx + wy;
+    out[1] = yx + wz;
+    out[4] = 1 - xx - zz;
+    out[7] = zy - wx;
+    out[2] = zx - wy;
+    out[5] = zy + wx;
+    out[8] = 1 - xx - yy;
+    return out;
+  }
+  /**
+   * Calculates a 3x3 normal matrix (transpose inverse) from the 4x4 matrix
+   *
+   * @param {mat3} out mat3 receiving operation result
+   * @param {ReadonlyMat4} a Mat4 to derive the normal matrix from
+   *
+   * @returns {mat3} out
+   */
+
+  function normalFromMat4(out, a) {
+    var a00 = a[0],
+        a01 = a[1],
+        a02 = a[2],
+        a03 = a[3];
+    var a10 = a[4],
+        a11 = a[5],
+        a12 = a[6],
+        a13 = a[7];
+    var a20 = a[8],
+        a21 = a[9],
+        a22 = a[10],
+        a23 = a[11];
+    var a30 = a[12],
+        a31 = a[13],
+        a32 = a[14],
+        a33 = a[15];
+    var b00 = a00 * a11 - a01 * a10;
+    var b01 = a00 * a12 - a02 * a10;
+    var b02 = a00 * a13 - a03 * a10;
+    var b03 = a01 * a12 - a02 * a11;
+    var b04 = a01 * a13 - a03 * a11;
+    var b05 = a02 * a13 - a03 * a12;
+    var b06 = a20 * a31 - a21 * a30;
+    var b07 = a20 * a32 - a22 * a30;
+    var b08 = a20 * a33 - a23 * a30;
+    var b09 = a21 * a32 - a22 * a31;
+    var b10 = a21 * a33 - a23 * a31;
+    var b11 = a22 * a33 - a23 * a32; // Calculate the determinant
+
+    var det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;
+
+    if (!det) {
+      return null;
+    }
+
+    det = 1.0 / det;
+    out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det;
+    out[1] = (a12 * b08 - a10 * b11 - a13 * b07) * det;
+    out[2] = (a10 * b10 - a11 * b08 + a13 * b06) * det;
+    out[3] = (a02 * b10 - a01 * b11 - a03 * b09) * det;
+    out[4] = (a00 * b11 - a02 * b08 + a03 * b07) * det;
+    out[5] = (a01 * b08 - a00 * b10 - a03 * b06) * det;
+    out[6] = (a31 * b05 - a32 * b04 + a33 * b03) * det;
+    out[7] = (a32 * b02 - a30 * b05 - a33 * b01) * det;
+    out[8] = (a30 * b04 - a31 * b02 + a33 * b00) * det;
+    return out;
+  }
+  /**
+   * Generates a 2D projection matrix with the given bounds
+   *
+   * @param {mat3} out mat3 frustum matrix will be written into
+   * @param {number} width Width of your gl context
+   * @param {number} height Height of gl context
+   * @returns {mat3} out
+   */
+
+  function projection(out, width, height) {
+    out[0] = 2 / width;
+    out[1] = 0;
+    out[2] = 0;
+    out[3] = 0;
+    out[4] = -2 / height;
+    out[5] = 0;
+    out[6] = -1;
+    out[7] = 1;
+    out[8] = 1;
+    return out;
+  }
+  /**
+   * Returns a string representation of a mat3
+   *
+   * @param {ReadonlyMat3} a matrix to represent as a string
+   * @returns {String} string representation of the matrix
+   */
+
+  function str$2(a) {
+    return "mat3(" + a[0] + ", " + a[1] + ", " + a[2] + ", " + a[3] + ", " + a[4] + ", " + a[5] + ", " + a[6] + ", " + a[7] + ", " + a[8] + ")";
+  }
+  /**
+   * Returns Frobenius norm of a mat3
+   *
+   * @param {ReadonlyMat3} a the matrix to calculate Frobenius norm of
+   * @returns {Number} Frobenius norm
+   */
+
+  function frob$2(a) {
+    return Math.hypot(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8]);
+  }
+  /**
+   * Adds two mat3's
+   *
+   * @param {mat3} out the receiving matrix
+   * @param {ReadonlyMat3} a the first operand
+   * @param {ReadonlyMat3} b the second operand
+   * @returns {mat3} out
+   */
+
+  function add$2(out, a, b) {
+    out[0] = a[0] + b[0];
+    out[1] = a[1] + b[1];
+    out[2] = a[2] + b[2];
+    out[3] = a[3] + b[3];
+    out[4] = a[4] + b[4];
+    out[5] = a[5] + b[5];
+    out[6] = a[6] + b[6];
+    out[7] = a[7] + b[7];
+    out[8] = a[8] + b[8];
+    return out;
+  }
+  /**
+   * Subtracts matrix b from matrix a
+   *
+   * @param {mat3} out the receiving matrix
+   * @param {ReadonlyMat3} a the first operand
+   * @param {ReadonlyMat3} b the second operand
+   * @returns {mat3} out
+   */
+
+  function subtract$2(out, a, b) {
+    out[0] = a[0] - b[0];
+    out[1] = a[1] - b[1];
+    out[2] = a[2] - b[2];
+    out[3] = a[3] - b[3];
+    out[4] = a[4] - b[4];
+    out[5] = a[5] - b[5];
+    out[6] = a[6] - b[6];
+    out[7] = a[7] - b[7];
+    out[8] = a[8] - b[8];
+    return out;
+  }
+  /**
+   * Multiply each element of the matrix by a scalar.
+   *
+   * @param {mat3} out the receiving matrix
+   * @param {ReadonlyMat3} a the matrix to scale
+   * @param {Number} b amount to scale the matrix's elements by
+   * @returns {mat3} out
+   */
+
+  function multiplyScalar$2(out, a, b) {
+    out[0] = a[0] * b;
+    out[1] = a[1] * b;
+    out[2] = a[2] * b;
+    out[3] = a[3] * b;
+    out[4] = a[4] * b;
+    out[5] = a[5] * b;
+    out[6] = a[6] * b;
+    out[7] = a[7] * b;
+    out[8] = a[8] * b;
+    return out;
+  }
+  /**
+   * Adds two mat3's after multiplying each element of the second operand by a scalar value.
+   *
+   * @param {mat3} out the receiving vector
+   * @param {ReadonlyMat3} a the first operand
+   * @param {ReadonlyMat3} b the second operand
+   * @param {Number} scale the amount to scale b's elements by before adding
+   * @returns {mat3} out
+   */
+
+  function multiplyScalarAndAdd$2(out, a, b, scale) {
+    out[0] = a[0] + b[0] * scale;
+    out[1] = a[1] + b[1] * scale;
+    out[2] = a[2] + b[2] * scale;
+    out[3] = a[3] + b[3] * scale;
+    out[4] = a[4] + b[4] * scale;
+    out[5] = a[5] + b[5] * scale;
+    out[6] = a[6] + b[6] * scale;
+    out[7] = a[7] + b[7] * scale;
+    out[8] = a[8] + b[8] * scale;
+    return out;
+  }
+  /**
+   * Returns whether or not the matrices have exactly the same elements in the same position (when compared with ===)
+   *
+   * @param {ReadonlyMat3} a The first matrix.
+   * @param {ReadonlyMat3} b The second matrix.
+   * @returns {Boolean} True if the matrices are equal, false otherwise.
+   */
+
+  function exactEquals$2(a, b) {
+    return a[0] === b[0] && a[1] === b[1] && a[2] === b[2] && a[3] === b[3] && a[4] === b[4] && a[5] === b[5] && a[6] === b[6] && a[7] === b[7] && a[8] === b[8];
+  }
+  /**
+   * Returns whether or not the matrices have approximately the same elements in the same position.
+   *
+   * @param {ReadonlyMat3} a The first matrix.
+   * @param {ReadonlyMat3} b The second matrix.
+   * @returns {Boolean} True if the matrices are equal, false otherwise.
+   */
+
+  function equals$3(a, b) {
+    var a0 = a[0],
+        a1 = a[1],
+        a2 = a[2],
+        a3 = a[3],
+        a4 = a[4],
+        a5 = a[5],
+        a6 = a[6],
+        a7 = a[7],
+        a8 = a[8];
+    var b0 = b[0],
+        b1 = b[1],
+        b2 = b[2],
+        b3 = b[3],
+        b4 = b[4],
+        b5 = b[5],
+        b6 = b[6],
+        b7 = b[7],
+        b8 = b[8];
+    return Math.abs(a0 - b0) <= EPSILON * Math.max(1.0, Math.abs(a0), Math.abs(b0)) && Math.abs(a1 - b1) <= EPSILON * Math.max(1.0, Math.abs(a1), Math.abs(b1)) && Math.abs(a2 - b2) <= EPSILON * Math.max(1.0, Math.abs(a2), Math.abs(b2)) && Math.abs(a3 - b3) <= EPSILON * Math.max(1.0, Math.abs(a3), Math.abs(b3)) && Math.abs(a4 - b4) <= EPSILON * Math.max(1.0, Math.abs(a4), Math.abs(b4)) && Math.abs(a5 - b5) <= EPSILON * Math.max(1.0, Math.abs(a5), Math.abs(b5)) && Math.abs(a6 - b6) <= EPSILON * Math.max(1.0, Math.abs(a6), Math.abs(b6)) && Math.abs(a7 - b7) <= EPSILON * Math.max(1.0, Math.abs(a7), Math.abs(b7)) && Math.abs(a8 - b8) <= EPSILON * Math.max(1.0, Math.abs(a8), Math.abs(b8));
+  }
+  /**
+   * Alias for {@link mat3.multiply}
+   * @function
+   */
+
+  var mul$2 = multiply$2;
+  /**
+   * Alias for {@link mat3.subtract}
+   * @function
+   */
+
+  var sub$2 = subtract$2;
+
+  var mat3 = /*#__PURE__*/Object.freeze({
+    __proto__: null,
+    create: create$2,
+    fromMat4: fromMat4,
+    clone: clone$2,
+    copy: copy$2,
+    fromValues: fromValues$2,
+    set: set$2,
+    identity: identity$2,
+    transpose: transpose$1,
+    invert: invert$2,
+    adjoint: adjoint$1,
+    determinant: determinant$2,
+    multiply: multiply$2,
+    translate: translate$1,
+    rotate: rotate$2,
+    scale: scale$2,
+    fromTranslation: fromTranslation$1,
+    fromRotation: fromRotation$2,
+    fromScaling: fromScaling$2,
+    fromMat2d: fromMat2d,
+    fromQuat: fromQuat,
+    normalFromMat4: normalFromMat4,
+    projection: projection,
+    str: str$2,
+    frob: frob$2,
+    add: add$2,
+    subtract: subtract$2,
+    multiplyScalar: multiplyScalar$2,
+    multiplyScalarAndAdd: multiplyScalarAndAdd$2,
+    exactEquals: exactEquals$2,
+    equals: equals$3,
+    mul: mul$2,
+    sub: sub$2
+  });
+
+  /**
+   * 4x4 Matrix<br>Format: column-major, when typed out it looks like row-major<br>The matrices are being post multiplied.
+   * @module mat4
+   */
+
+  /**
+   * Creates a new identity mat4
+   *
+   * @returns {mat4} a new 4x4 matrix
+   */
+
+  function create$3() {
+    var out = new ARRAY_TYPE(16);
+
+    if (ARRAY_TYPE != Float32Array) {
+      out[1] = 0;
+      out[2] = 0;
+      out[3] = 0;
+      out[4] = 0;
+      out[6] = 0;
+      out[7] = 0;
+      out[8] = 0;
+      out[9] = 0;
+      out[11] = 0;
+      out[12] = 0;
+      out[13] = 0;
+      out[14] = 0;
+    }
+
+    out[0] = 1;
+    out[5] = 1;
+    out[10] = 1;
+    out[15] = 1;
+    return out;
+  }
+  /**
+   * Creates a new mat4 initialized with values from an existing matrix
+   *
+   * @param {ReadonlyMat4} a matrix to clone
+   * @returns {mat4} a new 4x4 matrix
+   */
+
+  function clone$3(a) {
+    var out = new ARRAY_TYPE(16);
+    out[0] = a[0];
+    out[1] = a[1];
+    out[2] = a[2];
+    out[3] = a[3];
+    out[4] = a[4];
+    out[5] = a[5];
+    out[6] = a[6];
+    out[7] = a[7];
+    out[8] = a[8];
+    out[9] = a[9];
+    out[10] = a[10];
+    out[11] = a[11];
+    out[12] = a[12];
+    out[13] = a[13];
+    out[14] = a[14];
+    out[15] = a[15];
+    return out;
+  }
+  /**
+   * Copy the values from one mat4 to another
+   *
+   * @param {mat4} out the receiving matrix
+   * @param {ReadonlyMat4} a the source matrix
+   * @returns {mat4} out
+   */
+
+  function copy$3(out, a) {
+    out[0] = a[0];
+    out[1] = a[1];
+    out[2] = a[2];
+    out[3] = a[3];
+    out[4] = a[4];
+    out[5] = a[5];
+    out[6] = a[6];
+    out[7] = a[7];
+    out[8] = a[8];
+    out[9] = a[9];
+    out[10] = a[10];
+    out[11] = a[11];
+    out[12] = a[12];
+    out[13] = a[13];
+    out[14] = a[14];
+    out[15] = a[15];
+    return out;
+  }
+  /**
+   * Create a new mat4 with the given values
+   *
+   * @param {Number} m00 Component in column 0, row 0 position (index 0)
+   * @param {Number} m01 Component in column 0, row 1 position (index 1)
+   * @param {Number} m02 Component in column 0, row 2 position (index 2)
+   * @param {Number} m03 Component in column 0, row 3 position (index 3)
+   * @param {Number} m10 Component in column 1, row 0 position (index 4)
+   * @param {Number} m11 Component in column 1, row 1 position (index 5)
+   * @param {Number} m12 Component in column 1, row 2 position (index 6)
+   * @param {Number} m13 Component in column 1, row 3 position (index 7)
+   * @param {Number} m20 Component in column 2, row 0 position (index 8)
+   * @param {Number} m21 Component in column 2, row 1 position (index 9)
+   * @param {Number} m22 Component in column 2, row 2 position (index 10)
+   * @param {Number} m23 Component in column 2, row 3 position (index 11)
+   * @param {Number} m30 Component in column 3, row 0 position (index 12)
+   * @param {Number} m31 Component in column 3, row 1 position (index 13)
+   * @param {Number} m32 Component in column 3, row 2 position (index 14)
+   * @param {Number} m33 Component in column 3, row 3 position (index 15)
+   * @returns {mat4} A new mat4
+   */
+
+  function fromValues$3(m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23, m30, m31, m32, m33) {
+    var out = new ARRAY_TYPE(16);
+    out[0] = m00;
+    out[1] = m01;
+    out[2] = m02;
+    out[3] = m03;
+    out[4] = m10;
+    out[5] = m11;
+    out[6] = m12;
+    out[7] = m13;
+    out[8] = m20;
+    out[9] = m21;
+    out[10] = m22;
+    out[11] = m23;
+    out[12] = m30;
+    out[13] = m31;
+    out[14] = m32;
+    out[15] = m33;
+    return out;
+  }
+  /**
+   * Set the components of a mat4 to the given values
+   *
+   * @param {mat4} out the receiving matrix
+   * @param {Number} m00 Component in column 0, row 0 position (index 0)
+   * @param {Number} m01 Component in column 0, row 1 position (index 1)
+   * @param {Number} m02 Component in column 0, row 2 position (index 2)
+   * @param {Number} m03 Component in column 0, row 3 position (index 3)
+   * @param {Number} m10 Component in column 1, row 0 position (index 4)
+   * @param {Number} m11 Component in column 1, row 1 position (index 5)
+   * @param {Number} m12 Component in column 1, row 2 position (index 6)
+   * @param {Number} m13 Component in column 1, row 3 position (index 7)
+   * @param {Number} m20 Component in column 2, row 0 position (index 8)
+   * @param {Number} m21 Component in column 2, row 1 position (index 9)
+   * @param {Number} m22 Component in column 2, row 2 position (index 10)
+   * @param {Number} m23 Component in column 2, row 3 position (index 11)
+   * @param {Number} m30 Component in column 3, row 0 position (index 12)
+   * @param {Number} m31 Component in column 3, row 1 position (index 13)
+   * @param {Number} m32 Component in column 3, row 2 position (index 14)
+   * @param {Number} m33 Component in column 3, row 3 position (index 15)
+   * @returns {mat4} out
+   */
+
+  function set$3(out, m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23, m30, m31, m32, m33) {
+    out[0] = m00;
+    out[1] = m01;
+    out[2] = m02;
+    out[3] = m03;
+    out[4] = m10;
+    out[5] = m11;
+    out[6] = m12;
+    out[7] = m13;
+    out[8] = m20;
+    out[9] = m21;
+    out[10] = m22;
+    out[11] = m23;
+    out[12] = m30;
+    out[13] = m31;
+    out[14] = m32;
+    out[15] = m33;
+    return out;
+  }
+  /**
+   * Set a mat4 to the identity matrix
+   *
+   * @param {mat4} out the receiving matrix
+   * @returns {mat4} out
+   */
+
+  function identity$3(out) {
+    out[0] = 1;
+    out[1] = 0;
+    out[2] = 0;
+    out[3] = 0;
+    out[4] = 0;
+    out[5] = 1;
+    out[6] = 0;
+    out[7] = 0;
+    out[8] = 0;
+    out[9] = 0;
+    out[10] = 1;
+    out[11] = 0;
+    out[12] = 0;
+    out[13] = 0;
+    out[14] = 0;
+    out[15] = 1;
+    return out;
+  }
+  /**
+   * Transpose the values of a mat4
+   *
+   * @param {mat4} out the receiving matrix
+   * @param {ReadonlyMat4} a the source matrix
+   * @returns {mat4} out
+   */
+
+  function transpose$2(out, a) {
+    // If we are transposing ourselves we can skip a few steps but have to cache some values
+    if (out === a) {
+      var a01 = a[1],
+          a02 = a[2],
+          a03 = a[3];
+      var a12 = a[6],
+          a13 = a[7];
+      var a23 = a[11];
+      out[1] = a[4];
+      out[2] = a[8];
+      out[3] = a[12];
+      out[4] = a01;
+      out[6] = a[9];
+      out[7] = a[13];
+      out[8] = a02;
+      out[9] = a12;
+      out[11] = a[14];
+      out[12] = a03;
+      out[13] = a13;
+      out[14] = a23;
+    } else {
+      out[0] = a[0];
+      out[1] = a[4];
+      out[2] = a[8];
+      out[3] = a[12];
+      out[4] = a[1];
+      out[5] = a[5];
+      out[6] = a[9];
+      out[7] = a[13];
+      out[8] = a[2];
+      out[9] = a[6];
+      out[10] = a[10];
+      out[11] = a[14];
+      out[12] = a[3];
+      out[13] = a[7];
+      out[14] = a[11];
+      out[15] = a[15];
+    }
+
+    return out;
+  }
+  /**
+   * Inverts a mat4
+   *
+   * @param {mat4} out the receiving matrix
+   * @param {ReadonlyMat4} a the source matrix
+   * @returns {mat4} out
+   */
+
+  function invert$3(out, a) {
+    var a00 = a[0],
+        a01 = a[1],
+        a02 = a[2],
+        a03 = a[3];
+    var a10 = a[4],
+        a11 = a[5],
+        a12 = a[6],
+        a13 = a[7];
+    var a20 = a[8],
+        a21 = a[9],
+        a22 = a[10],
+        a23 = a[11];
+    var a30 = a[12],
+        a31 = a[13],
+        a32 = a[14],
+        a33 = a[15];
+    var b00 = a00 * a11 - a01 * a10;
+    var b01 = a00 * a12 - a02 * a10;
+    var b02 = a00 * a13 - a03 * a10;
+    var b03 = a01 * a12 - a02 * a11;
+    var b04 = a01 * a13 - a03 * a11;
+    var b05 = a02 * a13 - a03 * a12;
+    var b06 = a20 * a31 - a21 * a30;
+    var b07 = a20 * a32 - a22 * a30;
+    var b08 = a20 * a33 - a23 * a30;
+    var b09 = a21 * a32 - a22 * a31;
+    var b10 = a21 * a33 - a23 * a31;
+    var b11 = a22 * a33 - a23 * a32; // Calculate the determinant
+
+    var det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;
+
+    if (!det) {
+      return null;
+    }
+
+    det = 1.0 / det;
+    out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det;
+    out[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det;
+    out[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det;
+    out[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det;
+    out[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det;
+    out[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det;
+    out[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det;
+    out[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det;
+    out[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det;
+    out[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det;
+    out[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det;
+    out[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det;
+    out[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det;
+    out[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det;
+    out[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det;
+    out[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det;
+    return out;
+  }
+  /**
+   * Calculates the adjugate of a mat4
+   *
+   * @param {mat4} out the receiving matrix
+   * @param {ReadonlyMat4} a the source matrix
+   * @returns {mat4} out
+   */
+
+  function adjoint$2(out, a) {
+    var a00 = a[0],
+        a01 = a[1],
+        a02 = a[2],
+        a03 = a[3];
+    var a10 = a[4],
+        a11 = a[5],
+        a12 = a[6],
+        a13 = a[7];
+    var a20 = a[8],
+        a21 = a[9],
+        a22 = a[10],
+        a23 = a[11];
+    var a30 = a[12],
+        a31 = a[13],
+        a32 = a[14],
+        a33 = a[15];
+    out[0] = a11 * (a22 * a33 - a23 * a32) - a21 * (a12 * a33 - a13 * a32) + a31 * (a12 * a23 - a13 * a22);
+    out[1] = -(a01 * (a22 * a33 - a23 * a32) - a21 * (a02 * a33 - a03 * a32) + a31 * (a02 * a23 - a03 * a22));
+    out[2] = a01 * (a12 * a33 - a13 * a32) - a11 * (a02 * a33 - a03 * a32) + a31 * (a02 * a13 - a03 * a12);
+    out[3] = -(a01 * (a12 * a23 - a13 * a22) - a11 * (a02 * a23 - a03 * a22) + a21 * (a02 * a13 - a03 * a12));
+    out[4] = -(a10 * (a22 * a33 - a23 * a32) - a20 * (a12 * a33 - a13 * a32) + a30 * (a12 * a23 - a13 * a22));
+    out[5] = a00 * (a22 * a33 - a23 * a32) - a20 * (a02 * a33 - a03 * a32) + a30 * (a02 * a23 - a03 * a22);
+    out[6] = -(a00 * (a12 * a33 - a13 * a32) - a10 * (a02 * a33 - a03 * a32) + a30 * (a02 * a13 - a03 * a12));
+    out[7] = a00 * (a12 * a23 - a13 * a22) - a10 * (a02 * a23 - a03 * a22) + a20 * (a02 * a13 - a03 * a12);
+    out[8] = a10 * (a21 * a33 - a23 * a31) - a20 * (a11 * a33 - a13 * a31) + a30 * (a11 * a23 - a13 * a21);
+    out[9] = -(a00 * (a21 * a33 - a23 * a31) - a20 * (a01 * a33 - a03 * a31) + a30 * (a01 * a23 - a03 * a21));
+    out[10] = a00 * (a11 * a33 - a13 * a31) - a10 * (a01 * a33 - a03 * a31) + a30 * (a01 * a13 - a03 * a11);
+    out[11] = -(a00 * (a11 * a23 - a13 * a21) - a10 * (a01 * a23 - a03 * a21) + a20 * (a01 * a13 - a03 * a11));
+    out[12] = -(a10 * (a21 * a32 - a22 * a31) - a20 * (a11 * a32 - a12 * a31) + a30 * (a11 * a22 - a12 * a21));
+    out[13] = a00 * (a21 * a32 - a22 * a31) - a20 * (a01 * a32 - a02 * a31) + a30 * (a01 * a22 - a02 * a21);
+    out[14] = -(a00 * (a11 * a32 - a12 * a31) - a10 * (a01 * a32 - a02 * a31) + a30 * (a01 * a12 - a02 * a11));
+    out[15] = a00 * (a11 * a22 - a12 * a21) - a10 * (a01 * a22 - a02 * a21) + a20 * (a01 * a12 - a02 * a11);
+    return out;
+  }
+  /**
+   * Calculates the determinant of a mat4
+   *
+   * @param {ReadonlyMat4} a the source matrix
+   * @returns {Number} determinant of a
+   */
+
+  function determinant$3(a) {
+    var a00 = a[0],
+        a01 = a[1],
+        a02 = a[2],
+        a03 = a[3];
+    var a10 = a[4],
+        a11 = a[5],
+        a12 = a[6],
+        a13 = a[7];
+    var a20 = a[8],
+        a21 = a[9],
+        a22 = a[10],
+        a23 = a[11];
+    var a30 = a[12],
+        a31 = a[13],
+        a32 = a[14],
+        a33 = a[15];
+    var b00 = a00 * a11 - a01 * a10;
+    var b01 = a00 * a12 - a02 * a10;
+    var b02 = a00 * a13 - a03 * a10;
+    var b03 = a01 * a12 - a02 * a11;
+    var b04 = a01 * a13 - a03 * a11;
+    var b05 = a02 * a13 - a03 * a12;
+    var b06 = a20 * a31 - a21 * a30;
+    var b07 = a20 * a32 - a22 * a30;
+    var b08 = a20 * a33 - a23 * a30;
+    var b09 = a21 * a32 - a22 * a31;
+    var b10 = a21 * a33 - a23 * a31;
+    var b11 = a22 * a33 - a23 * a32; // Calculate the determinant
+
+    return b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;
+  }
+  /**
+   * Multiplies two mat4s
+   *
+   * @param {mat4} out the receiving matrix
+   * @param {ReadonlyMat4} a the first operand
+   * @param {ReadonlyMat4} b the second operand
+   * @returns {mat4} out
+   */
+
+  function multiply$3(out, a, b) {
+    var a00 = a[0],
+        a01 = a[1],
+        a02 = a[2],
+        a03 = a[3];
+    var a10 = a[4],
+        a11 = a[5],
+        a12 = a[6],
+        a13 = a[7];
+    var a20 = a[8],
+        a21 = a[9],
+        a22 = a[10],
+        a23 = a[11];
+    var a30 = a[12],
+        a31 = a[13],
+        a32 = a[14],
+        a33 = a[15]; // Cache only the current line of the second matrix
+
+    var b0 = b[0],
+        b1 = b[1],
+        b2 = b[2],
+        b3 = b[3];
+    out[0] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30;
+    out[1] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31;
+    out[2] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32;
+    out[3] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33;
+    b0 = b[4];
+    b1 = b[5];
+    b2 = b[6];
+    b3 = b[7];
+    out[4] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30;
+    out[5] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31;
+    out[6] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32;
+    out[7] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33;
+    b0 = b[8];
+    b1 = b[9];
+    b2 = b[10];
+    b3 = b[11];
+    out[8] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30;
+    out[9] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31;
+    out[10] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32;
+    out[11] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33;
+    b0 = b[12];
+    b1 = b[13];
+    b2 = b[14];
+    b3 = b[15];
+    out[12] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30;
+    out[13] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31;
+    out[14] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32;
+    out[15] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33;
+    return out;
+  }
+  /**
+   * Translate a mat4 by the given vector
+   *
+   * @param {mat4} out the receiving matrix
+   * @param {ReadonlyMat4} a the matrix to translate
+   * @param {ReadonlyVec3} v vector to translate by
+   * @returns {mat4} out
+   */
+
+  function translate$2(out, a, v) {
+    var x = v[0],
+        y = v[1],
+        z = v[2];
+    var a00, a01, a02, a03;
+    var a10, a11, a12, a13;
+    var a20, a21, a22, a23;
+
+    if (a === out) {
+      out[12] = a[0] * x + a[4] * y + a[8] * z + a[12];
+      out[13] = a[1] * x + a[5] * y + a[9] * z + a[13];
+      out[14] = a[2] * x + a[6] * y + a[10] * z + a[14];
+      out[15] = a[3] * x + a[7] * y + a[11] * z + a[15];
+    } else {
+      a00 = a[0];
+      a01 = a[1];
+      a02 = a[2];
+      a03 = a[3];
+      a10 = a[4];
+      a11 = a[5];
+      a12 = a[6];
+      a13 = a[7];
+      a20 = a[8];
+      a21 = a[9];
+      a22 = a[10];
+      a23 = a[11];
+      out[0] = a00;
+      out[1] = a01;
+      out[2] = a02;
+      out[3] = a03;
+      out[4] = a10;
+      out[5] = a11;
+      out[6] = a12;
+      out[7] = a13;
+      out[8] = a20;
+      out[9] = a21;
+      out[10] = a22;
+      out[11] = a23;
+      out[12] = a00 * x + a10 * y + a20 * z + a[12];
+      out[13] = a01 * x + a11 * y + a21 * z + a[13];
+      out[14] = a02 * x + a12 * y + a22 * z + a[14];
+      out[15] = a03 * x + a13 * y + a23 * z + a[15];
+    }
+
+    return out;
+  }
+  /**
+   * Scales the mat4 by the dimensions in the given vec3 not using vectorization
+   *
+   * @param {mat4} out the receiving matrix
+   * @param {ReadonlyMat4} a the matrix to scale
+   * @param {ReadonlyVec3} v the vec3 to scale the matrix by
+   * @returns {mat4} out
+   **/
+
+  function scale$3(out, a, v) {
+    var x = v[0],
+        y = v[1],
+        z = v[2];
+    out[0] = a[0] * x;
+    out[1] = a[1] * x;
+    out[2] = a[2] * x;
+    out[3] = a[3] * x;
+    out[4] = a[4] * y;
+    out[5] = a[5] * y;
+    out[6] = a[6] * y;
+    out[7] = a[7] * y;
+    out[8] = a[8] * z;
+    out[9] = a[9] * z;
+    out[10] = a[10] * z;
+    out[11] = a[11] * z;
+    out[12] = a[12];
+    out[13] = a[13];
+    out[14] = a[14];
+    out[15] = a[15];
+    return out;
+  }
+  /**
+   * Rotates a mat4 by the given angle around the given axis
+   *
+   * @param {mat4} out the receiving matrix
+   * @param {ReadonlyMat4} a the matrix to rotate
+   * @param {Number} rad the angle to rotate the matrix by
+   * @param {ReadonlyVec3} axis the axis to rotate around
+   * @returns {mat4} out
+   */
+
+  function rotate$3(out, a, rad, axis) {
+    var x = axis[0],
+        y = axis[1],
+        z = axis[2];
+    var len = Math.hypot(x, y, z);
+    var s, c, t;
+    var a00, a01, a02, a03;
+    var a10, a11, a12, a13;
+    var a20, a21, a22, a23;
+    var b00, b01, b02;
+    var b10, b11, b12;
+    var b20, b21, b22;
+
+    if (len < EPSILON) {
+      return null;
+    }
+
+    len = 1 / len;
+    x *= len;
+    y *= len;
+    z *= len;
+    s = Math.sin(rad);
+    c = Math.cos(rad);
+    t = 1 - c;
+    a00 = a[0];
+    a01 = a[1];
+    a02 = a[2];
+    a03 = a[3];
+    a10 = a[4];
+    a11 = a[5];
+    a12 = a[6];
+    a13 = a[7];
+    a20 = a[8];
+    a21 = a[9];
+    a22 = a[10];
+    a23 = a[11]; // Construct the elements of the rotation matrix
+
+    b00 = x * x * t + c;
+    b01 = y * x * t + z * s;
+    b02 = z * x * t - y * s;
+    b10 = x * y * t - z * s;
+    b11 = y * y * t + c;
+    b12 = z * y * t + x * s;
+    b20 = x * z * t + y * s;
+    b21 = y * z * t - x * s;
+    b22 = z * z * t + c; // Perform rotation-specific matrix multiplication
+
+    out[0] = a00 * b00 + a10 * b01 + a20 * b02;
+    out[1] = a01 * b00 + a11 * b01 + a21 * b02;
+    out[2] = a02 * b00 + a12 * b01 + a22 * b02;
+    out[3] = a03 * b00 + a13 * b01 + a23 * b02;
+    out[4] = a00 * b10 + a10 * b11 + a20 * b12;
+    out[5] = a01 * b10 + a11 * b11 + a21 * b12;
+    out[6] = a02 * b10 + a12 * b11 + a22 * b12;
+    out[7] = a03 * b10 + a13 * b11 + a23 * b12;
+    out[8] = a00 * b20 + a10 * b21 + a20 * b22;
+    out[9] = a01 * b20 + a11 * b21 + a21 * b22;
+    out[10] = a02 * b20 + a12 * b21 + a22 * b22;
+    out[11] = a03 * b20 + a13 * b21 + a23 * b22;
+
+    if (a !== out) {
+      // If the source and destination differ, copy the unchanged last row
+      out[12] = a[12];
+      out[13] = a[13];
+      out[14] = a[14];
+      out[15] = a[15];
+    }
+
+    return out;
+  }
+  /**
+   * Rotates a matrix by the given angle around the X axis
+   *
+   * @param {mat4} out the receiving matrix
+   * @param {ReadonlyMat4} a the matrix to rotate
+   * @param {Number} rad the angle to rotate the matrix by
+   * @returns {mat4} out
+   */
+
+  function rotateX(out, a, rad) {
+    var s = Math.sin(rad);
+    var c = Math.cos(rad);
+    var a10 = a[4];
+    var a11 = a[5];
+    var a12 = a[6];
+    var a13 = a[7];
+    var a20 = a[8];
+    var a21 = a[9];
+    var a22 = a[10];
+    var a23 = a[11];
+
+    if (a !== out) {
+      // If the source and destination differ, copy the unchanged rows
+      out[0] = a[0];
+      out[1] = a[1];
+      out[2] = a[2];
+      out[3] = a[3];
+      out[12] = a[12];
+      out[13] = a[13];
+      out[14] = a[14];
+      out[15] = a[15];
+    } // Perform axis-specific matrix multiplication
+
+
+    out[4] = a10 * c + a20 * s;
+    out[5] = a11 * c + a21 * s;
+    out[6] = a12 * c + a22 * s;
+    out[7] = a13 * c + a23 * s;
+    out[8] = a20 * c - a10 * s;
+    out[9] = a21 * c - a11 * s;
+    out[10] = a22 * c - a12 * s;
+    out[11] = a23 * c - a13 * s;
+    return out;
+  }
+  /**
+   * Rotates a matrix by the given angle around the Y axis
+   *
+   * @param {mat4} out the receiving matrix
+   * @param {ReadonlyMat4} a the matrix to rotate
+   * @param {Number} rad the angle to rotate the matrix by
+   * @returns {mat4} out
+   */
+
+  function rotateY(out, a, rad) {
+    var s = Math.sin(rad);
+    var c = Math.cos(rad);
+    var a00 = a[0];
+    var a01 = a[1];
+    var a02 = a[2];
+    var a03 = a[3];
+    var a20 = a[8];
+    var a21 = a[9];
+    var a22 = a[10];
+    var a23 = a[11];
+
+    if (a !== out) {
+      // If the source and destination differ, copy the unchanged rows
+      out[4] = a[4];
+      out[5] = a[5];
+      out[6] = a[6];
+      out[7] = a[7];
+      out[12] = a[12];
+      out[13] = a[13];
+      out[14] = a[14];
+      out[15] = a[15];
+    } // Perform axis-specific matrix multiplication
+
+
+    out[0] = a00 * c - a20 * s;
+    out[1] = a01 * c - a21 * s;
+    out[2] = a02 * c - a22 * s;
+    out[3] = a03 * c - a23 * s;
+    out[8] = a00 * s + a20 * c;
+    out[9] = a01 * s + a21 * c;
+    out[10] = a02 * s + a22 * c;
+    out[11] = a03 * s + a23 * c;
+    return out;
+  }
+  /**
+   * Rotates a matrix by the given angle around the Z axis
+   *
+   * @param {mat4} out the receiving matrix
+   * @param {ReadonlyMat4} a the matrix to rotate
+   * @param {Number} rad the angle to rotate the matrix by
+   * @returns {mat4} out
+   */
+
+  function rotateZ(out, a, rad) {
+    var s = Math.sin(rad);
+    var c = Math.cos(rad);
+    var a00 = a[0];
+    var a01 = a[1];
+    var a02 = a[2];
+    var a03 = a[3];
+    var a10 = a[4];
+    var a11 = a[5];
+    var a12 = a[6];
+    var a13 = a[7];
+
+    if (a !== out) {
+      // If the source and destination differ, copy the unchanged last row
+      out[8] = a[8];
+      out[9] = a[9];
+      out[10] = a[10];
+      out[11] = a[11];
+      out[12] = a[12];
+      out[13] = a[13];
+      out[14] = a[14];
+      out[15] = a[15];
+    } // Perform axis-specific matrix multiplication
+
+
+    out[0] = a00 * c + a10 * s;
+    out[1] = a01 * c + a11 * s;
+    out[2] = a02 * c + a12 * s;
+    out[3] = a03 * c + a13 * s;
+    out[4] = a10 * c - a00 * s;
+    out[5] = a11 * c - a01 * s;
+    out[6] = a12 * c - a02 * s;
+    out[7] = a13 * c - a03 * s;
+    return out;
+  }
+  /**
+   * Creates a matrix from a vector translation
+   * This is equivalent to (but much faster than):
+   *
+   *     mat4.identity(dest);
+   *     mat4.translate(dest, dest, vec);
+   *
+   * @param {mat4} out mat4 receiving operation result
+   * @param {ReadonlyVec3} v Translation vector
+   * @returns {mat4} out
+   */
+
+  function fromTranslation$2(out, v) {
+    out[0] = 1;
+    out[1] = 0;
+    out[2] = 0;
+    out[3] = 0;
+    out[4] = 0;
+    out[5] = 1;
+    out[6] = 0;
+    out[7] = 0;
+    out[8] = 0;
+    out[9] = 0;
+    out[10] = 1;
+    out[11] = 0;
+    out[12] = v[0];
+    out[13] = v[1];
+    out[14] = v[2];
+    out[15] = 1;
+    return out;
+  }
+  /**
+   * Creates a matrix from a vector scaling
+   * This is equivalent to (but much faster than):
+   *
+   *     mat4.identity(dest);
+   *     mat4.scale(dest, dest, vec);
+   *
+   * @param {mat4} out mat4 receiving operation result
+   * @param {ReadonlyVec3} v Scaling vector
+   * @returns {mat4} out
+   */
+
+  function fromScaling$3(out, v) {
+    out[0] = v[0];
+    out[1] = 0;
+    out[2] = 0;
+    out[3] = 0;
+    out[4] = 0;
+    out[5] = v[1];
+    out[6] = 0;
+    out[7] = 0;
+    out[8] = 0;
+    out[9] = 0;
+    out[10] = v[2];
+    out[11] = 0;
+    out[12] = 0;
+    out[13] = 0;
+    out[14] = 0;
+    out[15] = 1;
+    return out;
+  }
+  /**
+   * Creates a matrix from a given angle around a given axis
+   * This is equivalent to (but much faster than):
+   *
+   *     mat4.identity(dest);
+   *     mat4.rotate(dest, dest, rad, axis);
+   *
+   * @param {mat4} out mat4 receiving operation result
+   * @param {Number} rad the angle to rotate the matrix by
+   * @param {ReadonlyVec3} axis the axis to rotate around
+   * @returns {mat4} out
+   */
+
+  function fromRotation$3(out, rad, axis) {
+    var x = axis[0],
+        y = axis[1],
+        z = axis[2];
+    var len = Math.hypot(x, y, z);
+    var s, c, t;
+
+    if (len < EPSILON) {
+      return null;
+    }
+
+    len = 1 / len;
+    x *= len;
+    y *= len;
+    z *= len;
+    s = Math.sin(rad);
+    c = Math.cos(rad);
+    t = 1 - c; // Perform rotation-specific matrix multiplication
+
+    out[0] = x * x * t + c;
+    out[1] = y * x * t + z * s;
+    out[2] = z * x * t - y * s;
+    out[3] = 0;
+    out[4] = x * y * t - z * s;
+    out[5] = y * y * t + c;
+    out[6] = z * y * t + x * s;
+    out[7] = 0;
+    out[8] = x * z * t + y * s;
+    out[9] = y * z * t - x * s;
+    out[10] = z * z * t + c;
+    out[11] = 0;
+    out[12] = 0;
+    out[13] = 0;
+    out[14] = 0;
+    out[15] = 1;
+    return out;
+  }
+  /**
+   * Creates a matrix from the given angle around the X axis
+   * This is equivalent to (but much faster than):
+   *
+   *     mat4.identity(dest);
+   *     mat4.rotateX(dest, dest, rad);
+   *
+   * @param {mat4} out mat4 receiving operation result
+   * @param {Number} rad the angle to rotate the matrix by
+   * @returns {mat4} out
+   */
+
+  function fromXRotation(out, rad) {
+    var s = Math.sin(rad);
+    var c = Math.cos(rad); // Perform axis-specific matrix multiplication
+
+    out[0] = 1;
+    out[1] = 0;
+    out[2] = 0;
+    out[3] = 0;
+    out[4] = 0;
+    out[5] = c;
+    out[6] = s;
+    out[7] = 0;
+    out[8] = 0;
+    out[9] = -s;
+    out[10] = c;
+    out[11] = 0;
+    out[12] = 0;
+    out[13] = 0;
+    out[14] = 0;
+    out[15] = 1;
+    return out;
+  }
+  /**
+   * Creates a matrix from the given angle around the Y axis
+   * This is equivalent to (but much faster than):
+   *
+   *     mat4.identity(dest);
+   *     mat4.rotateY(dest, dest, rad);
+   *
+   * @param {mat4} out mat4 receiving operation result
+   * @param {Number} rad the angle to rotate the matrix by
+   * @returns {mat4} out
+   */
+
+  function fromYRotation(out, rad) {
+    var s = Math.sin(rad);
+    var c = Math.cos(rad); // Perform axis-specific matrix multiplication
+
+    out[0] = c;
+    out[1] = 0;
+    out[2] = -s;
+    out[3] = 0;
+    out[4] = 0;
+    out[5] = 1;
+    out[6] = 0;
+    out[7] = 0;
+    out[8] = s;
+    out[9] = 0;
+    out[10] = c;
+    out[11] = 0;
+    out[12] = 0;
+    out[13] = 0;
+    out[14] = 0;
+    out[15] = 1;
+    return out;
+  }
+  /**
+   * Creates a matrix from the given angle around the Z axis
+   * This is equivalent to (but much faster than):
+   *
+   *     mat4.identity(dest);
+   *     mat4.rotateZ(dest, dest, rad);
+   *
+   * @param {mat4} out mat4 receiving operation result
+   * @param {Number} rad the angle to rotate the matrix by
+   * @returns {mat4} out
+   */
+
+  function fromZRotation(out, rad) {
+    var s = Math.sin(rad);
+    var c = Math.cos(rad); // Perform axis-specific matrix multiplication
+
+    out[0] = c;
+    out[1] = s;
+    out[2] = 0;
+    out[3] = 0;
+    out[4] = -s;
+    out[5] = c;
+    out[6] = 0;
+    out[7] = 0;
+    out[8] = 0;
+    out[9] = 0;
+    out[10] = 1;
+    out[11] = 0;
+    out[12] = 0;
+    out[13] = 0;
+    out[14] = 0;
+    out[15] = 1;
+    return out;
+  }
+  /**
+   * Creates a matrix from a quaternion rotation and vector translation
+   * This is equivalent to (but much faster than):
+   *
+   *     mat4.identity(dest);
+   *     mat4.translate(dest, vec);
+   *     let quatMat = mat4.create();
+   *     quat4.toMat4(quat, quatMat);
+   *     mat4.multiply(dest, quatMat);
+   *
+   * @param {mat4} out mat4 receiving operation result
+   * @param {quat4} q Rotation quaternion
+   * @param {ReadonlyVec3} v Translation vector
+   * @returns {mat4} out
+   */
+
+  function fromRotationTranslation(out, q, v) {
+    // Quaternion math
+    var x = q[0],
+        y = q[1],
+        z = q[2],
+        w = q[3];
+    var x2 = x + x;
+    var y2 = y + y;
+    var z2 = z + z;
+    var xx = x * x2;
+    var xy = x * y2;
+    var xz = x * z2;
+    var yy = y * y2;
+    var yz = y * z2;
+    var zz = z * z2;
+    var wx = w * x2;
+    var wy = w * y2;
+    var wz = w * z2;
+    out[0] = 1 - (yy + zz);
+    out[1] = xy + wz;
+    out[2] = xz - wy;
+    out[3] = 0;
+    out[4] = xy - wz;
+    out[5] = 1 - (xx + zz);
+    out[6] = yz + wx;
+    out[7] = 0;
+    out[8] = xz + wy;
+    out[9] = yz - wx;
+    out[10] = 1 - (xx + yy);
+    out[11] = 0;
+    out[12] = v[0];
+    out[13] = v[1];
+    out[14] = v[2];
+    out[15] = 1;
+    return out;
+  }
+  /**
+   * Creates a new mat4 from a dual quat.
+   *
+   * @param {mat4} out Matrix
+   * @param {ReadonlyQuat2} a Dual Quaternion
+   * @returns {mat4} mat4 receiving operation result
+   */
+
+  function fromQuat2(out, a) {
+    var translation = new ARRAY_TYPE(3);
+    var bx = -a[0],
+        by = -a[1],
+        bz = -a[2],
+        bw = a[3],
+        ax = a[4],
+        ay = a[5],
+        az = a[6],
+        aw = a[7];
+    var magnitude = bx * bx + by * by + bz * bz + bw * bw; //Only scale if it makes sense
+
+    if (magnitude > 0) {
+      translation[0] = (ax * bw + aw * bx + ay * bz - az * by) * 2 / magnitude;
+      translation[1] = (ay * bw + aw * by + az * bx - ax * bz) * 2 / magnitude;
+      translation[2] = (az * bw + aw * bz + ax * by - ay * bx) * 2 / magnitude;
+    } else {
+      translation[0] = (ax * bw + aw * bx + ay * bz - az * by) * 2;
+      translation[1] = (ay * bw + aw * by + az * bx - ax * bz) * 2;
+      translation[2] = (az * bw + aw * bz + ax * by - ay * bx) * 2;
+    }
+
+    fromRotationTranslation(out, a, translation);
+    return out;
+  }
+  /**
+   * Returns the translation vector component of a transformation
+   *  matrix. If a matrix is built with fromRotationTranslation,
+   *  the returned vector will be the same as the translation vector
+   *  originally supplied.
+   * @param  {vec3} out Vector to receive translation component
+   * @param  {ReadonlyMat4} mat Matrix to be decomposed (input)
+   * @return {vec3} out
+   */
+
+  function getTranslation(out, mat) {
+    out[0] = mat[12];
+    out[1] = mat[13];
+    out[2] = mat[14];
+    return out;
+  }
+  /**
+   * Returns the scaling factor component of a transformation
+   *  matrix. If a matrix is built with fromRotationTranslationScale
+   *  with a normalized Quaternion paramter, the returned vector will be
+   *  the same as the scaling vector
+   *  originally supplied.
+   * @param  {vec3} out Vector to receive scaling factor component
+   * @param  {ReadonlyMat4} mat Matrix to be decomposed (input)
+   * @return {vec3} out
+   */
+
+  function getScaling(out, mat) {
+    var m11 = mat[0];
+    var m12 = mat[1];
+    var m13 = mat[2];
+    var m21 = mat[4];
+    var m22 = mat[5];
+    var m23 = mat[6];
+    var m31 = mat[8];
+    var m32 = mat[9];
+    var m33 = mat[10];
+    out[0] = Math.hypot(m11, m12, m13);
+    out[1] = Math.hypot(m21, m22, m23);
+    out[2] = Math.hypot(m31, m32, m33);
+    return out;
+  }
+  /**
+   * Returns a quaternion representing the rotational component
+   *  of a transformation matrix. If a matrix is built with
+   *  fromRotationTranslation, the returned quaternion will be the
+   *  same as the quaternion originally supplied.
+   * @param {quat} out Quaternion to receive the rotation component
+   * @param {ReadonlyMat4} mat Matrix to be decomposed (input)
+   * @return {quat} out
+   */
+
+  function getRotation(out, mat) {
+    var scaling = new ARRAY_TYPE(3);
+    getScaling(scaling, mat);
+    var is1 = 1 / scaling[0];
+    var is2 = 1 / scaling[1];
+    var is3 = 1 / scaling[2];
+    var sm11 = mat[0] * is1;
+    var sm12 = mat[1] * is2;
+    var sm13 = mat[2] * is3;
+    var sm21 = mat[4] * is1;
+    var sm22 = mat[5] * is2;
+    var sm23 = mat[6] * is3;
+    var sm31 = mat[8] * is1;
+    var sm32 = mat[9] * is2;
+    var sm33 = mat[10] * is3;
+    var trace = sm11 + sm22 + sm33;
+    var S = 0;
+
+    if (trace > 0) {
+      S = Math.sqrt(trace + 1.0) * 2;
+      out[3] = 0.25 * S;
+      out[0] = (sm23 - sm32) / S;
+      out[1] = (sm31 - sm13) / S;
+      out[2] = (sm12 - sm21) / S;
+    } else if (sm11 > sm22 && sm11 > sm33) {
+      S = Math.sqrt(1.0 + sm11 - sm22 - sm33) * 2;
+      out[3] = (sm23 - sm32) / S;
+      out[0] = 0.25 * S;
+      out[1] = (sm12 + sm21) / S;
+      out[2] = (sm31 + sm13) / S;
+    } else if (sm22 > sm33) {
+      S = Math.sqrt(1.0 + sm22 - sm11 - sm33) * 2;
+      out[3] = (sm31 - sm13) / S;
+      out[0] = (sm12 + sm21) / S;
+      out[1] = 0.25 * S;
+      out[2] = (sm23 + sm32) / S;
+    } else {
+      S = Math.sqrt(1.0 + sm33 - sm11 - sm22) * 2;
+      out[3] = (sm12 - sm21) / S;
+      out[0] = (sm31 + sm13) / S;
+      out[1] = (sm23 + sm32) / S;
+      out[2] = 0.25 * S;
+    }
+
+    return out;
+  }
+  /**
+   * Creates a matrix from a quaternion rotation, vector translation and vector scale
+   * This is equivalent to (but much faster than):
+   *
+   *     mat4.identity(dest);
+   *     mat4.translate(dest, vec);
+   *     let quatMat = mat4.create();
+   *     quat4.toMat4(quat, quatMat);
+   *     mat4.multiply(dest, quatMat);
+   *     mat4.scale(dest, scale)
+   *
+   * @param {mat4} out mat4 receiving operation result
+   * @param {quat4} q Rotation quaternion
+   * @param {ReadonlyVec3} v Translation vector
+   * @param {ReadonlyVec3} s Scaling vector
+   * @returns {mat4} out
+   */
+
+  function fromRotationTranslationScale(out, q, v, s) {
+    // Quaternion math
+    var x = q[0],
+        y = q[1],
+        z = q[2],
+        w = q[3];
+    var x2 = x + x;
+    var y2 = y + y;
+    var z2 = z + z;
+    var xx = x * x2;
+    var xy = x * y2;
+    var xz = x * z2;
+    var yy = y * y2;
+    var yz = y * z2;
+    var zz = z * z2;
+    var wx = w * x2;
+    var wy = w * y2;
+    var wz = w * z2;
+    var sx = s[0];
+    var sy = s[1];
+    var sz = s[2];
+    out[0] = (1 - (yy + zz)) * sx;
+    out[1] = (xy + wz) * sx;
+    out[2] = (xz - wy) * sx;
+    out[3] = 0;
+    out[4] = (xy - wz) * sy;
+    out[5] = (1 - (xx + zz)) * sy;
+    out[6] = (yz + wx) * sy;
+    out[7] = 0;
+    out[8] = (xz + wy) * sz;
+    out[9] = (yz - wx) * sz;
+    out[10] = (1 - (xx + yy)) * sz;
+    out[11] = 0;
+    out[12] = v[0];
+    out[13] = v[1];
+    out[14] = v[2];
+    out[15] = 1;
+    return out;
+  }
+  /**
+   * Creates a matrix from a quaternion rotation, vector translation and vector scale, rotating and scaling around the given origin
+   * This is equivalent to (but much faster than):
+   *
+   *     mat4.identity(dest);
+   *     mat4.translate(dest, vec);
+   *     mat4.translate(dest, origin);
+   *     let quatMat = mat4.create();
+   *     quat4.toMat4(quat, quatMat);
+   *     mat4.multiply(dest, quatMat);
+   *     mat4.scale(dest, scale)
+   *     mat4.translate(dest, negativeOrigin);
+   *
+   * @param {mat4} out mat4 receiving operation result
+   * @param {quat4} q Rotation quaternion
+   * @param {ReadonlyVec3} v Translation vector
+   * @param {ReadonlyVec3} s Scaling vector
+   * @param {ReadonlyVec3} o The origin vector around which to scale and rotate
+   * @returns {mat4} out
+   */
+
+  function fromRotationTranslationScaleOrigin(out, q, v, s, o) {
+    // Quaternion math
+    var x = q[0],
+        y = q[1],
+        z = q[2],
+        w = q[3];
+    var x2 = x + x;
+    var y2 = y + y;
+    var z2 = z + z;
+    var xx = x * x2;
+    var xy = x * y2;
+    var xz = x * z2;
+    var yy = y * y2;
+    var yz = y * z2;
+    var zz = z * z2;
+    var wx = w * x2;
+    var wy = w * y2;
+    var wz = w * z2;
+    var sx = s[0];
+    var sy = s[1];
+    var sz = s[2];
+    var ox = o[0];
+    var oy = o[1];
+    var oz = o[2];
+    var out0 = (1 - (yy + zz)) * sx;
+    var out1 = (xy + wz) * sx;
+    var out2 = (xz - wy) * sx;
+    var out4 = (xy - wz) * sy;
+    var out5 = (1 - (xx + zz)) * sy;
+    var out6 = (yz + wx) * sy;
+    var out8 = (xz + wy) * sz;
+    var out9 = (yz - wx) * sz;
+    var out10 = (1 - (xx + yy)) * sz;
+    out[0] = out0;
+    out[1] = out1;
+    out[2] = out2;
+    out[3] = 0;
+    out[4] = out4;
+    out[5] = out5;
+    out[6] = out6;
+    out[7] = 0;
+    out[8] = out8;
+    out[9] = out9;
+    out[10] = out10;
+    out[11] = 0;
+    out[12] = v[0] + ox - (out0 * ox + out4 * oy + out8 * oz);
+    out[13] = v[1] + oy - (out1 * ox + out5 * oy + out9 * oz);
+    out[14] = v[2] + oz - (out2 * ox + out6 * oy + out10 * oz);
+    out[15] = 1;
+    return out;
+  }
+  /**
+   * Calculates a 4x4 matrix from the given quaternion
+   *
+   * @param {mat4} out mat4 receiving operation result
+   * @param {ReadonlyQuat} q Quaternion to create matrix from
+   *
+   * @returns {mat4} out
+   */
+
+  function fromQuat$1(out, q) {
+    var x = q[0],
+        y = q[1],
+        z = q[2],
+        w = q[3];
+    var x2 = x + x;
+    var y2 = y + y;
+    var z2 = z + z;
+    var xx = x * x2;
+    var yx = y * x2;
+    var yy = y * y2;
+    var zx = z * x2;
+    var zy = z * y2;
+    var zz = z * z2;
+    var wx = w * x2;
+    var wy = w * y2;
+    var wz = w * z2;
+    out[0] = 1 - yy - zz;
+    out[1] = yx + wz;
+    out[2] = zx - wy;
+    out[3] = 0;
+    out[4] = yx - wz;
+    out[5] = 1 - xx - zz;
+    out[6] = zy + wx;
+    out[7] = 0;
+    out[8] = zx + wy;
+    out[9] = zy - wx;
+    out[10] = 1 - xx - yy;
+    out[11] = 0;
+    out[12] = 0;
+    out[13] = 0;
+    out[14] = 0;
+    out[15] = 1;
+    return out;
+  }
+  /**
+   * Generates a frustum matrix with the given bounds
+   *
+   * @param {mat4} out mat4 frustum matrix will be written into
+   * @param {Number} left Left bound of the frustum
+   * @param {Number} right Right bound of the frustum
+   * @param {Number} bottom Bottom bound of the frustum
+   * @param {Number} top Top bound of the frustum
+   * @param {Number} near Near bound of the frustum
+   * @param {Number} far Far bound of the frustum
+   * @returns {mat4} out
+   */
+
+  function frustum(out, left, right, bottom, top, near, far) {
+    var rl = 1 / (right - left);
+    var tb = 1 / (top - bottom);
+    var nf = 1 / (near - far);
+    out[0] = near * 2 * rl;
+    out[1] = 0;
+    out[2] = 0;
+    out[3] = 0;
+    out[4] = 0;
+    out[5] = near * 2 * tb;
+    out[6] = 0;
+    out[7] = 0;
+    out[8] = (right + left) * rl;
+    out[9] = (top + bottom) * tb;
+    out[10] = (far + near) * nf;
+    out[11] = -1;
+    out[12] = 0;
+    out[13] = 0;
+    out[14] = far * near * 2 * nf;
+    out[15] = 0;
+    return out;
+  }
+  /**
+   * Generates a perspective projection matrix with the given bounds.
+   * Passing null/undefined/no value for far will generate infinite projection matrix.
+   *
+   * @param {mat4} out mat4 frustum matrix will be written into
+   * @param {number} fovy Vertical field of view in radians
+   * @param {number} aspect Aspect ratio. typically viewport width/height
+   * @param {number} near Near bound of the frustum
+   * @param {number} far Far bound of the frustum, can be null or Infinity
+   * @returns {mat4} out
+   */
+
+  function perspective(out, fovy, aspect, near, far) {
+    var f = 1.0 / Math.tan(fovy / 2),
+        nf;
+    out[0] = f / aspect;
+    out[1] = 0;
+    out[2] = 0;
+    out[3] = 0;
+    out[4] = 0;
+    out[5] = f;
+    out[6] = 0;
+    out[7] = 0;
+    out[8] = 0;
+    out[9] = 0;
+    out[11] = -1;
+    out[12] = 0;
+    out[13] = 0;
+    out[15] = 0;
+
+    if (far != null && far !== Infinity) {
+      nf = 1 / (near - far);
+      out[10] = (far + near) * nf;
+      out[14] = 2 * far * near * nf;
+    } else {
+      out[10] = -1;
+      out[14] = -2 * near;
+    }
+
+    return out;
+  }
+  /**
+   * Generates a perspective projection matrix with the given field of view.
+   * This is primarily useful for generating projection matrices to be used
+   * with the still experiemental WebVR API.
+   *
+   * @param {mat4} out mat4 frustum matrix will be written into
+   * @param {Object} fov Object containing the following values: upDegrees, downDegrees, leftDegrees, rightDegrees
+   * @param {number} near Near bound of the frustum
+   * @param {number} far Far bound of the frustum
+   * @returns {mat4} out
+   */
+
+  function perspectiveFromFieldOfView(out, fov, near, far) {
+    var upTan = Math.tan(fov.upDegrees * Math.PI / 180.0);
+    var downTan = Math.tan(fov.downDegrees * Math.PI / 180.0);
+    var leftTan = Math.tan(fov.leftDegrees * Math.PI / 180.0);
+    var rightTan = Math.tan(fov.rightDegrees * Math.PI / 180.0);
+    var xScale = 2.0 / (leftTan + rightTan);
+    var yScale = 2.0 / (upTan + downTan);
+    out[0] = xScale;
+    out[1] = 0.0;
+    out[2] = 0.0;
+    out[3] = 0.0;
+    out[4] = 0.0;
+    out[5] = yScale;
+    out[6] = 0.0;
+    out[7] = 0.0;
+    out[8] = -((leftTan - rightTan) * xScale * 0.5);
+    out[9] = (upTan - downTan) * yScale * 0.5;
+    out[10] = far / (near - far);
+    out[11] = -1.0;
+    out[12] = 0.0;
+    out[13] = 0.0;
+    out[14] = far * near / (near - far);
+    out[15] = 0.0;
+    return out;
+  }
+  /**
+   * Generates a orthogonal projection matrix with the given bounds
+   *
+   * @param {mat4} out mat4 frustum matrix will be written into
+   * @param {number} left Left bound of the frustum
+   * @param {number} right Right bound of the frustum
+   * @param {number} bottom Bottom bound of the frustum
+   * @param {number} top Top bound of the frustum
+   * @param {number} near Near bound of the frustum
+   * @param {number} far Far bound of the frustum
+   * @returns {mat4} out
+   */
+
+  function ortho(out, left, right, bottom, top, near, far) {
+    var lr = 1 / (left - right);
+    var bt = 1 / (bottom - top);
+    var nf = 1 / (near - far);
+    out[0] = -2 * lr;
+    out[1] = 0;
+    out[2] = 0;
+    out[3] = 0;
+    out[4] = 0;
+    out[5] = -2 * bt;
+    out[6] = 0;
+    out[7] = 0;
+    out[8] = 0;
+    out[9] = 0;
+    out[10] = 2 * nf;
+    out[11] = 0;
+    out[12] = (left + right) * lr;
+    out[13] = (top + bottom) * bt;
+    out[14] = (far + near) * nf;
+    out[15] = 1;
+    return out;
+  }
+  /**
+   * Generates a look-at matrix with the given eye position, focal point, and up axis.
+   * If you want a matrix that actually makes an object look at another object, you should use targetTo instead.
+   *
+   * @param {mat4} out mat4 frustum matrix will be written into
+   * @param {ReadonlyVec3} eye Position of the viewer
+   * @param {ReadonlyVec3} center Point the viewer is looking at
+   * @param {ReadonlyVec3} up vec3 pointing up
+   * @returns {mat4} out
+   */
+
+  function lookAt(out, eye, center, up) {
+    var x0, x1, x2, y0, y1, y2, z0, z1, z2, len;
+    var eyex = eye[0];
+    var eyey = eye[1];
+    var eyez = eye[2];
+    var upx = up[0];
+    var upy = up[1];
+    var upz = up[2];
+    var centerx = center[0];
+    var centery = center[1];
+    var centerz = center[2];
+
+    if (Math.abs(eyex - centerx) < EPSILON && Math.abs(eyey - centery) < EPSILON && Math.abs(eyez - centerz) < EPSILON) {
+      return identity$3(out);
+    }
+
+    z0 = eyex - centerx;
+    z1 = eyey - centery;
+    z2 = eyez - centerz;
+    len = 1 / Math.hypot(z0, z1, z2);
+    z0 *= len;
+    z1 *= len;
+    z2 *= len;
+    x0 = upy * z2 - upz * z1;
+    x1 = upz * z0 - upx * z2;
+    x2 = upx * z1 - upy * z0;
+    len = Math.hypot(x0, x1, x2);
+
+    if (!len) {
+      x0 = 0;
+      x1 = 0;
+      x2 = 0;
+    } else {
+      len = 1 / len;
+      x0 *= len;
+      x1 *= len;
+      x2 *= len;
+    }
+
+    y0 = z1 * x2 - z2 * x1;
+    y1 = z2 * x0 - z0 * x2;
+    y2 = z0 * x1 - z1 * x0;
+    len = Math.hypot(y0, y1, y2);
+
+    if (!len) {
+      y0 = 0;
+      y1 = 0;
+      y2 = 0;
+    } else {
+      len = 1 / len;
+      y0 *= len;
+      y1 *= len;
+      y2 *= len;
+    }
+
+    out[0] = x0;
+    out[1] = y0;
+    out[2] = z0;
+    out[3] = 0;
+    out[4] = x1;
+    out[5] = y1;
+    out[6] = z1;
+    out[7] = 0;
+    out[8] = x2;
+    out[9] = y2;
+    out[10] = z2;
+    out[11] = 0;
+    out[12] = -(x0 * eyex + x1 * eyey + x2 * eyez);
+    out[13] = -(y0 * eyex + y1 * eyey + y2 * eyez);
+    out[14] = -(z0 * eyex + z1 * eyey + z2 * eyez);
+    out[15] = 1;
+    return out;
+  }
+  /**
+   * Generates a matrix that makes something look at something else.
+   *
+   * @param {mat4} out mat4 frustum matrix will be written into
+   * @param {ReadonlyVec3} eye Position of the viewer
+   * @param {ReadonlyVec3} center Point the viewer is looking at
+   * @param {ReadonlyVec3} up vec3 pointing up
+   * @returns {mat4} out
+   */
+
+  function targetTo(out, eye, target, up) {
+    var eyex = eye[0],
+        eyey = eye[1],
+        eyez = eye[2],
+        upx = up[0],
+        upy = up[1],
+        upz = up[2];
+    var z0 = eyex - target[0],
+        z1 = eyey - target[1],
+        z2 = eyez - target[2];
+    var len = z0 * z0 + z1 * z1 + z2 * z2;
+
+    if (len > 0) {
+      len = 1 / Math.sqrt(len);
+      z0 *= len;
+      z1 *= len;
+      z2 *= len;
+    }
+
+    var x0 = upy * z2 - upz * z1,
+        x1 = upz * z0 - upx * z2,
+        x2 = upx * z1 - upy * z0;
+    len = x0 * x0 + x1 * x1 + x2 * x2;
+
+    if (len > 0) {
+      len = 1 / Math.sqrt(len);
+      x0 *= len;
+      x1 *= len;
+      x2 *= len;
+    }
+
+    out[0] = x0;
+    out[1] = x1;
+    out[2] = x2;
+    out[3] = 0;
+    out[4] = z1 * x2 - z2 * x1;
+    out[5] = z2 * x0 - z0 * x2;
+    out[6] = z0 * x1 - z1 * x0;
+    out[7] = 0;
+    out[8] = z0;
+    out[9] = z1;
+    out[10] = z2;
+    out[11] = 0;
+    out[12] = eyex;
+    out[13] = eyey;
+    out[14] = eyez;
+    out[15] = 1;
+    return out;
+  }
+  /**
+   * Returns a string representation of a mat4
+   *
+   * @param {ReadonlyMat4} a matrix to represent as a string
+   * @returns {String} string representation of the matrix
+   */
+
+  function str$3(a) {
+    return "mat4(" + a[0] + ", " + a[1] + ", " + a[2] + ", " + a[3] + ", " + a[4] + ", " + a[5] + ", " + a[6] + ", " + a[7] + ", " + a[8] + ", " + a[9] + ", " + a[10] + ", " + a[11] + ", " + a[12] + ", " + a[13] + ", " + a[14] + ", " + a[15] + ")";
+  }
+  /**
+   * Returns Frobenius norm of a mat4
+   *
+   * @param {ReadonlyMat4} a the matrix to calculate Frobenius norm of
+   * @returns {Number} Frobenius norm
+   */
+
+  function frob$3(a) {
+    return Math.hypot(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15]);
+  }
+  /**
+   * Adds two mat4's
+   *
+   * @param {mat4} out the receiving matrix
+   * @param {ReadonlyMat4} a the first operand
+   * @param {ReadonlyMat4} b the second operand
+   * @returns {mat4} out
+   */
+
+  function add$3(out, a, b) {
+    out[0] = a[0] + b[0];
+    out[1] = a[1] + b[1];
+    out[2] = a[2] + b[2];
+    out[3] = a[3] + b[3];
+    out[4] = a[4] + b[4];
+    out[5] = a[5] + b[5];
+    out[6] = a[6] + b[6];
+    out[7] = a[7] + b[7];
+    out[8] = a[8] + b[8];
+    out[9] = a[9] + b[9];
+    out[10] = a[10] + b[10];
+    out[11] = a[11] + b[11];
+    out[12] = a[12] + b[12];
+    out[13] = a[13] + b[13];
+    out[14] = a[14] + b[14];
+    out[15] = a[15] + b[15];
+    return out;
+  }
+  /**
+   * Subtracts matrix b from matrix a
+   *
+   * @param {mat4} out the receiving matrix
+   * @param {ReadonlyMat4} a the first operand
+   * @param {ReadonlyMat4} b the second operand
+   * @returns {mat4} out
+   */
+
+  function subtract$3(out, a, b) {
+    out[0] = a[0] - b[0];
+    out[1] = a[1] - b[1];
+    out[2] = a[2] - b[2];
+    out[3] = a[3] - b[3];
+    out[4] = a[4] - b[4];
+    out[5] = a[5] - b[5];
+    out[6] = a[6] - b[6];
+    out[7] = a[7] - b[7];
+    out[8] = a[8] - b[8];
+    out[9] = a[9] - b[9];
+    out[10] = a[10] - b[10];
+    out[11] = a[11] - b[11];
+    out[12] = a[12] - b[12];
+    out[13] = a[13] - b[13];
+    out[14] = a[14] - b[14];
+    out[15] = a[15] - b[15];
+    return out;
+  }
+  /**
+   * Multiply each element of the matrix by a scalar.
+   *
+   * @param {mat4} out the receiving matrix
+   * @param {ReadonlyMat4} a the matrix to scale
+   * @param {Number} b amount to scale the matrix's elements by
+   * @returns {mat4} out
+   */
+
+  function multiplyScalar$3(out, a, b) {
+    out[0] = a[0] * b;
+    out[1] = a[1] * b;
+    out[2] = a[2] * b;
+    out[3] = a[3] * b;
+    out[4] = a[4] * b;
+    out[5] = a[5] * b;
+    out[6] = a[6] * b;
+    out[7] = a[7] * b;
+    out[8] = a[8] * b;
+    out[9] = a[9] * b;
+    out[10] = a[10] * b;
+    out[11] = a[11] * b;
+    out[12] = a[12] * b;
+    out[13] = a[13] * b;
+    out[14] = a[14] * b;
+    out[15] = a[15] * b;
+    return out;
+  }
+  /**
+   * Adds two mat4's after multiplying each element of the second operand by a scalar value.
+   *
+   * @param {mat4} out the receiving vector
+   * @param {ReadonlyMat4} a the first operand
+   * @param {ReadonlyMat4} b the second operand
+   * @param {Number} scale the amount to scale b's elements by before adding
+   * @returns {mat4} out
+   */
+
+  function multiplyScalarAndAdd$3(out, a, b, scale) {
+    out[0] = a[0] + b[0] * scale;
+    out[1] = a[1] + b[1] * scale;
+    out[2] = a[2] + b[2] * scale;
+    out[3] = a[3] + b[3] * scale;
+    out[4] = a[4] + b[4] * scale;
+    out[5] = a[5] + b[5] * scale;
+    out[6] = a[6] + b[6] * scale;
+    out[7] = a[7] + b[7] * scale;
+    out[8] = a[8] + b[8] * scale;
+    out[9] = a[9] + b[9] * scale;
+    out[10] = a[10] + b[10] * scale;
+    out[11] = a[11] + b[11] * scale;
+    out[12] = a[12] + b[12] * scale;
+    out[13] = a[13] + b[13] * scale;
+    out[14] = a[14] + b[14] * scale;
+    out[15] = a[15] + b[15] * scale;
+    return out;
+  }
+  /**
+   * Returns whether or not the matrices have exactly the same elements in the same position (when compared with ===)
+   *
+   * @param {ReadonlyMat4} a The first matrix.
+   * @param {ReadonlyMat4} b The second matrix.
+   * @returns {Boolean} True if the matrices are equal, false otherwise.
+   */
+
+  function exactEquals$3(a, b) {
+    return a[0] === b[0] && a[1] === b[1] && a[2] === b[2] && a[3] === b[3] && a[4] === b[4] && a[5] === b[5] && a[6] === b[6] && a[7] === b[7] && a[8] === b[8] && a[9] === b[9] && a[10] === b[10] && a[11] === b[11] && a[12] === b[12] && a[13] === b[13] && a[14] === b[14] && a[15] === b[15];
+  }
+  /**
+   * Returns whether or not the matrices have approximately the same elements in the same position.
+   *
+   * @param {ReadonlyMat4} a The first matrix.
+   * @param {ReadonlyMat4} b The second matrix.
+   * @returns {Boolean} True if the matrices are equal, false otherwise.
+   */
+
+  function equals$4(a, b) {
+    var a0 = a[0],
+        a1 = a[1],
+        a2 = a[2],
+        a3 = a[3];
+    var a4 = a[4],
+        a5 = a[5],
+        a6 = a[6],
+        a7 = a[7];
+    var a8 = a[8],
+        a9 = a[9],
+        a10 = a[10],
+        a11 = a[11];
+    var a12 = a[12],
+        a13 = a[13],
+        a14 = a[14],
+        a15 = a[15];
+    var b0 = b[0],
+        b1 = b[1],
+        b2 = b[2],
+        b3 = b[3];
+    var b4 = b[4],
+        b5 = b[5],
+        b6 = b[6],
+        b7 = b[7];
+    var b8 = b[8],
+        b9 = b[9],
+        b10 = b[10],
+        b11 = b[11];
+    var b12 = b[12],
+        b13 = b[13],
+        b14 = b[14],
+        b15 = b[15];
+    return Math.abs(a0 - b0) <= EPSILON * Math.max(1.0, Math.abs(a0), Math.abs(b0)) && Math.abs(a1 - b1) <= EPSILON * Math.max(1.0, Math.abs(a1), Math.abs(b1)) && Math.abs(a2 - b2) <= EPSILON * Math.max(1.0, Math.abs(a2), Math.abs(b2)) && Math.abs(a3 - b3) <= EPSILON * Math.max(1.0, Math.abs(a3), Math.abs(b3)) && Math.abs(a4 - b4) <= EPSILON * Math.max(1.0, Math.abs(a4), Math.abs(b4)) && Math.abs(a5 - b5) <= EPSILON * Math.max(1.0, Math.abs(a5), Math.abs(b5)) && Math.abs(a6 - b6) <= EPSILON * Math.max(1.0, Math.abs(a6), Math.abs(b6)) && Math.abs(a7 - b7) <= EPSILON * Math.max(1.0, Math.abs(a7), Math.abs(b7)) && Math.abs(a8 - b8) <= EPSILON * Math.max(1.0, Math.abs(a8), Math.abs(b8)) && Math.abs(a9 - b9) <= EPSILON * Math.max(1.0, Math.abs(a9), Math.abs(b9)) && Math.abs(a10 - b10) <= EPSILON * Math.max(1.0, Math.abs(a10), Math.abs(b10)) && Math.abs(a11 - b11) <= EPSILON * Math.max(1.0, Math.abs(a11), Math.abs(b11)) && Math.abs(a12 - b12) <= EPSILON * Math.max(1.0, Math.abs(a12), Math.abs(b12)) && Math.abs(a13 - b13) <= EPSILON * Math.max(1.0, Math.abs(a13), Math.abs(b13)) && Math.abs(a14 - b14) <= EPSILON * Math.max(1.0, Math.abs(a14), Math.abs(b14)) && Math.abs(a15 - b15) <= EPSILON * Math.max(1.0, Math.abs(a15), Math.abs(b15));
+  }
+  /**
+   * Alias for {@link mat4.multiply}
+   * @function
+   */
+
+  var mul$3 = multiply$3;
+  /**
+   * Alias for {@link mat4.subtract}
+   * @function
+   */
+
+  var sub$3 = subtract$3;
+
+  var mat4 = /*#__PURE__*/Object.freeze({
+    __proto__: null,
+    create: create$3,
+    clone: clone$3,
+    copy: copy$3,
+    fromValues: fromValues$3,
+    set: set$3,
+    identity: identity$3,
+    transpose: transpose$2,
+    invert: invert$3,
+    adjoint: adjoint$2,
+    determinant: determinant$3,
+    multiply: multiply$3,
+    translate: translate$2,
+    scale: scale$3,
+    rotate: rotate$3,
+    rotateX: rotateX,
+    rotateY: rotateY,
+    rotateZ: rotateZ,
+    fromTranslation: fromTranslation$2,
+    fromScaling: fromScaling$3,
+    fromRotation: fromRotation$3,
+    fromXRotation: fromXRotation,
+    fromYRotation: fromYRotation,
+    fromZRotation: fromZRotation,
+    fromRotationTranslation: fromRotationTranslation,
+    fromQuat2: fromQuat2,
+    getTranslation: getTranslation,
+    getScaling: getScaling,
+    getRotation: getRotation,
+    fromRotationTranslationScale: fromRotationTranslationScale,
+    fromRotationTranslationScaleOrigin: fromRotationTranslationScaleOrigin,
+    fromQuat: fromQuat$1,
+    frustum: frustum,
+    perspective: perspective,
+    perspectiveFromFieldOfView: perspectiveFromFieldOfView,
+    ortho: ortho,
+    lookAt: lookAt,
+    targetTo: targetTo,
+    str: str$3,
+    frob: frob$3,
+    add: add$3,
+    subtract: subtract$3,
+    multiplyScalar: multiplyScalar$3,
+    multiplyScalarAndAdd: multiplyScalarAndAdd$3,
+    exactEquals: exactEquals$3,
+    equals: equals$4,
+    mul: mul$3,
+    sub: sub$3
+  });
+
+  /**
+   * 3 Dimensional Vector
+   * @module vec3
+   */
+
+  /**
+   * Creates a new, empty vec3
+   *
+   * @returns {vec3} a new 3D vector
+   */
+
+  function create$4() {
+    var out = new ARRAY_TYPE(3);
+
+    if (ARRAY_TYPE != Float32Array) {
+      out[0] = 0;
+      out[1] = 0;
+      out[2] = 0;
+    }
+
+    return out;
+  }
+  /**
+   * Creates a new vec3 initialized with values from an existing vector
+   *
+   * @param {ReadonlyVec3} a vector to clone
+   * @returns {vec3} a new 3D vector
+   */
+
+  function clone$4(a) {
+    var out = new ARRAY_TYPE(3);
+    out[0] = a[0];
+    out[1] = a[1];
+    out[2] = a[2];
+    return out;
+  }
+  /**
+   * Calculates the length of a vec3
+   *
+   * @param {ReadonlyVec3} a vector to calculate length of
+   * @returns {Number} length of a
+   */
+
+  function length(a) {
+    var x = a[0];
+    var y = a[1];
+    var z = a[2];
+    return Math.hypot(x, y, z);
+  }
+  /**
+   * Creates a new vec3 initialized with the given values
+   *
+   * @param {Number} x X component
+   * @param {Number} y Y component
+   * @param {Number} z Z component
+   * @returns {vec3} a new 3D vector
+   */
+
+  function fromValues$4(x, y, z) {
+    var out = new ARRAY_TYPE(3);
+    out[0] = x;
+    out[1] = y;
+    out[2] = z;
+    return out;
+  }
+  /**
+   * Copy the values from one vec3 to another
+   *
+   * @param {vec3} out the receiving vector
+   * @param {ReadonlyVec3} a the source vector
+   * @returns {vec3} out
+   */
+
+  function copy$4(out, a) {
+    out[0] = a[0];
+    out[1] = a[1];
+    out[2] = a[2];
+    return out;
+  }
+  /**
+   * Set the components of a vec3 to the given values
+   *
+   * @param {vec3} out the receiving vector
+   * @param {Number} x X component
+   * @param {Number} y Y component
+   * @param {Number} z Z component
+   * @returns {vec3} out
+   */
+
+  function set$4(out, x, y, z) {
+    out[0] = x;
+    out[1] = y;
+    out[2] = z;
+    return out;
+  }
+  /**
+   * Adds two vec3's
+   *
+   * @param {vec3} out the receiving vector
+   * @param {ReadonlyVec3} a the first operand
+   * @param {ReadonlyVec3} b the second operand
+   * @returns {vec3} out
+   */
+
+  function add$4(out, a, b) {
+    out[0] = a[0] + b[0];
+    out[1] = a[1] + b[1];
+    out[2] = a[2] + b[2];
+    return out;
+  }
+  /**
+   * Subtracts vector b from vector a
+   *
+   * @param {vec3} out the receiving vector
+   * @param {ReadonlyVec3} a the first operand
+   * @param {ReadonlyVec3} b the second operand
+   * @returns {vec3} out
+   */
+
+  function subtract$4(out, a, b) {
+    out[0] = a[0] - b[0];
+    out[1] = a[1] - b[1];
+    out[2] = a[2] - b[2];
+    return out;
+  }
+  /**
+   * Multiplies two vec3's
+   *
+   * @param {vec3} out the receiving vector
+   * @param {ReadonlyVec3} a the first operand
+   * @param {ReadonlyVec3} b the second operand
+   * @returns {vec3} out
+   */
+
+  function multiply$4(out, a, b) {
+    out[0] = a[0] * b[0];
+    out[1] = a[1] * b[1];
+    out[2] = a[2] * b[2];
+    return out;
+  }
+  /**
+   * Divides two vec3's
+   *
+   * @param {vec3} out the receiving vector
+   * @param {ReadonlyVec3} a the first operand
+   * @param {ReadonlyVec3} b the second operand
+   * @returns {vec3} out
+   */
+
+  function divide(out, a, b) {
+    out[0] = a[0] / b[0];
+    out[1] = a[1] / b[1];
+    out[2] = a[2] / b[2];
+    return out;
+  }
+  /**
+   * Math.ceil the components of a vec3
+   *
+   * @param {vec3} out the receiving vector
+   * @param {ReadonlyVec3} a vector to ceil
+   * @returns {vec3} out
+   */
+
+  function ceil(out, a) {
+    out[0] = Math.ceil(a[0]);
+    out[1] = Math.ceil(a[1]);
+    out[2] = Math.ceil(a[2]);
+    return out;
+  }
+  /**
+   * Math.floor the components of a vec3
+   *
+   * @param {vec3} out the receiving vector
+   * @param {ReadonlyVec3} a vector to floor
+   * @returns {vec3} out
+   */
+
+  function floor(out, a) {
+    out[0] = Math.floor(a[0]);
+    out[1] = Math.floor(a[1]);
+    out[2] = Math.floor(a[2]);
+    return out;
+  }
+  /**
+   * Returns the minimum of two vec3's
+   *
+   * @param {vec3} out the receiving vector
+   * @param {ReadonlyVec3} a the first operand
+   * @param {ReadonlyVec3} b the second operand
+   * @returns {vec3} out
+   */
+
+  function min(out, a, b) {
+    out[0] = Math.min(a[0], b[0]);
+    out[1] = Math.min(a[1], b[1]);
+    out[2] = Math.min(a[2], b[2]);
+    return out;
+  }
+  /**
+   * Returns the maximum of two vec3's
+   *
+   * @param {vec3} out the receiving vector
+   * @param {ReadonlyVec3} a the first operand
+   * @param {ReadonlyVec3} b the second operand
+   * @returns {vec3} out
+   */
+
+  function max(out, a, b) {
+    out[0] = Math.max(a[0], b[0]);
+    out[1] = Math.max(a[1], b[1]);
+    out[2] = Math.max(a[2], b[2]);
+    return out;
+  }
+  /**
+   * Math.round the components of a vec3
+   *
+   * @param {vec3} out the receiving vector
+   * @param {ReadonlyVec3} a vector to round
+   * @returns {vec3} out
+   */
+
+  function round(out, a) {
+    out[0] = Math.round(a[0]);
+    out[1] = Math.round(a[1]);
+    out[2] = Math.round(a[2]);
+    return out;
+  }
+  /**
+   * Scales a vec3 by a scalar number
+   *
+   * @param {vec3} out the receiving vector
+   * @param {ReadonlyVec3} a the vector to scale
+   * @param {Number} b amount to scale the vector by
+   * @returns {vec3} out
+   */
+
+  function scale$4(out, a, b) {
+    out[0] = a[0] * b;
+    out[1] = a[1] * b;
+    out[2] = a[2] * b;
+    return out;
+  }
+  /**
+   * Adds two vec3's after scaling the second operand by a scalar value
+   *
+   * @param {vec3} out the receiving vector
+   * @param {ReadonlyVec3} a the first operand
+   * @param {ReadonlyVec3} b the second operand
+   * @param {Number} scale the amount to scale b by before adding
+   * @returns {vec3} out
+   */
+
+  function scaleAndAdd(out, a, b, scale) {
+    out[0] = a[0] + b[0] * scale;
+    out[1] = a[1] + b[1] * scale;
+    out[2] = a[2] + b[2] * scale;
+    return out;
+  }
+  /**
+   * Calculates the euclidian distance between two vec3's
+   *
+   * @param {ReadonlyVec3} a the first operand
+   * @param {ReadonlyVec3} b the second operand
+   * @returns {Number} distance between a and b
+   */
+
+  function distance(a, b) {
+    var x = b[0] - a[0];
+    var y = b[1] - a[1];
+    var z = b[2] - a[2];
+    return Math.hypot(x, y, z);
+  }
+  /**
+   * Calculates the squared euclidian distance between two vec3's
+   *
+   * @param {ReadonlyVec3} a the first operand
+   * @param {ReadonlyVec3} b the second operand
+   * @returns {Number} squared distance between a and b
+   */
+
+  function squaredDistance(a, b) {
+    var x = b[0] - a[0];
+    var y = b[1] - a[1];
+    var z = b[2] - a[2];
+    return x * x + y * y + z * z;
+  }
+  /**
+   * Calculates the squared length of a vec3
+   *
+   * @param {ReadonlyVec3} a vector to calculate squared length of
+   * @returns {Number} squared length of a
+   */
+
+  function squaredLength(a) {
+    var x = a[0];
+    var y = a[1];
+    var z = a[2];
+    return x * x + y * y + z * z;
+  }
+  /**
+   * Negates the components of a vec3
+   *
+   * @param {vec3} out the receiving vector
+   * @param {ReadonlyVec3} a vector to negate
+   * @returns {vec3} out
+   */
+
+  function negate(out, a) {
+    out[0] = -a[0];
+    out[1] = -a[1];
+    out[2] = -a[2];
+    return out;
+  }
+  /**
+   * Returns the inverse of the components of a vec3
+   *
+   * @param {vec3} out the receiving vector
+   * @param {ReadonlyVec3} a vector to invert
+   * @returns {vec3} out
+   */
+
+  function inverse(out, a) {
+    out[0] = 1.0 / a[0];
+    out[1] = 1.0 / a[1];
+    out[2] = 1.0 / a[2];
+    return out;
+  }
+  /**
+   * Normalize a vec3
+   *
+   * @param {vec3} out the receiving vector
+   * @param {ReadonlyVec3} a vector to normalize
+   * @returns {vec3} out
+   */
+
+  function normalize(out, a) {
+    var x = a[0];
+    var y = a[1];
+    var z = a[2];
+    var len = x * x + y * y + z * z;
+
+    if (len > 0) {
+      //TODO: evaluate use of glm_invsqrt here?
+      len = 1 / Math.sqrt(len);
+    }
+
+    out[0] = a[0] * len;
+    out[1] = a[1] * len;
+    out[2] = a[2] * len;
+    return out;
+  }
+  /**
+   * Calculates the dot product of two vec3's
+   *
+   * @param {ReadonlyVec3} a the first operand
+   * @param {ReadonlyVec3} b the second operand
+   * @returns {Number} dot product of a and b
+   */
+
+  function dot(a, b) {
+    return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
+  }
+  /**
+   * Computes the cross product of two vec3's
+   *
+   * @param {vec3} out the receiving vector
+   * @param {ReadonlyVec3} a the first operand
+   * @param {ReadonlyVec3} b the second operand
+   * @returns {vec3} out
+   */
+
+  function cross(out, a, b) {
+    var ax = a[0],
+        ay = a[1],
+        az = a[2];
+    var bx = b[0],
+        by = b[1],
+        bz = b[2];
+    out[0] = ay * bz - az * by;
+    out[1] = az * bx - ax * bz;
+    out[2] = ax * by - ay * bx;
+    return out;
+  }
+  /**
+   * Performs a linear interpolation between two vec3's
+   *
+   * @param {vec3} out the receiving vector
+   * @param {ReadonlyVec3} a the first operand
+   * @param {ReadonlyVec3} b the second operand
+   * @param {Number} t interpolation amount, in the range [0-1], between the two inputs
+   * @returns {vec3} out
+   */
+
+  function lerp(out, a, b, t) {
+    var ax = a[0];
+    var ay = a[1];
+    var az = a[2];
+    out[0] = ax + t * (b[0] - ax);
+    out[1] = ay + t * (b[1] - ay);
+    out[2] = az + t * (b[2] - az);
+    return out;
+  }
+  /**
+   * Performs a hermite interpolation with two control points
+   *
+   * @param {vec3} out the receiving vector
+   * @param {ReadonlyVec3} a the first operand
+   * @param {ReadonlyVec3} b the second operand
+   * @param {ReadonlyVec3} c the third operand
+   * @param {ReadonlyVec3} d the fourth operand
+   * @param {Number} t interpolation amount, in the range [0-1], between the two inputs
+   * @returns {vec3} out
+   */
+
+  function hermite(out, a, b, c, d, t) {
+    var factorTimes2 = t * t;
+    var factor1 = factorTimes2 * (2 * t - 3) + 1;
+    var factor2 = factorTimes2 * (t - 2) + t;
+    var factor3 = factorTimes2 * (t - 1);
+    var factor4 = factorTimes2 * (3 - 2 * t);
+    out[0] = a[0] * factor1 + b[0] * factor2 + c[0] * factor3 + d[0] * factor4;
+    out[1] = a[1] * factor1 + b[1] * factor2 + c[1] * factor3 + d[1] * factor4;
+    out[2] = a[2] * factor1 + b[2] * factor2 + c[2] * factor3 + d[2] * factor4;
+    return out;
+  }
+  /**
+   * Performs a bezier interpolation with two control points
+   *
+   * @param {vec3} out the receiving vector
+   * @param {ReadonlyVec3} a the first operand
+   * @param {ReadonlyVec3} b the second operand
+   * @param {ReadonlyVec3} c the third operand
+   * @param {ReadonlyVec3} d the fourth operand
+   * @param {Number} t interpolation amount, in the range [0-1], between the two inputs
+   * @returns {vec3} out
+   */
+
+  function bezier(out, a, b, c, d, t) {
+    var inverseFactor = 1 - t;
+    var inverseFactorTimesTwo = inverseFactor * inverseFactor;
+    var factorTimes2 = t * t;
+    var factor1 = inverseFactorTimesTwo * inverseFactor;
+    var factor2 = 3 * t * inverseFactorTimesTwo;
+    var factor3 = 3 * factorTimes2 * inverseFactor;
+    var factor4 = factorTimes2 * t;
+    out[0] = a[0] * factor1 + b[0] * factor2 + c[0] * factor3 + d[0] * factor4;
+    out[1] = a[1] * factor1 + b[1] * factor2 + c[1] * factor3 + d[1] * factor4;
+    out[2] = a[2] * factor1 + b[2] * factor2 + c[2] * factor3 + d[2] * factor4;
+    return out;
+  }
+  /**
+   * Generates a random vector with the given scale
+   *
+   * @param {vec3} out the receiving vector
+   * @param {Number} [scale] Length of the resulting vector. If ommitted, a unit vector will be returned
+   * @returns {vec3} out
+   */
+
+  function random(out, scale) {
+    scale = scale || 1.0;
+    var r = RANDOM() * 2.0 * Math.PI;
+    var z = RANDOM() * 2.0 - 1.0;
+    var zScale = Math.sqrt(1.0 - z * z) * scale;
+    out[0] = Math.cos(r) * zScale;
+    out[1] = Math.sin(r) * zScale;
+    out[2] = z * scale;
+    return out;
+  }
+  /**
+   * Transforms the vec3 with a mat4.
+   * 4th vector component is implicitly '1'
+   *
+   * @param {vec3} out the receiving vector
+   * @param {ReadonlyVec3} a the vector to transform
+   * @param {ReadonlyMat4} m matrix to transform with
+   * @returns {vec3} out
+   */
+
+  function transformMat4(out, a, m) {
+    var x = a[0],
+        y = a[1],
+        z = a[2];
+    var w = m[3] * x + m[7] * y + m[11] * z + m[15];
+    w = w || 1.0;
+    out[0] = (m[0] * x + m[4] * y + m[8] * z + m[12]) / w;
+    out[1] = (m[1] * x + m[5] * y + m[9] * z + m[13]) / w;
+    out[2] = (m[2] * x + m[6] * y + m[10] * z + m[14]) / w;
+    return out;
+  }
+  /**
+   * Transforms the vec3 with a mat3.
+   *
+   * @param {vec3} out the receiving vector
+   * @param {ReadonlyVec3} a the vector to transform
+   * @param {ReadonlyMat3} m the 3x3 matrix to transform with
+   * @returns {vec3} out
+   */
+
+  function transformMat3(out, a, m) {
+    var x = a[0],
+        y = a[1],
+        z = a[2];
+    out[0] = x * m[0] + y * m[3] + z * m[6];
+    out[1] = x * m[1] + y * m[4] + z * m[7];
+    out[2] = x * m[2] + y * m[5] + z * m[8];
+    return out;
+  }
+  /**
+   * Transforms the vec3 with a quat
+   * Can also be used for dual quaternions. (Multiply it with the real part)
+   *
+   * @param {vec3} out the receiving vector
+   * @param {ReadonlyVec3} a the vector to transform
+   * @param {ReadonlyQuat} q quaternion to transform with
+   * @returns {vec3} out
+   */
+
+  function transformQuat(out, a, q) {
+    // benchmarks: https://jsperf.com/quaternion-transform-vec3-implementations-fixed
+    var qx = q[0],
+        qy = q[1],
+        qz = q[2],
+        qw = q[3];
+    var x = a[0],
+        y = a[1],
+        z = a[2]; // var qvec = [qx, qy, qz];
+    // var uv = vec3.cross([], qvec, a);
+
+    var uvx = qy * z - qz * y,
+        uvy = qz * x - qx * z,
+        uvz = qx * y - qy * x; // var uuv = vec3.cross([], qvec, uv);
+
+    var uuvx = qy * uvz - qz * uvy,
+        uuvy = qz * uvx - qx * uvz,
+        uuvz = qx * uvy - qy * uvx; // vec3.scale(uv, uv, 2 * w);
+
+    var w2 = qw * 2;
+    uvx *= w2;
+    uvy *= w2;
+    uvz *= w2; // vec3.scale(uuv, uuv, 2);
+
+    uuvx *= 2;
+    uuvy *= 2;
+    uuvz *= 2; // return vec3.add(out, a, vec3.add(out, uv, uuv));
+
+    out[0] = x + uvx + uuvx;
+    out[1] = y + uvy + uuvy;
+    out[2] = z + uvz + uuvz;
+    return out;
+  }
+  /**
+   * Rotate a 3D vector around the x-axis
+   * @param {vec3} out The receiving vec3
+   * @param {ReadonlyVec3} a The vec3 point to rotate
+   * @param {ReadonlyVec3} b The origin of the rotation
+   * @param {Number} rad The angle of rotation in radians
+   * @returns {vec3} out
+   */
+
+  function rotateX$1(out, a, b, rad) {
+    var p = [],
+        r = []; //Translate point to the origin
+
+    p[0] = a[0] - b[0];
+    p[1] = a[1] - b[1];
+    p[2] = a[2] - b[2]; //perform rotation
+
+    r[0] = p[0];
+    r[1] = p[1] * Math.cos(rad) - p[2] * Math.sin(rad);
+    r[2] = p[1] * Math.sin(rad) + p[2] * Math.cos(rad); //translate to correct position
+
+    out[0] = r[0] + b[0];
+    out[1] = r[1] + b[1];
+    out[2] = r[2] + b[2];
+    return out;
+  }
+  /**
+   * Rotate a 3D vector around the y-axis
+   * @param {vec3} out The receiving vec3
+   * @param {ReadonlyVec3} a The vec3 point to rotate
+   * @param {ReadonlyVec3} b The origin of the rotation
+   * @param {Number} rad The angle of rotation in radians
+   * @returns {vec3} out
+   */
+
+  function rotateY$1(out, a, b, rad) {
+    var p = [],
+        r = []; //Translate point to the origin
+
+    p[0] = a[0] - b[0];
+    p[1] = a[1] - b[1];
+    p[2] = a[2] - b[2]; //perform rotation
+
+    r[0] = p[2] * Math.sin(rad) + p[0] * Math.cos(rad);
+    r[1] = p[1];
+    r[2] = p[2] * Math.cos(rad) - p[0] * Math.sin(rad); //translate to correct position
+
+    out[0] = r[0] + b[0];
+    out[1] = r[1] + b[1];
+    out[2] = r[2] + b[2];
+    return out;
+  }
+  /**
+   * Rotate a 3D vector around the z-axis
+   * @param {vec3} out The receiving vec3
+   * @param {ReadonlyVec3} a The vec3 point to rotate
+   * @param {ReadonlyVec3} b The origin of the rotation
+   * @param {Number} rad The angle of rotation in radians
+   * @returns {vec3} out
+   */
+
+  function rotateZ$1(out, a, b, rad) {
+    var p = [],
+        r = []; //Translate point to the origin
+
+    p[0] = a[0] - b[0];
+    p[1] = a[1] - b[1];
+    p[2] = a[2] - b[2]; //perform rotation
+
+    r[0] = p[0] * Math.cos(rad) - p[1] * Math.sin(rad);
+    r[1] = p[0] * Math.sin(rad) + p[1] * Math.cos(rad);
+    r[2] = p[2]; //translate to correct position
+
+    out[0] = r[0] + b[0];
+    out[1] = r[1] + b[1];
+    out[2] = r[2] + b[2];
+    return out;
+  }
+  /**
+   * Get the angle between two 3D vectors
+   * @param {ReadonlyVec3} a The first operand
+   * @param {ReadonlyVec3} b The second operand
+   * @returns {Number} The angle in radians
+   */
+
+  function angle(a, b) {
+    var ax = a[0],
+        ay = a[1],
+        az = a[2],
+        bx = b[0],
+        by = b[1],
+        bz = b[2],
+        mag1 = Math.sqrt(ax * ax + ay * ay + az * az),
+        mag2 = Math.sqrt(bx * bx + by * by + bz * bz),
+        mag = mag1 * mag2,
+        cosine = mag && dot(a, b) / mag;
+    return Math.acos(Math.min(Math.max(cosine, -1), 1));
+  }
+  /**
+   * Set the components of a vec3 to zero
+   *
+   * @param {vec3} out the receiving vector
+   * @returns {vec3} out
+   */
+
+  function zero(out) {
+    out[0] = 0.0;
+    out[1] = 0.0;
+    out[2] = 0.0;
+    return out;
+  }
+  /**
+   * Returns a string representation of a vector
+   *
+   * @param {ReadonlyVec3} a vector to represent as a string
+   * @returns {String} string representation of the vector
+   */
+
+  function str$4(a) {
+    return "vec3(" + a[0] + ", " + a[1] + ", " + a[2] + ")";
+  }
+  /**
+   * Returns whether or not the vectors have exactly the same elements in the same position (when compared with ===)
+   *
+   * @param {ReadonlyVec3} a The first vector.
+   * @param {ReadonlyVec3} b The second vector.
+   * @returns {Boolean} True if the vectors are equal, false otherwise.
+   */
+
+  function exactEquals$4(a, b) {
+    return a[0] === b[0] && a[1] === b[1] && a[2] === b[2];
+  }
+  /**
+   * Returns whether or not the vectors have approximately the same elements in the same position.
+   *
+   * @param {ReadonlyVec3} a The first vector.
+   * @param {ReadonlyVec3} b The second vector.
+   * @returns {Boolean} True if the vectors are equal, false otherwise.
+   */
+
+  function equals$5(a, b) {
+    var a0 = a[0],
+        a1 = a[1],
+        a2 = a[2];
+    var b0 = b[0],
+        b1 = b[1],
+        b2 = b[2];
+    return Math.abs(a0 - b0) <= EPSILON * Math.max(1.0, Math.abs(a0), Math.abs(b0)) && Math.abs(a1 - b1) <= EPSILON * Math.max(1.0, Math.abs(a1), Math.abs(b1)) && Math.abs(a2 - b2) <= EPSILON * Math.max(1.0, Math.abs(a2), Math.abs(b2));
+  }
+  /**
+   * Alias for {@link vec3.subtract}
+   * @function
+   */
+
+  var sub$4 = subtract$4;
+  /**
+   * Alias for {@link vec3.multiply}
+   * @function
+   */
+
+  var mul$4 = multiply$4;
+  /**
+   * Alias for {@link vec3.divide}
+   * @function
+   */
+
+  var div = divide;
+  /**
+   * Alias for {@link vec3.distance}
+   * @function
+   */
+
+  var dist = distance;
+  /**
+   * Alias for {@link vec3.squaredDistance}
+   * @function
+   */
+
+  var sqrDist = squaredDistance;
+  /**
+   * Alias for {@link vec3.length}
+   * @function
+   */
+
+  var len = length;
+  /**
+   * Alias for {@link vec3.squaredLength}
+   * @function
+   */
+
+  var sqrLen = squaredLength;
+  /**
+   * Perform some operation over an array of vec3s.
+   *
+   * @param {Array} a the array of vectors to iterate over
+   * @param {Number} stride Number of elements between the start of each vec3. If 0 assumes tightly packed
+   * @param {Number} offset Number of elements to skip at the beginning of the array
+   * @param {Number} count Number of vec3s to iterate over. If 0 iterates over entire array
+   * @param {Function} fn Function to call for each vector in the array
+   * @param {Object} [arg] additional argument to pass to fn
+   * @returns {Array} a
+   * @function
+   */
+
+  var forEach = function () {
+    var vec = create$4();
+    return function (a, stride, offset, count, fn, arg) {
+      var i, l;
+
+      if (!stride) {
+        stride = 3;
+      }
+
+      if (!offset) {
+        offset = 0;
+      }
+
+      if (count) {
+        l = Math.min(count * stride + offset, a.length);
+      } else {
+        l = a.length;
+      }
+
+      for (i = offset; i < l; i += stride) {
+        vec[0] = a[i];
+        vec[1] = a[i + 1];
+        vec[2] = a[i + 2];
+        fn(vec, vec, arg);
+        a[i] = vec[0];
+        a[i + 1] = vec[1];
+        a[i + 2] = vec[2];
+      }
+
+      return a;
+    };
+  }();
+
+  var vec3 = /*#__PURE__*/Object.freeze({
+    __proto__: null,
+    create: create$4,
+    clone: clone$4,
+    length: length,
+    fromValues: fromValues$4,
+    copy: copy$4,
+    set: set$4,
+    add: add$4,
+    subtract: subtract$4,
+    multiply: multiply$4,
+    divide: divide,
+    ceil: ceil,
+    floor: floor,
+    min: min,
+    max: max,
+    round: round,
+    scale: scale$4,
+    scaleAndAdd: scaleAndAdd,
+    distance: distance,
+    squaredDistance: squaredDistance,
+    squaredLength: squaredLength,
+    negate: negate,
+    inverse: inverse,
+    normalize: normalize,
+    dot: dot,
+    cross: cross,
+    lerp: lerp,
+    hermite: hermite,
+    bezier: bezier,
+    random: random,
+    transformMat4: transformMat4,
+    transformMat3: transformMat3,
+    transformQuat: transformQuat,
+    rotateX: rotateX$1,
+    rotateY: rotateY$1,
+    rotateZ: rotateZ$1,
+    angle: angle,
+    zero: zero,
+    str: str$4,
+    exactEquals: exactEquals$4,
+    equals: equals$5,
+    sub: sub$4,
+    mul: mul$4,
+    div: div,
+    dist: dist,
+    sqrDist: sqrDist,
+    len: len,
+    sqrLen: sqrLen,
+    forEach: forEach
+  });
+
+  /**
+   * 4 Dimensional Vector
+   * @module vec4
+   */
+
+  /**
+   * Creates a new, empty vec4
+   *
+   * @returns {vec4} a new 4D vector
+   */
+
+  function create$5() {
+    var out = new ARRAY_TYPE(4);
+
+    if (ARRAY_TYPE != Float32Array) {
+      out[0] = 0;
+      out[1] = 0;
+      out[2] = 0;
+      out[3] = 0;
+    }
+
+    return out;
+  }
+  /**
+   * Creates a new vec4 initialized with values from an existing vector
+   *
+   * @param {ReadonlyVec4} a vector to clone
+   * @returns {vec4} a new 4D vector
+   */
+
+  function clone$5(a) {
+    var out = new ARRAY_TYPE(4);
+    out[0] = a[0];
+    out[1] = a[1];
+    out[2] = a[2];
+    out[3] = a[3];
+    return out;
+  }
+  /**
+   * Creates a new vec4 initialized with the given values
+   *
+   * @param {Number} x X component
+   * @param {Number} y Y component
+   * @param {Number} z Z component
+   * @param {Number} w W component
+   * @returns {vec4} a new 4D vector
+   */
+
+  function fromValues$5(x, y, z, w) {
+    var out = new ARRAY_TYPE(4);
+    out[0] = x;
+    out[1] = y;
+    out[2] = z;
+    out[3] = w;
+    return out;
+  }
+  /**
+   * Copy the values from one vec4 to another
+   *
+   * @param {vec4} out the receiving vector
+   * @param {ReadonlyVec4} a the source vector
+   * @returns {vec4} out
+   */
+
+  function copy$5(out, a) {
+    out[0] = a[0];
+    out[1] = a[1];
+    out[2] = a[2];
+    out[3] = a[3];
+    return out;
+  }
+  /**
+   * Set the components of a vec4 to the given values
+   *
+   * @param {vec4} out the receiving vector
+   * @param {Number} x X component
+   * @param {Number} y Y component
+   * @param {Number} z Z component
+   * @param {Number} w W component
+   * @returns {vec4} out
+   */
+
+  function set$5(out, x, y, z, w) {
+    out[0] = x;
+    out[1] = y;
+    out[2] = z;
+    out[3] = w;
+    return out;
+  }
+  /**
+   * Adds two vec4's
+   *
+   * @param {vec4} out the receiving vector
+   * @param {ReadonlyVec4} a the first operand
+   * @param {ReadonlyVec4} b the second operand
+   * @returns {vec4} out
+   */
+
+  function add$5(out, a, b) {
+    out[0] = a[0] + b[0];
+    out[1] = a[1] + b[1];
+    out[2] = a[2] + b[2];
+    out[3] = a[3] + b[3];
+    return out;
+  }
+  /**
+   * Subtracts vector b from vector a
+   *
+   * @param {vec4} out the receiving vector
+   * @param {ReadonlyVec4} a the first operand
+   * @param {ReadonlyVec4} b the second operand
+   * @returns {vec4} out
+   */
+
+  function subtract$5(out, a, b) {
+    out[0] = a[0] - b[0];
+    out[1] = a[1] - b[1];
+    out[2] = a[2] - b[2];
+    out[3] = a[3] - b[3];
+    return out;
+  }
+  /**
+   * Multiplies two vec4's
+   *
+   * @param {vec4} out the receiving vector
+   * @param {ReadonlyVec4} a the first operand
+   * @param {ReadonlyVec4} b the second operand
+   * @returns {vec4} out
+   */
+
+  function multiply$5(out, a, b) {
+    out[0] = a[0] * b[0];
+    out[1] = a[1] * b[1];
+    out[2] = a[2] * b[2];
+    out[3] = a[3] * b[3];
+    return out;
+  }
+  /**
+   * Divides two vec4's
+   *
+   * @param {vec4} out the receiving vector
+   * @param {ReadonlyVec4} a the first operand
+   * @param {ReadonlyVec4} b the second operand
+   * @returns {vec4} out
+   */
+
+  function divide$1(out, a, b) {
+    out[0] = a[0] / b[0];
+    out[1] = a[1] / b[1];
+    out[2] = a[2] / b[2];
+    out[3] = a[3] / b[3];
+    return out;
+  }
+  /**
+   * Math.ceil the components of a vec4
+   *
+   * @param {vec4} out the receiving vector
+   * @param {ReadonlyVec4} a vector to ceil
+   * @returns {vec4} out
+   */
+
+  function ceil$1(out, a) {
+    out[0] = Math.ceil(a[0]);
+    out[1] = Math.ceil(a[1]);
+    out[2] = Math.ceil(a[2]);
+    out[3] = Math.ceil(a[3]);
+    return out;
+  }
+  /**
+   * Math.floor the components of a vec4
+   *
+   * @param {vec4} out the receiving vector
+   * @param {ReadonlyVec4} a vector to floor
+   * @returns {vec4} out
+   */
+
+  function floor$1(out, a) {
+    out[0] = Math.floor(a[0]);
+    out[1] = Math.floor(a[1]);
+    out[2] = Math.floor(a[2]);
+    out[3] = Math.floor(a[3]);
+    return out;
+  }
+  /**
+   * Returns the minimum of two vec4's
+   *
+   * @param {vec4} out the receiving vector
+   * @param {ReadonlyVec4} a the first operand
+   * @param {ReadonlyVec4} b the second operand
+   * @returns {vec4} out
+   */
+
+  function min$1(out, a, b) {
+    out[0] = Math.min(a[0], b[0]);
+    out[1] = Math.min(a[1], b[1]);
+    out[2] = Math.min(a[2], b[2]);
+    out[3] = Math.min(a[3], b[3]);
+    return out;
+  }
+  /**
+   * Returns the maximum of two vec4's
+   *
+   * @param {vec4} out the receiving vector
+   * @param {ReadonlyVec4} a the first operand
+   * @param {ReadonlyVec4} b the second operand
+   * @returns {vec4} out
+   */
+
+  function max$1(out, a, b) {
+    out[0] = Math.max(a[0], b[0]);
+    out[1] = Math.max(a[1], b[1]);
+    out[2] = Math.max(a[2], b[2]);
+    out[3] = Math.max(a[3], b[3]);
+    return out;
+  }
+  /**
+   * Math.round the components of a vec4
+   *
+   * @param {vec4} out the receiving vector
+   * @param {ReadonlyVec4} a vector to round
+   * @returns {vec4} out
+   */
+
+  function round$1(out, a) {
+    out[0] = Math.round(a[0]);
+    out[1] = Math.round(a[1]);
+    out[2] = Math.round(a[2]);
+    out[3] = Math.round(a[3]);
+    return out;
+  }
+  /**
+   * Scales a vec4 by a scalar number
+   *
+   * @param {vec4} out the receiving vector
+   * @param {ReadonlyVec4} a the vector to scale
+   * @param {Number} b amount to scale the vector by
+   * @returns {vec4} out
+   */
+
+  function scale$5(out, a, b) {
+    out[0] = a[0] * b;
+    out[1] = a[1] * b;
+    out[2] = a[2] * b;
+    out[3] = a[3] * b;
+    return out;
+  }
+  /**
+   * Adds two vec4's after scaling the second operand by a scalar value
+   *
+   * @param {vec4} out the receiving vector
+   * @param {ReadonlyVec4} a the first operand
+   * @param {ReadonlyVec4} b the second operand
+   * @param {Number} scale the amount to scale b by before adding
+   * @returns {vec4} out
+   */
+
+  function scaleAndAdd$1(out, a, b, scale) {
+    out[0] = a[0] + b[0] * scale;
+    out[1] = a[1] + b[1] * scale;
+    out[2] = a[2] + b[2] * scale;
+    out[3] = a[3] + b[3] * scale;
+    return out;
+  }
+  /**
+   * Calculates the euclidian distance between two vec4's
+   *
+   * @param {ReadonlyVec4} a the first operand
+   * @param {ReadonlyVec4} b the second operand
+   * @returns {Number} distance between a and b
+   */
+
+  function distance$1(a, b) {
+    var x = b[0] - a[0];
+    var y = b[1] - a[1];
+    var z = b[2] - a[2];
+    var w = b[3] - a[3];
+    return Math.hypot(x, y, z, w);
+  }
+  /**
+   * Calculates the squared euclidian distance between two vec4's
+   *
+   * @param {ReadonlyVec4} a the first operand
+   * @param {ReadonlyVec4} b the second operand
+   * @returns {Number} squared distance between a and b
+   */
+
+  function squaredDistance$1(a, b) {
+    var x = b[0] - a[0];
+    var y = b[1] - a[1];
+    var z = b[2] - a[2];
+    var w = b[3] - a[3];
+    return x * x + y * y + z * z + w * w;
+  }
+  /**
+   * Calculates the length of a vec4
+   *
+   * @param {ReadonlyVec4} a vector to calculate length of
+   * @returns {Number} length of a
+   */
+
+  function length$1(a) {
+    var x = a[0];
+    var y = a[1];
+    var z = a[2];
+    var w = a[3];
+    return Math.hypot(x, y, z, w);
+  }
+  /**
+   * Calculates the squared length of a vec4
+   *
+   * @param {ReadonlyVec4} a vector to calculate squared length of
+   * @returns {Number} squared length of a
+   */
+
+  function squaredLength$1(a) {
+    var x = a[0];
+    var y = a[1];
+    var z = a[2];
+    var w = a[3];
+    return x * x + y * y + z * z + w * w;
+  }
+  /**
+   * Negates the components of a vec4
+   *
+   * @param {vec4} out the receiving vector
+   * @param {ReadonlyVec4} a vector to negate
+   * @returns {vec4} out
+   */
+
+  function negate$1(out, a) {
+    out[0] = -a[0];
+    out[1] = -a[1];
+    out[2] = -a[2];
+    out[3] = -a[3];
+    return out;
+  }
+  /**
+   * Returns the inverse of the components of a vec4
+   *
+   * @param {vec4} out the receiving vector
+   * @param {ReadonlyVec4} a vector to invert
+   * @returns {vec4} out
+   */
+
+  function inverse$1(out, a) {
+    out[0] = 1.0 / a[0];
+    out[1] = 1.0 / a[1];
+    out[2] = 1.0 / a[2];
+    out[3] = 1.0 / a[3];
+    return out;
+  }
+  /**
+   * Normalize a vec4
+   *
+   * @param {vec4} out the receiving vector
+   * @param {ReadonlyVec4} a vector to normalize
+   * @returns {vec4} out
+   */
+
+  function normalize$1(out, a) {
+    var x = a[0];
+    var y = a[1];
+    var z = a[2];
+    var w = a[3];
+    var len = x * x + y * y + z * z + w * w;
+
+    if (len > 0) {
+      len = 1 / Math.sqrt(len);
+    }
+
+    out[0] = x * len;
+    out[1] = y * len;
+    out[2] = z * len;
+    out[3] = w * len;
+    return out;
+  }
+  /**
+   * Calculates the dot product of two vec4's
+   *
+   * @param {ReadonlyVec4} a the first operand
+   * @param {ReadonlyVec4} b the second operand
+   * @returns {Number} dot product of a and b
+   */
+
+  function dot$1(a, b) {
+    return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3];
+  }
+  /**
+   * Returns the cross-product of three vectors in a 4-dimensional space
+   *
+   * @param {ReadonlyVec4} result the receiving vector
+   * @param {ReadonlyVec4} U the first vector
+   * @param {ReadonlyVec4} V the second vector
+   * @param {ReadonlyVec4} W the third vector
+   * @returns {vec4} result
+   */
+
+  function cross$1(out, u, v, w) {
+    var A = v[0] * w[1] - v[1] * w[0],
+        B = v[0] * w[2] - v[2] * w[0],
+        C = v[0] * w[3] - v[3] * w[0],
+        D = v[1] * w[2] - v[2] * w[1],
+        E = v[1] * w[3] - v[3] * w[1],
+        F = v[2] * w[3] - v[3] * w[2];
+    var G = u[0];
+    var H = u[1];
+    var I = u[2];
+    var J = u[3];
+    out[0] = H * F - I * E + J * D;
+    out[1] = -(G * F) + I * C - J * B;
+    out[2] = G * E - H * C + J * A;
+    out[3] = -(G * D) + H * B - I * A;
+    return out;
+  }
+  /**
+   * Performs a linear interpolation between two vec4's
+   *
+   * @param {vec4} out the receiving vector
+   * @param {ReadonlyVec4} a the first operand
+   * @param {ReadonlyVec4} b the second operand
+   * @param {Number} t interpolation amount, in the range [0-1], between the two inputs
+   * @returns {vec4} out
+   */
+
+  function lerp$1(out, a, b, t) {
+    var ax = a[0];
+    var ay = a[1];
+    var az = a[2];
+    var aw = a[3];
+    out[0] = ax + t * (b[0] - ax);
+    out[1] = ay + t * (b[1] - ay);
+    out[2] = az + t * (b[2] - az);
+    out[3] = aw + t * (b[3] - aw);
+    return out;
+  }
+  /**
+   * Generates a random vector with the given scale
+   *
+   * @param {vec4} out the receiving vector
+   * @param {Number} [scale] Length of the resulting vector. If ommitted, a unit vector will be returned
+   * @returns {vec4} out
+   */
+
+  function random$1(out, scale) {
+    scale = scale || 1.0; // Marsaglia, George. Choosing a Point from the Surface of a
+    // Sphere. Ann. Math. Statist. 43 (1972), no. 2, 645--646.
+    // http://projecteuclid.org/euclid.aoms/1177692644;
+
+    var v1, v2, v3, v4;
+    var s1, s2;
+
+    do {
+      v1 = RANDOM() * 2 - 1;
+      v2 = RANDOM() * 2 - 1;
+      s1 = v1 * v1 + v2 * v2;
+    } while (s1 >= 1);
+
+    do {
+      v3 = RANDOM() * 2 - 1;
+      v4 = RANDOM() * 2 - 1;
+      s2 = v3 * v3 + v4 * v4;
+    } while (s2 >= 1);
+
+    var d = Math.sqrt((1 - s1) / s2);
+    out[0] = scale * v1;
+    out[1] = scale * v2;
+    out[2] = scale * v3 * d;
+    out[3] = scale * v4 * d;
+    return out;
+  }
+  /**
+   * Transforms the vec4 with a mat4.
+   *
+   * @param {vec4} out the receiving vector
+   * @param {ReadonlyVec4} a the vector to transform
+   * @param {ReadonlyMat4} m matrix to transform with
+   * @returns {vec4} out
+   */
+
+  function transformMat4$1(out, a, m) {
+    var x = a[0],
+        y = a[1],
+        z = a[2],
+        w = a[3];
+    out[0] = m[0] * x + m[4] * y + m[8] * z + m[12] * w;
+    out[1] = m[1] * x + m[5] * y + m[9] * z + m[13] * w;
+    out[2] = m[2] * x + m[6] * y + m[10] * z + m[14] * w;
+    out[3] = m[3] * x + m[7] * y + m[11] * z + m[15] * w;
+    return out;
+  }
+  /**
+   * Transforms the vec4 with a quat
+   *
+   * @param {vec4} out the receiving vector
+   * @param {ReadonlyVec4} a the vector to transform
+   * @param {ReadonlyQuat} q quaternion to transform with
+   * @returns {vec4} out
+   */
+
+  function transformQuat$1(out, a, q) {
+    var x = a[0],
+        y = a[1],
+        z = a[2];
+    var qx = q[0],
+        qy = q[1],
+        qz = q[2],
+        qw = q[3]; // calculate quat * vec
+
+    var ix = qw * x + qy * z - qz * y;
+    var iy = qw * y + qz * x - qx * z;
+    var iz = qw * z + qx * y - qy * x;
+    var iw = -qx * x - qy * y - qz * z; // calculate result * inverse quat
+
+    out[0] = ix * qw + iw * -qx + iy * -qz - iz * -qy;
+    out[1] = iy * qw + iw * -qy + iz * -qx - ix * -qz;
+    out[2] = iz * qw + iw * -qz + ix * -qy - iy * -qx;
+    out[3] = a[3];
+    return out;
+  }
+  /**
+   * Set the components of a vec4 to zero
+   *
+   * @param {vec4} out the receiving vector
+   * @returns {vec4} out
+   */
+
+  function zero$1(out) {
+    out[0] = 0.0;
+    out[1] = 0.0;
+    out[2] = 0.0;
+    out[3] = 0.0;
+    return out;
+  }
+  /**
+   * Returns a string representation of a vector
+   *
+   * @param {ReadonlyVec4} a vector to represent as a string
+   * @returns {String} string representation of the vector
+   */
+
+  function str$5(a) {
+    return "vec4(" + a[0] + ", " + a[1] + ", " + a[2] + ", " + a[3] + ")";
+  }
+  /**
+   * Returns whether or not the vectors have exactly the same elements in the same position (when compared with ===)
+   *
+   * @param {ReadonlyVec4} a The first vector.
+   * @param {ReadonlyVec4} b The second vector.
+   * @returns {Boolean} True if the vectors are equal, false otherwise.
+   */
+
+  function exactEquals$5(a, b) {
+    return a[0] === b[0] && a[1] === b[1] && a[2] === b[2] && a[3] === b[3];
+  }
+  /**
+   * Returns whether or not the vectors have approximately the same elements in the same position.
+   *
+   * @param {ReadonlyVec4} a The first vector.
+   * @param {ReadonlyVec4} b The second vector.
+   * @returns {Boolean} True if the vectors are equal, false otherwise.
+   */
+
+  function equals$6(a, b) {
+    var a0 = a[0],
+        a1 = a[1],
+        a2 = a[2],
+        a3 = a[3];
+    var b0 = b[0],
+        b1 = b[1],
+        b2 = b[2],
+        b3 = b[3];
+    return Math.abs(a0 - b0) <= EPSILON * Math.max(1.0, Math.abs(a0), Math.abs(b0)) && Math.abs(a1 - b1) <= EPSILON * Math.max(1.0, Math.abs(a1), Math.abs(b1)) && Math.abs(a2 - b2) <= EPSILON * Math.max(1.0, Math.abs(a2), Math.abs(b2)) && Math.abs(a3 - b3) <= EPSILON * Math.max(1.0, Math.abs(a3), Math.abs(b3));
+  }
+  /**
+   * Alias for {@link vec4.subtract}
+   * @function
+   */
+
+  var sub$5 = subtract$5;
+  /**
+   * Alias for {@link vec4.multiply}
+   * @function
+   */
+
+  var mul$5 = multiply$5;
+  /**
+   * Alias for {@link vec4.divide}
+   * @function
+   */
+
+  var div$1 = divide$1;
+  /**
+   * Alias for {@link vec4.distance}
+   * @function
+   */
+
+  var dist$1 = distance$1;
+  /**
+   * Alias for {@link vec4.squaredDistance}
+   * @function
+   */
+
+  var sqrDist$1 = squaredDistance$1;
+  /**
+   * Alias for {@link vec4.length}
+   * @function
+   */
+
+  var len$1 = length$1;
+  /**
+   * Alias for {@link vec4.squaredLength}
+   * @function
+   */
+
+  var sqrLen$1 = squaredLength$1;
+  /**
+   * Perform some operation over an array of vec4s.
+   *
+   * @param {Array} a the array of vectors to iterate over
+   * @param {Number} stride Number of elements between the start of each vec4. If 0 assumes tightly packed
+   * @param {Number} offset Number of elements to skip at the beginning of the array
+   * @param {Number} count Number of vec4s to iterate over. If 0 iterates over entire array
+   * @param {Function} fn Function to call for each vector in the array
+   * @param {Object} [arg] additional argument to pass to fn
+   * @returns {Array} a
+   * @function
+   */
+
+  var forEach$1 = function () {
+    var vec = create$5();
+    return function (a, stride, offset, count, fn, arg) {
+      var i, l;
+
+      if (!stride) {
+        stride = 4;
+      }
+
+      if (!offset) {
+        offset = 0;
+      }
+
+      if (count) {
+        l = Math.min(count * stride + offset, a.length);
+      } else {
+        l = a.length;
+      }
+
+      for (i = offset; i < l; i += stride) {
+        vec[0] = a[i];
+        vec[1] = a[i + 1];
+        vec[2] = a[i + 2];
+        vec[3] = a[i + 3];
+        fn(vec, vec, arg);
+        a[i] = vec[0];
+        a[i + 1] = vec[1];
+        a[i + 2] = vec[2];
+        a[i + 3] = vec[3];
+      }
+
+      return a;
+    };
+  }();
+
+  var vec4 = /*#__PURE__*/Object.freeze({
+    __proto__: null,
+    create: create$5,
+    clone: clone$5,
+    fromValues: fromValues$5,
+    copy: copy$5,
+    set: set$5,
+    add: add$5,
+    subtract: subtract$5,
+    multiply: multiply$5,
+    divide: divide$1,
+    ceil: ceil$1,
+    floor: floor$1,
+    min: min$1,
+    max: max$1,
+    round: round$1,
+    scale: scale$5,
+    scaleAndAdd: scaleAndAdd$1,
+    distance: distance$1,
+    squaredDistance: squaredDistance$1,
+    length: length$1,
+    squaredLength: squaredLength$1,
+    negate: negate$1,
+    inverse: inverse$1,
+    normalize: normalize$1,
+    dot: dot$1,
+    cross: cross$1,
+    lerp: lerp$1,
+    random: random$1,
+    transformMat4: transformMat4$1,
+    transformQuat: transformQuat$1,
+    zero: zero$1,
+    str: str$5,
+    exactEquals: exactEquals$5,
+    equals: equals$6,
+    sub: sub$5,
+    mul: mul$5,
+    div: div$1,
+    dist: dist$1,
+    sqrDist: sqrDist$1,
+    len: len$1,
+    sqrLen: sqrLen$1,
+    forEach: forEach$1
+  });
+
+  /**
+   * Quaternion
+   * @module quat
+   */
+
+  /**
+   * Creates a new identity quat
+   *
+   * @returns {quat} a new quaternion
+   */
+
+  function create$6() {
+    var out = new ARRAY_TYPE(4);
+
+    if (ARRAY_TYPE != Float32Array) {
+      out[0] = 0;
+      out[1] = 0;
+      out[2] = 0;
+    }
+
+    out[3] = 1;
+    return out;
+  }
+  /**
+   * Set a quat to the identity quaternion
+   *
+   * @param {quat} out the receiving quaternion
+   * @returns {quat} out
+   */
+
+  function identity$4(out) {
+    out[0] = 0;
+    out[1] = 0;
+    out[2] = 0;
+    out[3] = 1;
+    return out;
+  }
+  /**
+   * Sets a quat from the given angle and rotation axis,
+   * then returns it.
+   *
+   * @param {quat} out the receiving quaternion
+   * @param {ReadonlyVec3} axis the axis around which to rotate
+   * @param {Number} rad the angle in radians
+   * @returns {quat} out
+   **/
+
+  function setAxisAngle(out, axis, rad) {
+    rad = rad * 0.5;
+    var s = Math.sin(rad);
+    out[0] = s * axis[0];
+    out[1] = s * axis[1];
+    out[2] = s * axis[2];
+    out[3] = Math.cos(rad);
+    return out;
+  }
+  /**
+   * Gets the rotation axis and angle for a given
+   *  quaternion. If a quaternion is created with
+   *  setAxisAngle, this method will return the same
+   *  values as providied in the original parameter list
+   *  OR functionally equivalent values.
+   * Example: The quaternion formed by axis [0, 0, 1] and
+   *  angle -90 is the same as the quaternion formed by
+   *  [0, 0, 1] and 270. This method favors the latter.
+   * @param  {vec3} out_axis  Vector receiving the axis of rotation
+   * @param  {ReadonlyQuat} q     Quaternion to be decomposed
+   * @return {Number}     Angle, in radians, of the rotation
+   */
+
+  function getAxisAngle(out_axis, q) {
+    var rad = Math.acos(q[3]) * 2.0;
+    var s = Math.sin(rad / 2.0);
+
+    if (s > EPSILON) {
+      out_axis[0] = q[0] / s;
+      out_axis[1] = q[1] / s;
+      out_axis[2] = q[2] / s;
+    } else {
+      // If s is zero, return any axis (no rotation - axis does not matter)
+      out_axis[0] = 1;
+      out_axis[1] = 0;
+      out_axis[2] = 0;
+    }
+
+    return rad;
+  }
+  /**
+   * Gets the angular distance between two unit quaternions
+   *
+   * @param  {ReadonlyQuat} a     Origin unit quaternion
+   * @param  {ReadonlyQuat} b     Destination unit quaternion
+   * @return {Number}     Angle, in radians, between the two quaternions
+   */
+
+  function getAngle(a, b) {
+    var dotproduct = dot$2(a, b);
+    return Math.acos(2 * dotproduct * dotproduct - 1);
+  }
+  /**
+   * Multiplies two quat's
+   *
+   * @param {quat} out the receiving quaternion
+   * @param {ReadonlyQuat} a the first operand
+   * @param {ReadonlyQuat} b the second operand
+   * @returns {quat} out
+   */
+
+  function multiply$6(out, a, b) {
+    var ax = a[0],
+        ay = a[1],
+        az = a[2],
+        aw = a[3];
+    var bx = b[0],
+        by = b[1],
+        bz = b[2],
+        bw = b[3];
+    out[0] = ax * bw + aw * bx + ay * bz - az * by;
+    out[1] = ay * bw + aw * by + az * bx - ax * bz;
+    out[2] = az * bw + aw * bz + ax * by - ay * bx;
+    out[3] = aw * bw - ax * bx - ay * by - az * bz;
+    return out;
+  }
+  /**
+   * Rotates a quaternion by the given angle about the X axis
+   *
+   * @param {quat} out quat receiving operation result
+   * @param {ReadonlyQuat} a quat to rotate
+   * @param {number} rad angle (in radians) to rotate
+   * @returns {quat} out
+   */
+
+  function rotateX$2(out, a, rad) {
+    rad *= 0.5;
+    var ax = a[0],
+        ay = a[1],
+        az = a[2],
+        aw = a[3];
+    var bx = Math.sin(rad),
+        bw = Math.cos(rad);
+    out[0] = ax * bw + aw * bx;
+    out[1] = ay * bw + az * bx;
+    out[2] = az * bw - ay * bx;
+    out[3] = aw * bw - ax * bx;
+    return out;
+  }
+  /**
+   * Rotates a quaternion by the given angle about the Y axis
+   *
+   * @param {quat} out quat receiving operation result
+   * @param {ReadonlyQuat} a quat to rotate
+   * @param {number} rad angle (in radians) to rotate
+   * @returns {quat} out
+   */
+
+  function rotateY$2(out, a, rad) {
+    rad *= 0.5;
+    var ax = a[0],
+        ay = a[1],
+        az = a[2],
+        aw = a[3];
+    var by = Math.sin(rad),
+        bw = Math.cos(rad);
+    out[0] = ax * bw - az * by;
+    out[1] = ay * bw + aw * by;
+    out[2] = az * bw + ax * by;
+    out[3] = aw * bw - ay * by;
+    return out;
+  }
+  /**
+   * Rotates a quaternion by the given angle about the Z axis
+   *
+   * @param {quat} out quat receiving operation result
+   * @param {ReadonlyQuat} a quat to rotate
+   * @param {number} rad angle (in radians) to rotate
+   * @returns {quat} out
+   */
+
+  function rotateZ$2(out, a, rad) {
+    rad *= 0.5;
+    var ax = a[0],
+        ay = a[1],
+        az = a[2],
+        aw = a[3];
+    var bz = Math.sin(rad),
+        bw = Math.cos(rad);
+    out[0] = ax * bw + ay * bz;
+    out[1] = ay * bw - ax * bz;
+    out[2] = az * bw + aw * bz;
+    out[3] = aw * bw - az * bz;
+    return out;
+  }
+  /**
+   * Calculates the W component of a quat from the X, Y, and Z components.
+   * Assumes that quaternion is 1 unit in length.
+   * Any existing W component will be ignored.
+   *
+   * @param {quat} out the receiving quaternion
+   * @param {ReadonlyQuat} a quat to calculate W component of
+   * @returns {quat} out
+   */
+
+  function calculateW(out, a) {
+    var x = a[0],
+        y = a[1],
+        z = a[2];
+    out[0] = x;
+    out[1] = y;
+    out[2] = z;
+    out[3] = Math.sqrt(Math.abs(1.0 - x * x - y * y - z * z));
+    return out;
+  }
+  /**
+   * Calculate the exponential of a unit quaternion.
+   *
+   * @param {quat} out the receiving quaternion
+   * @param {ReadonlyQuat} a quat to calculate the exponential of
+   * @returns {quat} out
+   */
+
+  function exp(out, a) {
+    var x = a[0],
+        y = a[1],
+        z = a[2],
+        w = a[3];
+    var r = Math.sqrt(x * x + y * y + z * z);
+    var et = Math.exp(w);
+    var s = r > 0 ? et * Math.sin(r) / r : 0;
+    out[0] = x * s;
+    out[1] = y * s;
+    out[2] = z * s;
+    out[3] = et * Math.cos(r);
+    return out;
+  }
+  /**
+   * Calculate the natural logarithm of a unit quaternion.
+   *
+   * @param {quat} out the receiving quaternion
+   * @param {ReadonlyQuat} a quat to calculate the exponential of
+   * @returns {quat} out
+   */
+
+  function ln(out, a) {
+    var x = a[0],
+        y = a[1],
+        z = a[2],
+        w = a[3];
+    var r = Math.sqrt(x * x + y * y + z * z);
+    var t = r > 0 ? Math.atan2(r, w) / r : 0;
+    out[0] = x * t;
+    out[1] = y * t;
+    out[2] = z * t;
+    out[3] = 0.5 * Math.log(x * x + y * y + z * z + w * w);
+    return out;
+  }
+  /**
+   * Calculate the scalar power of a unit quaternion.
+   *
+   * @param {quat} out the receiving quaternion
+   * @param {ReadonlyQuat} a quat to calculate the exponential of
+   * @param {Number} b amount to scale the quaternion by
+   * @returns {quat} out
+   */
+
+  function pow(out, a, b) {
+    ln(out, a);
+    scale$6(out, out, b);
+    exp(out, out);
+    return out;
+  }
+  /**
+   * Performs a spherical linear interpolation between two quat
+   *
+   * @param {quat} out the receiving quaternion
+   * @param {ReadonlyQuat} a the first operand
+   * @param {ReadonlyQuat} b the second operand
+   * @param {Number} t interpolation amount, in the range [0-1], between the two inputs
+   * @returns {quat} out
+   */
+
+  function slerp(out, a, b, t) {
+    // benchmarks:
+    //    http://jsperf.com/quaternion-slerp-implementations
+    var ax = a[0],
+        ay = a[1],
+        az = a[2],
+        aw = a[3];
+    var bx = b[0],
+        by = b[1],
+        bz = b[2],
+        bw = b[3];
+    var omega, cosom, sinom, scale0, scale1; // calc cosine
+
+    cosom = ax * bx + ay * by + az * bz + aw * bw; // adjust signs (if necessary)
+
+    if (cosom < 0.0) {
+      cosom = -cosom;
+      bx = -bx;
+      by = -by;
+      bz = -bz;
+      bw = -bw;
+    } // calculate coefficients
+
+
+    if (1.0 - cosom > EPSILON) {
+      // standard case (slerp)
+      omega = Math.acos(cosom);
+      sinom = Math.sin(omega);
+      scale0 = Math.sin((1.0 - t) * omega) / sinom;
+      scale1 = Math.sin(t * omega) / sinom;
+    } else {
+      // "from" and "to" quaternions are very close
+      //  ... so we can do a linear interpolation
+      scale0 = 1.0 - t;
+      scale1 = t;
+    } // calculate final values
+
+
+    out[0] = scale0 * ax + scale1 * bx;
+    out[1] = scale0 * ay + scale1 * by;
+    out[2] = scale0 * az + scale1 * bz;
+    out[3] = scale0 * aw + scale1 * bw;
+    return out;
+  }
+  /**
+   * Generates a random unit quaternion
+   *
+   * @param {quat} out the receiving quaternion
+   * @returns {quat} out
+   */
+
+  function random$2(out) {
+    // Implementation of http://planning.cs.uiuc.edu/node198.html
+    // TODO: Calling random 3 times is probably not the fastest solution
+    var u1 = RANDOM();
+    var u2 = RANDOM();
+    var u3 = RANDOM();
+    var sqrt1MinusU1 = Math.sqrt(1 - u1);
+    var sqrtU1 = Math.sqrt(u1);
+    out[0] = sqrt1MinusU1 * Math.sin(2.0 * Math.PI * u2);
+    out[1] = sqrt1MinusU1 * Math.cos(2.0 * Math.PI * u2);
+    out[2] = sqrtU1 * Math.sin(2.0 * Math.PI * u3);
+    out[3] = sqrtU1 * Math.cos(2.0 * Math.PI * u3);
+    return out;
+  }
+  /**
+   * Calculates the inverse of a quat
+   *
+   * @param {quat} out the receiving quaternion
+   * @param {ReadonlyQuat} a quat to calculate inverse of
+   * @returns {quat} out
+   */
+
+  function invert$4(out, a) {
+    var a0 = a[0],
+        a1 = a[1],
+        a2 = a[2],
+        a3 = a[3];
+    var dot = a0 * a0 + a1 * a1 + a2 * a2 + a3 * a3;
+    var invDot = dot ? 1.0 / dot : 0; // TODO: Would be faster to return [0,0,0,0] immediately if dot == 0
+
+    out[0] = -a0 * invDot;
+    out[1] = -a1 * invDot;
+    out[2] = -a2 * invDot;
+    out[3] = a3 * invDot;
+    return out;
+  }
+  /**
+   * Calculates the conjugate of a quat
+   * If the quaternion is normalized, this function is faster than quat.inverse and produces the same result.
+   *
+   * @param {quat} out the receiving quaternion
+   * @param {ReadonlyQuat} a quat to calculate conjugate of
+   * @returns {quat} out
+   */
+
+  function conjugate(out, a) {
+    out[0] = -a[0];
+    out[1] = -a[1];
+    out[2] = -a[2];
+    out[3] = a[3];
+    return out;
+  }
+  /**
+   * Creates a quaternion from the given 3x3 rotation matrix.
+   *
+   * NOTE: The resultant quaternion is not normalized, so you should be sure
+   * to renormalize the quaternion yourself where necessary.
+   *
+   * @param {quat} out the receiving quaternion
+   * @param {ReadonlyMat3} m rotation matrix
+   * @returns {quat} out
+   * @function
+   */
+
+  function fromMat3(out, m) {
+    // Algorithm in Ken Shoemake's article in 1987 SIGGRAPH course notes
+    // article "Quaternion Calculus and Fast Animation".
+    var fTrace = m[0] + m[4] + m[8];
+    var fRoot;
+
+    if (fTrace > 0.0) {
+      // |w| > 1/2, may as well choose w > 1/2
+      fRoot = Math.sqrt(fTrace + 1.0); // 2w
+
+      out[3] = 0.5 * fRoot;
+      fRoot = 0.5 / fRoot; // 1/(4w)
+
+      out[0] = (m[5] - m[7]) * fRoot;
+      out[1] = (m[6] - m[2]) * fRoot;
+      out[2] = (m[1] - m[3]) * fRoot;
+    } else {
+      // |w| <= 1/2
+      var i = 0;
+      if (m[4] > m[0]) i = 1;
+      if (m[8] > m[i * 3 + i]) i = 2;
+      var j = (i + 1) % 3;
+      var k = (i + 2) % 3;
+      fRoot = Math.sqrt(m[i * 3 + i] - m[j * 3 + j] - m[k * 3 + k] + 1.0);
+      out[i] = 0.5 * fRoot;
+      fRoot = 0.5 / fRoot;
+      out[3] = (m[j * 3 + k] - m[k * 3 + j]) * fRoot;
+      out[j] = (m[j * 3 + i] + m[i * 3 + j]) * fRoot;
+      out[k] = (m[k * 3 + i] + m[i * 3 + k]) * fRoot;
+    }
+
+    return out;
+  }
+  /**
+   * Creates a quaternion from the given euler angle x, y, z.
+   *
+   * @param {quat} out the receiving quaternion
+   * @param {x} Angle to rotate around X axis in degrees.
+   * @param {y} Angle to rotate around Y axis in degrees.
+   * @param {z} Angle to rotate around Z axis in degrees.
+   * @returns {quat} out
+   * @function
+   */
+
+  function fromEuler(out, x, y, z) {
+    var halfToRad = 0.5 * Math.PI / 180.0;
+    x *= halfToRad;
+    y *= halfToRad;
+    z *= halfToRad;
+    var sx = Math.sin(x);
+    var cx = Math.cos(x);
+    var sy = Math.sin(y);
+    var cy = Math.cos(y);
+    var sz = Math.sin(z);
+    var cz = Math.cos(z);
+    out[0] = sx * cy * cz - cx * sy * sz;
+    out[1] = cx * sy * cz + sx * cy * sz;
+    out[2] = cx * cy * sz - sx * sy * cz;
+    out[3] = cx * cy * cz + sx * sy * sz;
+    return out;
+  }
+  /**
+   * Returns a string representation of a quatenion
+   *
+   * @param {ReadonlyQuat} a vector to represent as a string
+   * @returns {String} string representation of the vector
+   */
+
+  function str$6(a) {
+    return "quat(" + a[0] + ", " + a[1] + ", " + a[2] + ", " + a[3] + ")";
+  }
+  /**
+   * Creates a new quat initialized with values from an existing quaternion
+   *
+   * @param {ReadonlyQuat} a quaternion to clone
+   * @returns {quat} a new quaternion
+   * @function
+   */
+
+  var clone$6 = clone$5;
+  /**
+   * Creates a new quat initialized with the given values
+   *
+   * @param {Number} x X component
+   * @param {Number} y Y component
+   * @param {Number} z Z component
+   * @param {Number} w W component
+   * @returns {quat} a new quaternion
+   * @function
+   */
+
+  var fromValues$6 = fromValues$5;
+  /**
+   * Copy the values from one quat to another
+   *
+   * @param {quat} out the receiving quaternion
+   * @param {ReadonlyQuat} a the source quaternion
+   * @returns {quat} out
+   * @function
+   */
+
+  var copy$6 = copy$5;
+  /**
+   * Set the components of a quat to the given values
+   *
+   * @param {quat} out the receiving quaternion
+   * @param {Number} x X component
+   * @param {Number} y Y component
+   * @param {Number} z Z component
+   * @param {Number} w W component
+   * @returns {quat} out
+   * @function
+   */
+
+  var set$6 = set$5;
+  /**
+   * Adds two quat's
+   *
+   * @param {quat} out the receiving quaternion
+   * @param {ReadonlyQuat} a the first operand
+   * @param {ReadonlyQuat} b the second operand
+   * @returns {quat} out
+   * @function
+   */
+
+  var add$6 = add$5;
+  /**
+   * Alias for {@link quat.multiply}
+   * @function
+   */
+
+  var mul$6 = multiply$6;
+  /**
+   * Scales a quat by a scalar number
+   *
+   * @param {quat} out the receiving vector
+   * @param {ReadonlyQuat} a the vector to scale
+   * @param {Number} b amount to scale the vector by
+   * @returns {quat} out
+   * @function
+   */
+
+  var scale$6 = scale$5;
+  /**
+   * Calculates the dot product of two quat's
+   *
+   * @param {ReadonlyQuat} a the first operand
+   * @param {ReadonlyQuat} b the second operand
+   * @returns {Number} dot product of a and b
+   * @function
+   */
+
+  var dot$2 = dot$1;
+  /**
+   * Performs a linear interpolation between two quat's
+   *
+   * @param {quat} out the receiving quaternion
+   * @param {ReadonlyQuat} a the first operand
+   * @param {ReadonlyQuat} b the second operand
+   * @param {Number} t interpolation amount, in the range [0-1], between the two inputs
+   * @returns {quat} out
+   * @function
+   */
+
+  var lerp$2 = lerp$1;
+  /**
+   * Calculates the length of a quat
+   *
+   * @param {ReadonlyQuat} a vector to calculate length of
+   * @returns {Number} length of a
+   */
+
+  var length$2 = length$1;
+  /**
+   * Alias for {@link quat.length}
+   * @function
+   */
+
+  var len$2 = length$2;
+  /**
+   * Calculates the squared length of a quat
+   *
+   * @param {ReadonlyQuat} a vector to calculate squared length of
+   * @returns {Number} squared length of a
+   * @function
+   */
+
+  var squaredLength$2 = squaredLength$1;
+  /**
+   * Alias for {@link quat.squaredLength}
+   * @function
+   */
+
+  var sqrLen$2 = squaredLength$2;
+  /**
+   * Normalize a quat
+   *
+   * @param {quat} out the receiving quaternion
+   * @param {ReadonlyQuat} a quaternion to normalize
+   * @returns {quat} out
+   * @function
+   */
+
+  var normalize$2 = normalize$1;
+  /**
+   * Returns whether or not the quaternions have exactly the same elements in the same position (when compared with ===)
+   *
+   * @param {ReadonlyQuat} a The first quaternion.
+   * @param {ReadonlyQuat} b The second quaternion.
+   * @returns {Boolean} True if the vectors are equal, false otherwise.
+   */
+
+  var exactEquals$6 = exactEquals$5;
+  /**
+   * Returns whether or not the quaternions have approximately the same elements in the same position.
+   *
+   * @param {ReadonlyQuat} a The first vector.
+   * @param {ReadonlyQuat} b The second vector.
+   * @returns {Boolean} True if the vectors are equal, false otherwise.
+   */
+
+  var equals$7 = equals$6;
+  /**
+   * Sets a quaternion to represent the shortest rotation from one
+   * vector to another.
+   *
+   * Both vectors are assumed to be unit length.
+   *
+   * @param {quat} out the receiving quaternion.
+   * @param {ReadonlyVec3} a the initial vector
+   * @param {ReadonlyVec3} b the destination vector
+   * @returns {quat} out
+   */
+
+  var rotationTo = function () {
+    var tmpvec3 = create$4();
+    var xUnitVec3 = fromValues$4(1, 0, 0);
+    var yUnitVec3 = fromValues$4(0, 1, 0);
+    return function (out, a, b) {
+      var dot$1 = dot(a, b);
+
+      if (dot$1 < -0.999999) {
+        cross(tmpvec3, xUnitVec3, a);
+        if (len(tmpvec3) < 0.000001) cross(tmpvec3, yUnitVec3, a);
+        normalize(tmpvec3, tmpvec3);
+        setAxisAngle(out, tmpvec3, Math.PI);
+        return out;
+      } else if (dot$1 > 0.999999) {
+        out[0] = 0;
+        out[1] = 0;
+        out[2] = 0;
+        out[3] = 1;
+        return out;
+      } else {
+        cross(tmpvec3, a, b);
+        out[0] = tmpvec3[0];
+        out[1] = tmpvec3[1];
+        out[2] = tmpvec3[2];
+        out[3] = 1 + dot$1;
+        return normalize$2(out, out);
+      }
+    };
+  }();
+  /**
+   * Performs a spherical linear interpolation with two control points
+   *
+   * @param {quat} out the receiving quaternion
+   * @param {ReadonlyQuat} a the first operand
+   * @param {ReadonlyQuat} b the second operand
+   * @param {ReadonlyQuat} c the third operand
+   * @param {ReadonlyQuat} d the fourth operand
+   * @param {Number} t interpolation amount, in the range [0-1], between the two inputs
+   * @returns {quat} out
+   */
+
+  var sqlerp = function () {
+    var temp1 = create$6();
+    var temp2 = create$6();
+    return function (out, a, b, c, d, t) {
+      slerp(temp1, a, d, t);
+      slerp(temp2, b, c, t);
+      slerp(out, temp1, temp2, 2 * t * (1 - t));
+      return out;
+    };
+  }();
+  /**
+   * Sets the specified quaternion with values corresponding to the given
+   * axes. Each axis is a vec3 and is expected to be unit length and
+   * perpendicular to all other specified axes.
+   *
+   * @param {ReadonlyVec3} view  the vector representing the viewing direction
+   * @param {ReadonlyVec3} right the vector representing the local "right" direction
+   * @param {ReadonlyVec3} up    the vector representing the local "up" direction
+   * @returns {quat} out
+   */
+
+  var setAxes = function () {
+    var matr = create$2();
+    return function (out, view, right, up) {
+      matr[0] = right[0];
+      matr[3] = right[1];
+      matr[6] = right[2];
+      matr[1] = up[0];
+      matr[4] = up[1];
+      matr[7] = up[2];
+      matr[2] = -view[0];
+      matr[5] = -view[1];
+      matr[8] = -view[2];
+      return normalize$2(out, fromMat3(out, matr));
+    };
+  }();
+
+  var quat = /*#__PURE__*/Object.freeze({
+    __proto__: null,
+    create: create$6,
+    identity: identity$4,
+    setAxisAngle: setAxisAngle,
+    getAxisAngle: getAxisAngle,
+    getAngle: getAngle,
+    multiply: multiply$6,
+    rotateX: rotateX$2,
+    rotateY: rotateY$2,
+    rotateZ: rotateZ$2,
+    calculateW: calculateW,
+    exp: exp,
+    ln: ln,
+    pow: pow,
+    slerp: slerp,
+    random: random$2,
+    invert: invert$4,
+    conjugate: conjugate,
+    fromMat3: fromMat3,
+    fromEuler: fromEuler,
+    str: str$6,
+    clone: clone$6,
+    fromValues: fromValues$6,
+    copy: copy$6,
+    set: set$6,
+    add: add$6,
+    mul: mul$6,
+    scale: scale$6,
+    dot: dot$2,
+    lerp: lerp$2,
+    length: length$2,
+    len: len$2,
+    squaredLength: squaredLength$2,
+    sqrLen: sqrLen$2,
+    normalize: normalize$2,
+    exactEquals: exactEquals$6,
+    equals: equals$7,
+    rotationTo: rotationTo,
+    sqlerp: sqlerp,
+    setAxes: setAxes
+  });
+
+  /**
+   * Dual Quaternion<br>
+   * Format: [real, dual]<br>
+   * Quaternion format: XYZW<br>
+   * Make sure to have normalized dual quaternions, otherwise the functions may not work as intended.<br>
+   * @module quat2
+   */
+
+  /**
+   * Creates a new identity dual quat
+   *
+   * @returns {quat2} a new dual quaternion [real -> rotation, dual -> translation]
+   */
+
+  function create$7() {
+    var dq = new ARRAY_TYPE(8);
+
+    if (ARRAY_TYPE != Float32Array) {
+      dq[0] = 0;
+      dq[1] = 0;
+      dq[2] = 0;
+      dq[4] = 0;
+      dq[5] = 0;
+      dq[6] = 0;
+      dq[7] = 0;
+    }
+
+    dq[3] = 1;
+    return dq;
+  }
+  /**
+   * Creates a new quat initialized with values from an existing quaternion
+   *
+   * @param {ReadonlyQuat2} a dual quaternion to clone
+   * @returns {quat2} new dual quaternion
+   * @function
+   */
+
+  function clone$7(a) {
+    var dq = new ARRAY_TYPE(8);
+    dq[0] = a[0];
+    dq[1] = a[1];
+    dq[2] = a[2];
+    dq[3] = a[3];
+    dq[4] = a[4];
+    dq[5] = a[5];
+    dq[6] = a[6];
+    dq[7] = a[7];
+    return dq;
+  }
+  /**
+   * Creates a new dual quat initialized with the given values
+   *
+   * @param {Number} x1 X component
+   * @param {Number} y1 Y component
+   * @param {Number} z1 Z component
+   * @param {Number} w1 W component
+   * @param {Number} x2 X component
+   * @param {Number} y2 Y component
+   * @param {Number} z2 Z component
+   * @param {Number} w2 W component
+   * @returns {quat2} new dual quaternion
+   * @function
+   */
+
+  function fromValues$7(x1, y1, z1, w1, x2, y2, z2, w2) {
+    var dq = new ARRAY_TYPE(8);
+    dq[0] = x1;
+    dq[1] = y1;
+    dq[2] = z1;
+    dq[3] = w1;
+    dq[4] = x2;
+    dq[5] = y2;
+    dq[6] = z2;
+    dq[7] = w2;
+    return dq;
+  }
+  /**
+   * Creates a new dual quat from the given values (quat and translation)
+   *
+   * @param {Number} x1 X component
+   * @param {Number} y1 Y component
+   * @param {Number} z1 Z component
+   * @param {Number} w1 W component
+   * @param {Number} x2 X component (translation)
+   * @param {Number} y2 Y component (translation)
+   * @param {Number} z2 Z component (translation)
+   * @returns {quat2} new dual quaternion
+   * @function
+   */
+
+  function fromRotationTranslationValues(x1, y1, z1, w1, x2, y2, z2) {
+    var dq = new ARRAY_TYPE(8);
+    dq[0] = x1;
+    dq[1] = y1;
+    dq[2] = z1;
+    dq[3] = w1;
+    var ax = x2 * 0.5,
+        ay = y2 * 0.5,
+        az = z2 * 0.5;
+    dq[4] = ax * w1 + ay * z1 - az * y1;
+    dq[5] = ay * w1 + az * x1 - ax * z1;
+    dq[6] = az * w1 + ax * y1 - ay * x1;
+    dq[7] = -ax * x1 - ay * y1 - az * z1;
+    return dq;
+  }
+  /**
+   * Creates a dual quat from a quaternion and a translation
+   *
+   * @param {ReadonlyQuat2} dual quaternion receiving operation result
+   * @param {ReadonlyQuat} q a normalized quaternion
+   * @param {ReadonlyVec3} t tranlation vector
+   * @returns {quat2} dual quaternion receiving operation result
+   * @function
+   */
+
+  function fromRotationTranslation$1(out, q, t) {
+    var ax = t[0] * 0.5,
+        ay = t[1] * 0.5,
+        az = t[2] * 0.5,
+        bx = q[0],
+        by = q[1],
+        bz = q[2],
+        bw = q[3];
+    out[0] = bx;
+    out[1] = by;
+    out[2] = bz;
+    out[3] = bw;
+    out[4] = ax * bw + ay * bz - az * by;
+    out[5] = ay * bw + az * bx - ax * bz;
+    out[6] = az * bw + ax * by - ay * bx;
+    out[7] = -ax * bx - ay * by - az * bz;
+    return out;
+  }
+  /**
+   * Creates a dual quat from a translation
+   *
+   * @param {ReadonlyQuat2} dual quaternion receiving operation result
+   * @param {ReadonlyVec3} t translation vector
+   * @returns {quat2} dual quaternion receiving operation result
+   * @function
+   */
+
+  function fromTranslation$3(out, t) {
+    out[0] = 0;
+    out[1] = 0;
+    out[2] = 0;
+    out[3] = 1;
+    out[4] = t[0] * 0.5;
+    out[5] = t[1] * 0.5;
+    out[6] = t[2] * 0.5;
+    out[7] = 0;
+    return out;
+  }
+  /**
+   * Creates a dual quat from a quaternion
+   *
+   * @param {ReadonlyQuat2} dual quaternion receiving operation result
+   * @param {ReadonlyQuat} q the quaternion
+   * @returns {quat2} dual quaternion receiving operation result
+   * @function
+   */
+
+  function fromRotation$4(out, q) {
+    out[0] = q[0];
+    out[1] = q[1];
+    out[2] = q[2];
+    out[3] = q[3];
+    out[4] = 0;
+    out[5] = 0;
+    out[6] = 0;
+    out[7] = 0;
+    return out;
+  }
+  /**
+   * Creates a new dual quat from a matrix (4x4)
+   *
+   * @param {quat2} out the dual quaternion
+   * @param {ReadonlyMat4} a the matrix
+   * @returns {quat2} dual quat receiving operation result
+   * @function
+   */
+
+  function fromMat4$1(out, a) {
+    //TODO Optimize this
+    var outer = create$6();
+    getRotation(outer, a);
+    var t = new ARRAY_TYPE(3);
+    getTranslation(t, a);
+    fromRotationTranslation$1(out, outer, t);
+    return out;
+  }
+  /**
+   * Copy the values from one dual quat to another
+   *
+   * @param {quat2} out the receiving dual quaternion
+   * @param {ReadonlyQuat2} a the source dual quaternion
+   * @returns {quat2} out
+   * @function
+   */
+
+  function copy$7(out, a) {
+    out[0] = a[0];
+    out[1] = a[1];
+    out[2] = a[2];
+    out[3] = a[3];
+    out[4] = a[4];
+    out[5] = a[5];
+    out[6] = a[6];
+    out[7] = a[7];
+    return out;
+  }
+  /**
+   * Set a dual quat to the identity dual quaternion
+   *
+   * @param {quat2} out the receiving quaternion
+   * @returns {quat2} out
+   */
+
+  function identity$5(out) {
+    out[0] = 0;
+    out[1] = 0;
+    out[2] = 0;
+    out[3] = 1;
+    out[4] = 0;
+    out[5] = 0;
+    out[6] = 0;
+    out[7] = 0;
+    return out;
+  }
+  /**
+   * Set the components of a dual quat to the given values
+   *
+   * @param {quat2} out the receiving quaternion
+   * @param {Number} x1 X component
+   * @param {Number} y1 Y component
+   * @param {Number} z1 Z component
+   * @param {Number} w1 W component
+   * @param {Number} x2 X component
+   * @param {Number} y2 Y component
+   * @param {Number} z2 Z component
+   * @param {Number} w2 W component
+   * @returns {quat2} out
+   * @function
+   */
+
+  function set$7(out, x1, y1, z1, w1, x2, y2, z2, w2) {
+    out[0] = x1;
+    out[1] = y1;
+    out[2] = z1;
+    out[3] = w1;
+    out[4] = x2;
+    out[5] = y2;
+    out[6] = z2;
+    out[7] = w2;
+    return out;
+  }
+  /**
+   * Gets the real part of a dual quat
+   * @param  {quat} out real part
+   * @param  {ReadonlyQuat2} a Dual Quaternion
+   * @return {quat} real part
+   */
+
+  var getReal = copy$6;
+  /**
+   * Gets the dual part of a dual quat
+   * @param  {quat} out dual part
+   * @param  {ReadonlyQuat2} a Dual Quaternion
+   * @return {quat} dual part
+   */
+
+  function getDual(out, a) {
+    out[0] = a[4];
+    out[1] = a[5];
+    out[2] = a[6];
+    out[3] = a[7];
+    return out;
+  }
+  /**
+   * Set the real component of a dual quat to the given quaternion
+   *
+   * @param {quat2} out the receiving quaternion
+   * @param {ReadonlyQuat} q a quaternion representing the real part
+   * @returns {quat2} out
+   * @function
+   */
+
+  var setReal = copy$6;
+  /**
+   * Set the dual component of a dual quat to the given quaternion
+   *
+   * @param {quat2} out the receiving quaternion
+   * @param {ReadonlyQuat} q a quaternion representing the dual part
+   * @returns {quat2} out
+   * @function
+   */
+
+  function setDual(out, q) {
+    out[4] = q[0];
+    out[5] = q[1];
+    out[6] = q[2];
+    out[7] = q[3];
+    return out;
+  }
+  /**
+   * Gets the translation of a normalized dual quat
+   * @param  {vec3} out translation
+   * @param  {ReadonlyQuat2} a Dual Quaternion to be decomposed
+   * @return {vec3} translation
+   */
+
+  function getTranslation$1(out, a) {
+    var ax = a[4],
+        ay = a[5],
+        az = a[6],
+        aw = a[7],
+        bx = -a[0],
+        by = -a[1],
+        bz = -a[2],
+        bw = a[3];
+    out[0] = (ax * bw + aw * bx + ay * bz - az * by) * 2;
+    out[1] = (ay * bw + aw * by + az * bx - ax * bz) * 2;
+    out[2] = (az * bw + aw * bz + ax * by - ay * bx) * 2;
+    return out;
+  }
+  /**
+   * Translates a dual quat by the given vector
+   *
+   * @param {quat2} out the receiving dual quaternion
+   * @param {ReadonlyQuat2} a the dual quaternion to translate
+   * @param {ReadonlyVec3} v vector to translate by
+   * @returns {quat2} out
+   */
+
+  function translate$3(out, a, v) {
+    var ax1 = a[0],
+        ay1 = a[1],
+        az1 = a[2],
+        aw1 = a[3],
+        bx1 = v[0] * 0.5,
+        by1 = v[1] * 0.5,
+        bz1 = v[2] * 0.5,
+        ax2 = a[4],
+        ay2 = a[5],
+        az2 = a[6],
+        aw2 = a[7];
+    out[0] = ax1;
+    out[1] = ay1;
+    out[2] = az1;
+    out[3] = aw1;
+    out[4] = aw1 * bx1 + ay1 * bz1 - az1 * by1 + ax2;
+    out[5] = aw1 * by1 + az1 * bx1 - ax1 * bz1 + ay2;
+    out[6] = aw1 * bz1 + ax1 * by1 - ay1 * bx1 + az2;
+    out[7] = -ax1 * bx1 - ay1 * by1 - az1 * bz1 + aw2;
+    return out;
+  }
+  /**
+   * Rotates a dual quat around the X axis
+   *
+   * @param {quat2} out the receiving dual quaternion
+   * @param {ReadonlyQuat2} a the dual quaternion to rotate
+   * @param {number} rad how far should the rotation be
+   * @returns {quat2} out
+   */
+
+  function rotateX$3(out, a, rad) {
+    var bx = -a[0],
+        by = -a[1],
+        bz = -a[2],
+        bw = a[3],
+        ax = a[4],
+        ay = a[5],
+        az = a[6],
+        aw = a[7],
+        ax1 = ax * bw + aw * bx + ay * bz - az * by,
+        ay1 = ay * bw + aw * by + az * bx - ax * bz,
+        az1 = az * bw + aw * bz + ax * by - ay * bx,
+        aw1 = aw * bw - ax * bx - ay * by - az * bz;
+    rotateX$2(out, a, rad);
+    bx = out[0];
+    by = out[1];
+    bz = out[2];
+    bw = out[3];
+    out[4] = ax1 * bw + aw1 * bx + ay1 * bz - az1 * by;
+    out[5] = ay1 * bw + aw1 * by + az1 * bx - ax1 * bz;
+    out[6] = az1 * bw + aw1 * bz + ax1 * by - ay1 * bx;
+    out[7] = aw1 * bw - ax1 * bx - ay1 * by - az1 * bz;
+    return out;
+  }
+  /**
+   * Rotates a dual quat around the Y axis
+   *
+   * @param {quat2} out the receiving dual quaternion
+   * @param {ReadonlyQuat2} a the dual quaternion to rotate
+   * @param {number} rad how far should the rotation be
+   * @returns {quat2} out
+   */
+
+  function rotateY$3(out, a, rad) {
+    var bx = -a[0],
+        by = -a[1],
+        bz = -a[2],
+        bw = a[3],
+        ax = a[4],
+        ay = a[5],
+        az = a[6],
+        aw = a[7],
+        ax1 = ax * bw + aw * bx + ay * bz - az * by,
+        ay1 = ay * bw + aw * by + az * bx - ax * bz,
+        az1 = az * bw + aw * bz + ax * by - ay * bx,
+        aw1 = aw * bw - ax * bx - ay * by - az * bz;
+    rotateY$2(out, a, rad);
+    bx = out[0];
+    by = out[1];
+    bz = out[2];
+    bw = out[3];
+    out[4] = ax1 * bw + aw1 * bx + ay1 * bz - az1 * by;
+    out[5] = ay1 * bw + aw1 * by + az1 * bx - ax1 * bz;
+    out[6] = az1 * bw + aw1 * bz + ax1 * by - ay1 * bx;
+    out[7] = aw1 * bw - ax1 * bx - ay1 * by - az1 * bz;
+    return out;
+  }
+  /**
+   * Rotates a dual quat around the Z axis
+   *
+   * @param {quat2} out the receiving dual quaternion
+   * @param {ReadonlyQuat2} a the dual quaternion to rotate
+   * @param {number} rad how far should the rotation be
+   * @returns {quat2} out
+   */
+
+  function rotateZ$3(out, a, rad) {
+    var bx = -a[0],
+        by = -a[1],
+        bz = -a[2],
+        bw = a[3],
+        ax = a[4],
+        ay = a[5],
+        az = a[6],
+        aw = a[7],
+        ax1 = ax * bw + aw * bx + ay * bz - az * by,
+        ay1 = ay * bw + aw * by + az * bx - ax * bz,
+        az1 = az * bw + aw * bz + ax * by - ay * bx,
+        aw1 = aw * bw - ax * bx - ay * by - az * bz;
+    rotateZ$2(out, a, rad);
+    bx = out[0];
+    by = out[1];
+    bz = out[2];
+    bw = out[3];
+    out[4] = ax1 * bw + aw1 * bx + ay1 * bz - az1 * by;
+    out[5] = ay1 * bw + aw1 * by + az1 * bx - ax1 * bz;
+    out[6] = az1 * bw + aw1 * bz + ax1 * by - ay1 * bx;
+    out[7] = aw1 * bw - ax1 * bx - ay1 * by - az1 * bz;
+    return out;
+  }
+  /**
+   * Rotates a dual quat by a given quaternion (a * q)
+   *
+   * @param {quat2} out the receiving dual quaternion
+   * @param {ReadonlyQuat2} a the dual quaternion to rotate
+   * @param {ReadonlyQuat} q quaternion to rotate by
+   * @returns {quat2} out
+   */
+
+  function rotateByQuatAppend(out, a, q) {
+    var qx = q[0],
+        qy = q[1],
+        qz = q[2],
+        qw = q[3],
+        ax = a[0],
+        ay = a[1],
+        az = a[2],
+        aw = a[3];
+    out[0] = ax * qw + aw * qx + ay * qz - az * qy;
+    out[1] = ay * qw + aw * qy + az * qx - ax * qz;
+    out[2] = az * qw + aw * qz + ax * qy - ay * qx;
+    out[3] = aw * qw - ax * qx - ay * qy - az * qz;
+    ax = a[4];
+    ay = a[5];
+    az = a[6];
+    aw = a[7];
+    out[4] = ax * qw + aw * qx + ay * qz - az * qy;
+    out[5] = ay * qw + aw * qy + az * qx - ax * qz;
+    out[6] = az * qw + aw * qz + ax * qy - ay * qx;
+    out[7] = aw * qw - ax * qx - ay * qy - az * qz;
+    return out;
+  }
+  /**
+   * Rotates a dual quat by a given quaternion (q * a)
+   *
+   * @param {quat2} out the receiving dual quaternion
+   * @param {ReadonlyQuat} q quaternion to rotate by
+   * @param {ReadonlyQuat2} a the dual quaternion to rotate
+   * @returns {quat2} out
+   */
+
+  function rotateByQuatPrepend(out, q, a) {
+    var qx = q[0],
+        qy = q[1],
+        qz = q[2],
+        qw = q[3],
+        bx = a[0],
+        by = a[1],
+        bz = a[2],
+        bw = a[3];
+    out[0] = qx * bw + qw * bx + qy * bz - qz * by;
+    out[1] = qy * bw + qw * by + qz * bx - qx * bz;
+    out[2] = qz * bw + qw * bz + qx * by - qy * bx;
+    out[3] = qw * bw - qx * bx - qy * by - qz * bz;
+    bx = a[4];
+    by = a[5];
+    bz = a[6];
+    bw = a[7];
+    out[4] = qx * bw + qw * bx + qy * bz - qz * by;
+    out[5] = qy * bw + qw * by + qz * bx - qx * bz;
+    out[6] = qz * bw + qw * bz + qx * by - qy * bx;
+    out[7] = qw * bw - qx * bx - qy * by - qz * bz;
+    return out;
+  }
+  /**
+   * Rotates a dual quat around a given axis. Does the normalisation automatically
+   *
+   * @param {quat2} out the receiving dual quaternion
+   * @param {ReadonlyQuat2} a the dual quaternion to rotate
+   * @param {ReadonlyVec3} axis the axis to rotate around
+   * @param {Number} rad how far the rotation should be
+   * @returns {quat2} out
+   */
+
+  function rotateAroundAxis(out, a, axis, rad) {
+    //Special case for rad = 0
+    if (Math.abs(rad) < EPSILON) {
+      return copy$7(out, a);
+    }
+
+    var axisLength = Math.hypot(axis[0], axis[1], axis[2]);
+    rad = rad * 0.5;
+    var s = Math.sin(rad);
+    var bx = s * axis[0] / axisLength;
+    var by = s * axis[1] / axisLength;
+    var bz = s * axis[2] / axisLength;
+    var bw = Math.cos(rad);
+    var ax1 = a[0],
+        ay1 = a[1],
+        az1 = a[2],
+        aw1 = a[3];
+    out[0] = ax1 * bw + aw1 * bx + ay1 * bz - az1 * by;
+    out[1] = ay1 * bw + aw1 * by + az1 * bx - ax1 * bz;
+    out[2] = az1 * bw + aw1 * bz + ax1 * by - ay1 * bx;
+    out[3] = aw1 * bw - ax1 * bx - ay1 * by - az1 * bz;
+    var ax = a[4],
+        ay = a[5],
+        az = a[6],
+        aw = a[7];
+    out[4] = ax * bw + aw * bx + ay * bz - az * by;
+    out[5] = ay * bw + aw * by + az * bx - ax * bz;
+    out[6] = az * bw + aw * bz + ax * by - ay * bx;
+    out[7] = aw * bw - ax * bx - ay * by - az * bz;
+    return out;
+  }
+  /**
+   * Adds two dual quat's
+   *
+   * @param {quat2} out the receiving dual quaternion
+   * @param {ReadonlyQuat2} a the first operand
+   * @param {ReadonlyQuat2} b the second operand
+   * @returns {quat2} out
+   * @function
+   */
+
+  function add$7(out, a, b) {
+    out[0] = a[0] + b[0];
+    out[1] = a[1] + b[1];
+    out[2] = a[2] + b[2];
+    out[3] = a[3] + b[3];
+    out[4] = a[4] + b[4];
+    out[5] = a[5] + b[5];
+    out[6] = a[6] + b[6];
+    out[7] = a[7] + b[7];
+    return out;
+  }
+  /**
+   * Multiplies two dual quat's
+   *
+   * @param {quat2} out the receiving dual quaternion
+   * @param {ReadonlyQuat2} a the first operand
+   * @param {ReadonlyQuat2} b the second operand
+   * @returns {quat2} out
+   */
+
+  function multiply$7(out, a, b) {
+    var ax0 = a[0],
+        ay0 = a[1],
+        az0 = a[2],
+        aw0 = a[3],
+        bx1 = b[4],
+        by1 = b[5],
+        bz1 = b[6],
+        bw1 = b[7],
+        ax1 = a[4],
+        ay1 = a[5],
+        az1 = a[6],
+        aw1 = a[7],
+        bx0 = b[0],
+        by0 = b[1],
+        bz0 = b[2],
+        bw0 = b[3];
+    out[0] = ax0 * bw0 + aw0 * bx0 + ay0 * bz0 - az0 * by0;
+    out[1] = ay0 * bw0 + aw0 * by0 + az0 * bx0 - ax0 * bz0;
+    out[2] = az0 * bw0 + aw0 * bz0 + ax0 * by0 - ay0 * bx0;
+    out[3] = aw0 * bw0 - ax0 * bx0 - ay0 * by0 - az0 * bz0;
+    out[4] = ax0 * bw1 + aw0 * bx1 + ay0 * bz1 - az0 * by1 + ax1 * bw0 + aw1 * bx0 + ay1 * bz0 - az1 * by0;
+    out[5] = ay0 * bw1 + aw0 * by1 + az0 * bx1 - ax0 * bz1 + ay1 * bw0 + aw1 * by0 + az1 * bx0 - ax1 * bz0;
+    out[6] = az0 * bw1 + aw0 * bz1 + ax0 * by1 - ay0 * bx1 + az1 * bw0 + aw1 * bz0 + ax1 * by0 - ay1 * bx0;
+    out[7] = aw0 * bw1 - ax0 * bx1 - ay0 * by1 - az0 * bz1 + aw1 * bw0 - ax1 * bx0 - ay1 * by0 - az1 * bz0;
+    return out;
+  }
+  /**
+   * Alias for {@link quat2.multiply}
+   * @function
+   */
+
+  var mul$7 = multiply$7;
+  /**
+   * Scales a dual quat by a scalar number
+   *
+   * @param {quat2} out the receiving dual quat
+   * @param {ReadonlyQuat2} a the dual quat to scale
+   * @param {Number} b amount to scale the dual quat by
+   * @returns {quat2} out
+   * @function
+   */
+
+  function scale$7(out, a, b) {
+    out[0] = a[0] * b;
+    out[1] = a[1] * b;
+    out[2] = a[2] * b;
+    out[3] = a[3] * b;
+    out[4] = a[4] * b;
+    out[5] = a[5] * b;
+    out[6] = a[6] * b;
+    out[7] = a[7] * b;
+    return out;
+  }
+  /**
+   * Calculates the dot product of two dual quat's (The dot product of the real parts)
+   *
+   * @param {ReadonlyQuat2} a the first operand
+   * @param {ReadonlyQuat2} b the second operand
+   * @returns {Number} dot product of a and b
+   * @function
+   */
+
+  var dot$3 = dot$2;
+  /**
+   * Performs a linear interpolation between two dual quats's
+   * NOTE: The resulting dual quaternions won't always be normalized (The error is most noticeable when t = 0.5)
+   *
+   * @param {quat2} out the receiving dual quat
+   * @param {ReadonlyQuat2} a the first operand
+   * @param {ReadonlyQuat2} b the second operand
+   * @param {Number} t interpolation amount, in the range [0-1], between the two inputs
+   * @returns {quat2} out
+   */
+
+  function lerp$3(out, a, b, t) {
+    var mt = 1 - t;
+    if (dot$3(a, b) < 0) t = -t;
+    out[0] = a[0] * mt + b[0] * t;
+    out[1] = a[1] * mt + b[1] * t;
+    out[2] = a[2] * mt + b[2] * t;
+    out[3] = a[3] * mt + b[3] * t;
+    out[4] = a[4] * mt + b[4] * t;
+    out[5] = a[5] * mt + b[5] * t;
+    out[6] = a[6] * mt + b[6] * t;
+    out[7] = a[7] * mt + b[7] * t;
+    return out;
+  }
+  /**
+   * Calculates the inverse of a dual quat. If they are normalized, conjugate is cheaper
+   *
+   * @param {quat2} out the receiving dual quaternion
+   * @param {ReadonlyQuat2} a dual quat to calculate inverse of
+   * @returns {quat2} out
+   */
+
+  function invert$5(out, a) {
+    var sqlen = squaredLength$3(a);
+    out[0] = -a[0] / sqlen;
+    out[1] = -a[1] / sqlen;
+    out[2] = -a[2] / sqlen;
+    out[3] = a[3] / sqlen;
+    out[4] = -a[4] / sqlen;
+    out[5] = -a[5] / sqlen;
+    out[6] = -a[6] / sqlen;
+    out[7] = a[7] / sqlen;
+    return out;
+  }
+  /**
+   * Calculates the conjugate of a dual quat
+   * If the dual quaternion is normalized, this function is faster than quat2.inverse and produces the same result.
+   *
+   * @param {quat2} out the receiving quaternion
+   * @param {ReadonlyQuat2} a quat to calculate conjugate of
+   * @returns {quat2} out
+   */
+
+  function conjugate$1(out, a) {
+    out[0] = -a[0];
+    out[1] = -a[1];
+    out[2] = -a[2];
+    out[3] = a[3];
+    out[4] = -a[4];
+    out[5] = -a[5];
+    out[6] = -a[6];
+    out[7] = a[7];
+    return out;
+  }
+  /**
+   * Calculates the length of a dual quat
+   *
+   * @param {ReadonlyQuat2} a dual quat to calculate length of
+   * @returns {Number} length of a
+   * @function
+   */
+
+  var length$3 = length$2;
+  /**
+   * Alias for {@link quat2.length}
+   * @function
+   */
+
+  var len$3 = length$3;
+  /**
+   * Calculates the squared length of a dual quat
+   *
+   * @param {ReadonlyQuat2} a dual quat to calculate squared length of
+   * @returns {Number} squared length of a
+   * @function
+   */
+
+  var squaredLength$3 = squaredLength$2;
+  /**
+   * Alias for {@link quat2.squaredLength}
+   * @function
+   */
+
+  var sqrLen$3 = squaredLength$3;
+  /**
+   * Normalize a dual quat
+   *
+   * @param {quat2} out the receiving dual quaternion
+   * @param {ReadonlyQuat2} a dual quaternion to normalize
+   * @returns {quat2} out
+   * @function
+   */
+
+  function normalize$3(out, a) {
+    var magnitude = squaredLength$3(a);
+
+    if (magnitude > 0) {
+      magnitude = Math.sqrt(magnitude);
+      var a0 = a[0] / magnitude;
+      var a1 = a[1] / magnitude;
+      var a2 = a[2] / magnitude;
+      var a3 = a[3] / magnitude;
+      var b0 = a[4];
+      var b1 = a[5];
+      var b2 = a[6];
+      var b3 = a[7];
+      var a_dot_b = a0 * b0 + a1 * b1 + a2 * b2 + a3 * b3;
+      out[0] = a0;
+      out[1] = a1;
+      out[2] = a2;
+      out[3] = a3;
+      out[4] = (b0 - a0 * a_dot_b) / magnitude;
+      out[5] = (b1 - a1 * a_dot_b) / magnitude;
+      out[6] = (b2 - a2 * a_dot_b) / magnitude;
+      out[7] = (b3 - a3 * a_dot_b) / magnitude;
+    }
+
+    return out;
+  }
+  /**
+   * Returns a string representation of a dual quatenion
+   *
+   * @param {ReadonlyQuat2} a dual quaternion to represent as a string
+   * @returns {String} string representation of the dual quat
+   */
+
+  function str$7(a) {
+    return "quat2(" + a[0] + ", " + a[1] + ", " + a[2] + ", " + a[3] + ", " + a[4] + ", " + a[5] + ", " + a[6] + ", " + a[7] + ")";
+  }
+  /**
+   * Returns whether or not the dual quaternions have exactly the same elements in the same position (when compared with ===)
+   *
+   * @param {ReadonlyQuat2} a the first dual quaternion.
+   * @param {ReadonlyQuat2} b the second dual quaternion.
+   * @returns {Boolean} true if the dual quaternions are equal, false otherwise.
+   */
+
+  function exactEquals$7(a, b) {
+    return a[0] === b[0] && a[1] === b[1] && a[2] === b[2] && a[3] === b[3] && a[4] === b[4] && a[5] === b[5] && a[6] === b[6] && a[7] === b[7];
+  }
+  /**
+   * Returns whether or not the dual quaternions have approximately the same elements in the same position.
+   *
+   * @param {ReadonlyQuat2} a the first dual quat.
+   * @param {ReadonlyQuat2} b the second dual quat.
+   * @returns {Boolean} true if the dual quats are equal, false otherwise.
+   */
+
+  function equals$8(a, b) {
+    var a0 = a[0],
+        a1 = a[1],
+        a2 = a[2],
+        a3 = a[3],
+        a4 = a[4],
+        a5 = a[5],
+        a6 = a[6],
+        a7 = a[7];
+    var b0 = b[0],
+        b1 = b[1],
+        b2 = b[2],
+        b3 = b[3],
+        b4 = b[4],
+        b5 = b[5],
+        b6 = b[6],
+        b7 = b[7];
+    return Math.abs(a0 - b0) <= EPSILON * Math.max(1.0, Math.abs(a0), Math.abs(b0)) && Math.abs(a1 - b1) <= EPSILON * Math.max(1.0, Math.abs(a1), Math.abs(b1)) && Math.abs(a2 - b2) <= EPSILON * Math.max(1.0, Math.abs(a2), Math.abs(b2)) && Math.abs(a3 - b3) <= EPSILON * Math.max(1.0, Math.abs(a3), Math.abs(b3)) && Math.abs(a4 - b4) <= EPSILON * Math.max(1.0, Math.abs(a4), Math.abs(b4)) && Math.abs(a5 - b5) <= EPSILON * Math.max(1.0, Math.abs(a5), Math.abs(b5)) && Math.abs(a6 - b6) <= EPSILON * Math.max(1.0, Math.abs(a6), Math.abs(b6)) && Math.abs(a7 - b7) <= EPSILON * Math.max(1.0, Math.abs(a7), Math.abs(b7));
+  }
+
+  var quat2 = /*#__PURE__*/Object.freeze({
+    __proto__: null,
+    create: create$7,
+    clone: clone$7,
+    fromValues: fromValues$7,
+    fromRotationTranslationValues: fromRotationTranslationValues,
+    fromRotationTranslation: fromRotationTranslation$1,
+    fromTranslation: fromTranslation$3,
+    fromRotation: fromRotation$4,
+    fromMat4: fromMat4$1,
+    copy: copy$7,
+    identity: identity$5,
+    set: set$7,
+    getReal: getReal,
+    getDual: getDual,
+    setReal: setReal,
+    setDual: setDual,
+    getTranslation: getTranslation$1,
+    translate: translate$3,
+    rotateX: rotateX$3,
+    rotateY: rotateY$3,
+    rotateZ: rotateZ$3,
+    rotateByQuatAppend: rotateByQuatAppend,
+    rotateByQuatPrepend: rotateByQuatPrepend,
+    rotateAroundAxis: rotateAroundAxis,
+    add: add$7,
+    multiply: multiply$7,
+    mul: mul$7,
+    scale: scale$7,
+    dot: dot$3,
+    lerp: lerp$3,
+    invert: invert$5,
+    conjugate: conjugate$1,
+    length: length$3,
+    len: len$3,
+    squaredLength: squaredLength$3,
+    sqrLen: sqrLen$3,
+    normalize: normalize$3,
+    str: str$7,
+    exactEquals: exactEquals$7,
+    equals: equals$8
+  });
+
+  /**
+   * 2 Dimensional Vector
+   * @module vec2
+   */
+
+  /**
+   * Creates a new, empty vec2
+   *
+   * @returns {vec2} a new 2D vector
+   */
+
+  function create$8() {
+    var out = new ARRAY_TYPE(2);
+
+    if (ARRAY_TYPE != Float32Array) {
+      out[0] = 0;
+      out[1] = 0;
+    }
+
+    return out;
+  }
+  /**
+   * Creates a new vec2 initialized with values from an existing vector
+   *
+   * @param {ReadonlyVec2} a vector to clone
+   * @returns {vec2} a new 2D vector
+   */
+
+  function clone$8(a) {
+    var out = new ARRAY_TYPE(2);
+    out[0] = a[0];
+    out[1] = a[1];
+    return out;
+  }
+  /**
+   * Creates a new vec2 initialized with the given values
+   *
+   * @param {Number} x X component
+   * @param {Number} y Y component
+   * @returns {vec2} a new 2D vector
+   */
+
+  function fromValues$8(x, y) {
+    var out = new ARRAY_TYPE(2);
+    out[0] = x;
+    out[1] = y;
+    return out;
+  }
+  /**
+   * Copy the values from one vec2 to another
+   *
+   * @param {vec2} out the receiving vector
+   * @param {ReadonlyVec2} a the source vector
+   * @returns {vec2} out
+   */
+
+  function copy$8(out, a) {
+    out[0] = a[0];
+    out[1] = a[1];
+    return out;
+  }
+  /**
+   * Set the components of a vec2 to the given values
+   *
+   * @param {vec2} out the receiving vector
+   * @param {Number} x X component
+   * @param {Number} y Y component
+   * @returns {vec2} out
+   */
+
+  function set$8(out, x, y) {
+    out[0] = x;
+    out[1] = y;
+    return out;
+  }
+  /**
+   * Adds two vec2's
+   *
+   * @param {vec2} out the receiving vector
+   * @param {ReadonlyVec2} a the first operand
+   * @param {ReadonlyVec2} b the second operand
+   * @returns {vec2} out
+   */
+
+  function add$8(out, a, b) {
+    out[0] = a[0] + b[0];
+    out[1] = a[1] + b[1];
+    return out;
+  }
+  /**
+   * Subtracts vector b from vector a
+   *
+   * @param {vec2} out the receiving vector
+   * @param {ReadonlyVec2} a the first operand
+   * @param {ReadonlyVec2} b the second operand
+   * @returns {vec2} out
+   */
+
+  function subtract$6(out, a, b) {
+    out[0] = a[0] - b[0];
+    out[1] = a[1] - b[1];
+    return out;
+  }
+  /**
+   * Multiplies two vec2's
+   *
+   * @param {vec2} out the receiving vector
+   * @param {ReadonlyVec2} a the first operand
+   * @param {ReadonlyVec2} b the second operand
+   * @returns {vec2} out
+   */
+
+  function multiply$8(out, a, b) {
+    out[0] = a[0] * b[0];
+    out[1] = a[1] * b[1];
+    return out;
+  }
+  /**
+   * Divides two vec2's
+   *
+   * @param {vec2} out the receiving vector
+   * @param {ReadonlyVec2} a the first operand
+   * @param {ReadonlyVec2} b the second operand
+   * @returns {vec2} out
+   */
+
+  function divide$2(out, a, b) {
+    out[0] = a[0] / b[0];
+    out[1] = a[1] / b[1];
+    return out;
+  }
+  /**
+   * Math.ceil the components of a vec2
+   *
+   * @param {vec2} out the receiving vector
+   * @param {ReadonlyVec2} a vector to ceil
+   * @returns {vec2} out
+   */
+
+  function ceil$2(out, a) {
+    out[0] = Math.ceil(a[0]);
+    out[1] = Math.ceil(a[1]);
+    return out;
+  }
+  /**
+   * Math.floor the components of a vec2
+   *
+   * @param {vec2} out the receiving vector
+   * @param {ReadonlyVec2} a vector to floor
+   * @returns {vec2} out
+   */
+
+  function floor$2(out, a) {
+    out[0] = Math.floor(a[0]);
+    out[1] = Math.floor(a[1]);
+    return out;
+  }
+  /**
+   * Returns the minimum of two vec2's
+   *
+   * @param {vec2} out the receiving vector
+   * @param {ReadonlyVec2} a the first operand
+   * @param {ReadonlyVec2} b the second operand
+   * @returns {vec2} out
+   */
+
+  function min$2(out, a, b) {
+    out[0] = Math.min(a[0], b[0]);
+    out[1] = Math.min(a[1], b[1]);
+    return out;
+  }
+  /**
+   * Returns the maximum of two vec2's
+   *
+   * @param {vec2} out the receiving vector
+   * @param {ReadonlyVec2} a the first operand
+   * @param {ReadonlyVec2} b the second operand
+   * @returns {vec2} out
+   */
+
+  function max$2(out, a, b) {
+    out[0] = Math.max(a[0], b[0]);
+    out[1] = Math.max(a[1], b[1]);
+    return out;
+  }
+  /**
+   * Math.round the components of a vec2
+   *
+   * @param {vec2} out the receiving vector
+   * @param {ReadonlyVec2} a vector to round
+   * @returns {vec2} out
+   */
+
+  function round$2(out, a) {
+    out[0] = Math.round(a[0]);
+    out[1] = Math.round(a[1]);
+    return out;
+  }
+  /**
+   * Scales a vec2 by a scalar number
+   *
+   * @param {vec2} out the receiving vector
+   * @param {ReadonlyVec2} a the vector to scale
+   * @param {Number} b amount to scale the vector by
+   * @returns {vec2} out
+   */
+
+  function scale$8(out, a, b) {
+    out[0] = a[0] * b;
+    out[1] = a[1] * b;
+    return out;
+  }
+  /**
+   * Adds two vec2's after scaling the second operand by a scalar value
+   *
+   * @param {vec2} out the receiving vector
+   * @param {ReadonlyVec2} a the first operand
+   * @param {ReadonlyVec2} b the second operand
+   * @param {Number} scale the amount to scale b by before adding
+   * @returns {vec2} out
+   */
+
+  function scaleAndAdd$2(out, a, b, scale) {
+    out[0] = a[0] + b[0] * scale;
+    out[1] = a[1] + b[1] * scale;
+    return out;
+  }
+  /**
+   * Calculates the euclidian distance between two vec2's
+   *
+   * @param {ReadonlyVec2} a the first operand
+   * @param {ReadonlyVec2} b the second operand
+   * @returns {Number} distance between a and b
+   */
+
+  function distance$2(a, b) {
+    var x = b[0] - a[0],
+        y = b[1] - a[1];
+    return Math.hypot(x, y);
+  }
+  /**
+   * Calculates the squared euclidian distance between two vec2's
+   *
+   * @param {ReadonlyVec2} a the first operand
+   * @param {ReadonlyVec2} b the second operand
+   * @returns {Number} squared distance between a and b
+   */
+
+  function squaredDistance$2(a, b) {
+    var x = b[0] - a[0],
+        y = b[1] - a[1];
+    return x * x + y * y;
+  }
+  /**
+   * Calculates the length of a vec2
+   *
+   * @param {ReadonlyVec2} a vector to calculate length of
+   * @returns {Number} length of a
+   */
+
+  function length$4(a) {
+    var x = a[0],
+        y = a[1];
+    return Math.hypot(x, y);
+  }
+  /**
+   * Calculates the squared length of a vec2
+   *
+   * @param {ReadonlyVec2} a vector to calculate squared length of
+   * @returns {Number} squared length of a
+   */
+
+  function squaredLength$4(a) {
+    var x = a[0],
+        y = a[1];
+    return x * x + y * y;
+  }
+  /**
+   * Negates the components of a vec2
+   *
+   * @param {vec2} out the receiving vector
+   * @param {ReadonlyVec2} a vector to negate
+   * @returns {vec2} out
+   */
+
+  function negate$2(out, a) {
+    out[0] = -a[0];
+    out[1] = -a[1];
+    return out;
+  }
+  /**
+   * Returns the inverse of the components of a vec2
+   *
+   * @param {vec2} out the receiving vector
+   * @param {ReadonlyVec2} a vector to invert
+   * @returns {vec2} out
+   */
+
+  function inverse$2(out, a) {
+    out[0] = 1.0 / a[0];
+    out[1] = 1.0 / a[1];
+    return out;
+  }
+  /**
+   * Normalize a vec2
+   *
+   * @param {vec2} out the receiving vector
+   * @param {ReadonlyVec2} a vector to normalize
+   * @returns {vec2} out
+   */
+
+  function normalize$4(out, a) {
+    var x = a[0],
+        y = a[1];
+    var len = x * x + y * y;
+
+    if (len > 0) {
+      //TODO: evaluate use of glm_invsqrt here?
+      len = 1 / Math.sqrt(len);
+    }
+
+    out[0] = a[0] * len;
+    out[1] = a[1] * len;
+    return out;
+  }
+  /**
+   * Calculates the dot product of two vec2's
+   *
+   * @param {ReadonlyVec2} a the first operand
+   * @param {ReadonlyVec2} b the second operand
+   * @returns {Number} dot product of a and b
+   */
+
+  function dot$4(a, b) {
+    return a[0] * b[0] + a[1] * b[1];
+  }
+  /**
+   * Computes the cross product of two vec2's
+   * Note that the cross product must by definition produce a 3D vector
+   *
+   * @param {vec3} out the receiving vector
+   * @param {ReadonlyVec2} a the first operand
+   * @param {ReadonlyVec2} b the second operand
+   * @returns {vec3} out
+   */
+
+  function cross$2(out, a, b) {
+    var z = a[0] * b[1] - a[1] * b[0];
+    out[0] = out[1] = 0;
+    out[2] = z;
+    return out;
+  }
+  /**
+   * Performs a linear interpolation between two vec2's
+   *
+   * @param {vec2} out the receiving vector
+   * @param {ReadonlyVec2} a the first operand
+   * @param {ReadonlyVec2} b the second operand
+   * @param {Number} t interpolation amount, in the range [0-1], between the two inputs
+   * @returns {vec2} out
+   */
+
+  function lerp$4(out, a, b, t) {
+    var ax = a[0],
+        ay = a[1];
+    out[0] = ax + t * (b[0] - ax);
+    out[1] = ay + t * (b[1] - ay);
+    return out;
+  }
+  /**
+   * Generates a random vector with the given scale
+   *
+   * @param {vec2} out the receiving vector
+   * @param {Number} [scale] Length of the resulting vector. If ommitted, a unit vector will be returned
+   * @returns {vec2} out
+   */
+
+  function random$3(out, scale) {
+    scale = scale || 1.0;
+    var r = RANDOM() * 2.0 * Math.PI;
+    out[0] = Math.cos(r) * scale;
+    out[1] = Math.sin(r) * scale;
+    return out;
+  }
+  /**
+   * Transforms the vec2 with a mat2
+   *
+   * @param {vec2} out the receiving vector
+   * @param {ReadonlyVec2} a the vector to transform
+   * @param {ReadonlyMat2} m matrix to transform with
+   * @returns {vec2} out
+   */
+
+  function transformMat2(out, a, m) {
+    var x = a[0],
+        y = a[1];
+    out[0] = m[0] * x + m[2] * y;
+    out[1] = m[1] * x + m[3] * y;
+    return out;
+  }
+  /**
+   * Transforms the vec2 with a mat2d
+   *
+   * @param {vec2} out the receiving vector
+   * @param {ReadonlyVec2} a the vector to transform
+   * @param {ReadonlyMat2d} m matrix to transform with
+   * @returns {vec2} out
+   */
+
+  function transformMat2d(out, a, m) {
+    var x = a[0],
+        y = a[1];
+    out[0] = m[0] * x + m[2] * y + m[4];
+    out[1] = m[1] * x + m[3] * y + m[5];
+    return out;
+  }
+  /**
+   * Transforms the vec2 with a mat3
+   * 3rd vector component is implicitly '1'
+   *
+   * @param {vec2} out the receiving vector
+   * @param {ReadonlyVec2} a the vector to transform
+   * @param {ReadonlyMat3} m matrix to transform with
+   * @returns {vec2} out
+   */
+
+  function transformMat3$1(out, a, m) {
+    var x = a[0],
+        y = a[1];
+    out[0] = m[0] * x + m[3] * y + m[6];
+    out[1] = m[1] * x + m[4] * y + m[7];
+    return out;
+  }
+  /**
+   * Transforms the vec2 with a mat4
+   * 3rd vector component is implicitly '0'
+   * 4th vector component is implicitly '1'
+   *
+   * @param {vec2} out the receiving vector
+   * @param {ReadonlyVec2} a the vector to transform
+   * @param {ReadonlyMat4} m matrix to transform with
+   * @returns {vec2} out
+   */
+
+  function transformMat4$2(out, a, m) {
+    var x = a[0];
+    var y = a[1];
+    out[0] = m[0] * x + m[4] * y + m[12];
+    out[1] = m[1] * x + m[5] * y + m[13];
+    return out;
+  }
+  /**
+   * Rotate a 2D vector
+   * @param {vec2} out The receiving vec2
+   * @param {ReadonlyVec2} a The vec2 point to rotate
+   * @param {ReadonlyVec2} b The origin of the rotation
+   * @param {Number} rad The angle of rotation in radians
+   * @returns {vec2} out
+   */
+
+  function rotate$4(out, a, b, rad) {
+    //Translate point to the origin
+    var p0 = a[0] - b[0],
+        p1 = a[1] - b[1],
+        sinC = Math.sin(rad),
+        cosC = Math.cos(rad); //perform rotation and translate to correct position
+
+    out[0] = p0 * cosC - p1 * sinC + b[0];
+    out[1] = p0 * sinC + p1 * cosC + b[1];
+    return out;
+  }
+  /**
+   * Get the angle between two 2D vectors
+   * @param {ReadonlyVec2} a The first operand
+   * @param {ReadonlyVec2} b The second operand
+   * @returns {Number} The angle in radians
+   */
+
+  function angle$1(a, b) {
+    var x1 = a[0],
+        y1 = a[1],
+        x2 = b[0],
+        y2 = b[1],
+        // mag is the product of the magnitudes of a and b
+    mag = Math.sqrt(x1 * x1 + y1 * y1) * Math.sqrt(x2 * x2 + y2 * y2),
+        // mag &&.. short circuits if mag == 0
+    cosine = mag && (x1 * x2 + y1 * y2) / mag; // Math.min(Math.max(cosine, -1), 1) clamps the cosine between -1 and 1
+
+    return Math.acos(Math.min(Math.max(cosine, -1), 1));
+  }
+  /**
+   * Set the components of a vec2 to zero
+   *
+   * @param {vec2} out the receiving vector
+   * @returns {vec2} out
+   */
+
+  function zero$2(out) {
+    out[0] = 0.0;
+    out[1] = 0.0;
+    return out;
+  }
+  /**
+   * Returns a string representation of a vector
+   *
+   * @param {ReadonlyVec2} a vector to represent as a string
+   * @returns {String} string representation of the vector
+   */
+
+  function str$8(a) {
+    return "vec2(" + a[0] + ", " + a[1] + ")";
+  }
+  /**
+   * Returns whether or not the vectors exactly have the same elements in the same position (when compared with ===)
+   *
+   * @param {ReadonlyVec2} a The first vector.
+   * @param {ReadonlyVec2} b The second vector.
+   * @returns {Boolean} True if the vectors are equal, false otherwise.
+   */
+
+  function exactEquals$8(a, b) {
+    return a[0] === b[0] && a[1] === b[1];
+  }
+  /**
+   * Returns whether or not the vectors have approximately the same elements in the same position.
+   *
+   * @param {ReadonlyVec2} a The first vector.
+   * @param {ReadonlyVec2} b The second vector.
+   * @returns {Boolean} True if the vectors are equal, false otherwise.
+   */
+
+  function equals$9(a, b) {
+    var a0 = a[0],
+        a1 = a[1];
+    var b0 = b[0],
+        b1 = b[1];
+    return Math.abs(a0 - b0) <= EPSILON * Math.max(1.0, Math.abs(a0), Math.abs(b0)) && Math.abs(a1 - b1) <= EPSILON * Math.max(1.0, Math.abs(a1), Math.abs(b1));
+  }
+  /**
+   * Alias for {@link vec2.length}
+   * @function
+   */
+
+  var len$4 = length$4;
+  /**
+   * Alias for {@link vec2.subtract}
+   * @function
+   */
+
+  var sub$6 = subtract$6;
+  /**
+   * Alias for {@link vec2.multiply}
+   * @function
+   */
+
+  var mul$8 = multiply$8;
+  /**
+   * Alias for {@link vec2.divide}
+   * @function
+   */
+
+  var div$2 = divide$2;
+  /**
+   * Alias for {@link vec2.distance}
+   * @function
+   */
+
+  var dist$2 = distance$2;
+  /**
+   * Alias for {@link vec2.squaredDistance}
+   * @function
+   */
+
+  var sqrDist$2 = squaredDistance$2;
+  /**
+   * Alias for {@link vec2.squaredLength}
+   * @function
+   */
+
+  var sqrLen$4 = squaredLength$4;
+  /**
+   * Perform some operation over an array of vec2s.
+   *
+   * @param {Array} a the array of vectors to iterate over
+   * @param {Number} stride Number of elements between the start of each vec2. If 0 assumes tightly packed
+   * @param {Number} offset Number of elements to skip at the beginning of the array
+   * @param {Number} count Number of vec2s to iterate over. If 0 iterates over entire array
+   * @param {Function} fn Function to call for each vector in the array
+   * @param {Object} [arg] additional argument to pass to fn
+   * @returns {Array} a
+   * @function
+   */
+
+  var forEach$2 = function () {
+    var vec = create$8();
+    return function (a, stride, offset, count, fn, arg) {
+      var i, l;
+
+      if (!stride) {
+        stride = 2;
+      }
+
+      if (!offset) {
+        offset = 0;
+      }
+
+      if (count) {
+        l = Math.min(count * stride + offset, a.length);
+      } else {
+        l = a.length;
+      }
+
+      for (i = offset; i < l; i += stride) {
+        vec[0] = a[i];
+        vec[1] = a[i + 1];
+        fn(vec, vec, arg);
+        a[i] = vec[0];
+        a[i + 1] = vec[1];
+      }
+
+      return a;
+    };
+  }();
+
+  var vec2 = /*#__PURE__*/Object.freeze({
+    __proto__: null,
+    create: create$8,
+    clone: clone$8,
+    fromValues: fromValues$8,
+    copy: copy$8,
+    set: set$8,
+    add: add$8,
+    subtract: subtract$6,
+    multiply: multiply$8,
+    divide: divide$2,
+    ceil: ceil$2,
+    floor: floor$2,
+    min: min$2,
+    max: max$2,
+    round: round$2,
+    scale: scale$8,
+    scaleAndAdd: scaleAndAdd$2,
+    distance: distance$2,
+    squaredDistance: squaredDistance$2,
+    length: length$4,
+    squaredLength: squaredLength$4,
+    negate: negate$2,
+    inverse: inverse$2,
+    normalize: normalize$4,
+    dot: dot$4,
+    cross: cross$2,
+    lerp: lerp$4,
+    random: random$3,
+    transformMat2: transformMat2,
+    transformMat2d: transformMat2d,
+    transformMat3: transformMat3$1,
+    transformMat4: transformMat4$2,
+    rotate: rotate$4,
+    angle: angle$1,
+    zero: zero$2,
+    str: str$8,
+    exactEquals: exactEquals$8,
+    equals: equals$9,
+    len: len$4,
+    sub: sub$6,
+    mul: mul$8,
+    div: div$2,
+    dist: dist$2,
+    sqrDist: sqrDist$2,
+    sqrLen: sqrLen$4,
+    forEach: forEach$2
+  });
+
+  exports.glMatrix = common;
+  exports.mat2 = mat2;
+  exports.mat2d = mat2d;
+  exports.mat3 = mat3;
+  exports.mat4 = mat4;
+  exports.quat = quat;
+  exports.quat2 = quat2;
+  exports.vec2 = vec2;
+  exports.vec3 = vec3;
+  exports.vec4 = vec4;
+
+  Object.defineProperty(exports, '__esModule', { value: true });
+
+})));
diff --git a/img/FragmentShader.png b/img/FragmentShader.png
new file mode 100644
index 0000000000000000000000000000000000000000..52a0a5d5da21d01d9a5fb71b5e2a910ed4469329
GIT binary patch
literal 12026
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
S={f@g1B0ilpUXO@geCxbhnC&|

literal 0
HcmV?d00001

diff --git a/img/InitializeGL.png b/img/InitializeGL.png
new file mode 100644
index 0000000000000000000000000000000000000000..8687c942874655fa7b176d16d0423741b31ab8a8
GIT binary patch
literal 26383
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

literal 0
HcmV?d00001

diff --git a/img/VertexShader.png b/img/VertexShader.png
new file mode 100644
index 0000000000000000000000000000000000000000..33144945b2cf70730e1515b353f0c5de2834a958
GIT binary patch
literal 14815
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
T=QaI1L8CFAu6{1-oD!M<Ez>$*

literal 0
HcmV?d00001

diff --git a/index.html b/index.html
new file mode 100644
index 0000000..d128ecb
--- /dev/null
+++ b/index.html
@@ -0,0 +1,71 @@
+<html>
+
+<head>
+<title>WebGL1.0 to WebGL2.0</title>
+<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
+<script type="text/javascript" src="gl-matrix.js"> </script>
+<script type="text/javascript" src="Script.js"> </script>
+
+</head>
+
+<body onload="main()">
+    <div>
+        <header>
+            <h1>WebGL Tutorial</h1>
+            <h2>How to change WebGL1-based documents to WebGL2</h2>
+        </header>
+    </div>
+
+    <canvas id="helloapicanvas" style="border: none;" width="600" height="600"></canvas>
+	
+	<br/> <br/>
+<button onclick="toggleAnimation()">Toggle Animation</button>
+<button onclick="speed_plus()"> + </button>
+<button onclick="speed_minus()"> - </button>
+<button onclick="draw_mode_change(2)"> Lines </button>
+<button onclick="draw_mode_change(4)"> Triangles </button>
+<button onclick="draw_mode_change(0)"> Points </button>
+
+<hr>
+<div>
+    <h3>Description</h3>
+    <div>This document is to describe the basic method of changing the WebGL1-based document to WebGL2.</div>
+    <div>WebGL2 is nearly 100% back compatible with WebGL1. In this document, there will be description of very basic way to change the WebGL1 script to WebGL2.</div>
+    <div>Very brief comments are here, so read the source code to see the details.</div>
+</div>
+<hr>
+<div>
+    <h3>Initializing</h3>
+    <div>First, you use "webgl2" instead of "webgl", on initializing canvas.</div>
+    <div>Note that there is no "experimental-webgl2". Many WebGL1 extensions are a standard part of WebGL2.</div>
+</div>
+<hr>
+<div>
+    <h3>Switching the Shader Version to GLSL 3.00 ES</h3>
+    <div>Add "#version 300 es" to the very first line of the shader source code.</div>
+    <div>CAUTION: NO BLANKS AND LINES ARE ALLOWED BEFORE THIS DECLARATION.</div>
+</div>
+<hr>
+<div>
+    <h3>Some Changes in Shader Code</h3>
+    <div>Add "#version 300 es" to the very first line of the shader source code.</div>
+    <div>CAUTION: NO BLANKS AND LINES ARE ALLOWED BEFORE THIS DECLARATION.</div>
+    <br>
+    <div>No more gl_fragColor. In WebGL1, your fragment shader would set the specific variable "gl_fragColor" to set the output value.</div>
+    <div>You can now use any name for the output, except for those starting with "gl_".</div>
+    <br>
+    <div>Change "varying" to "in" or "out", and "attribute" to "in".</div>
+    <div>The output of the vertex shader would be the input of the fragment shader. We call them "varying" values.</div>
+    <div>Simply change them to "out" in the vertex shader, and "in" in the fragment shader.</div>
+</div>
+<hr>
+<div>
+    <h3>References</h3>
+    <div>This page is modified from Lab 06 of the Computer Graphics course.</div>
+    <div>Additional information is from WebGL2 Fundamentals.</div>
+    <div> <a href="https://webgl2fundamentals.org/webgl/lessons/webgl1-to-webgl2.html">WebGL1 to WebGL2</a> / <a href="https://webgl2fundamentals.org/webgl/lessons/ko/webgl-fundamentals.html">WebGL2 Fundamentals</a></div>
+</div>
+
+</body>
+
+</html>
-- 
GitLab