diff --git "a/student2019/201220913/\354\273\264\352\267\270-\352\270\260\353\247\220 \355\224\204\353\241\234\354\240\235\355\212\270 \353\263\264\352\263\240\354\204\234.docx" b/student2019/201220913/report.docx
similarity index 100%
rename from "student2019/201220913/\354\273\264\352\267\270-\352\270\260\353\247\220 \355\224\204\353\241\234\354\240\235\355\212\270 \353\263\264\352\263\240\354\204\234.docx"
rename to student2019/201220913/report.docx
diff --git a/student2019/201421044/Final_Project b/student2019/201421044/Final_Project
deleted file mode 160000
index e21815064c18d90d4dafcec5abdf71e95a5f2589..0000000000000000000000000000000000000000
--- a/student2019/201421044/Final_Project
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit e21815064c18d90d4dafcec5abdf71e95a5f2589
diff --git a/student2019/201421044/README.md b/student2019/201421044/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..f1f3031c0de4f5ea48a75811c0d3ff01ae620c5d
--- /dev/null
+++ b/student2019/201421044/README.md
@@ -0,0 +1,65 @@
+Hidden Surface Removal이란?
+
+우리는 눈에서 가까운 물체 뒤에 있는 물체를 볼 수 없다.
+
+예를 들어 어떠한 상자가 존재할 때, 우리가 상자의 앞면을 보고 있다면,
+
+동시에 뒷면을 볼 수 없다. 사실적인 이미지 화면을 얻기 위해서는 물체의 숨겨진 면을 제거해야 한다.
+
+이러한 숨겨진 면을 식별하고 제거하는 것을 Hidden Surface Removal이라고 한다.
+
+
+ WebGL의 경우 Hidden Surface Removal을 구현하기 위해 Z-buffering을 사용한다.
+ 
+ Z-Buffering이란 3차원 이미지를 화면에 그릴 때,
+ 
+ Z-Buffer에 저장된 각 픽셀의 깊이 정보를 비교하여,
+ 
+ 어떤 물체를 그려야 할 지, 어떤 물체를 그리지 말아야 할 지 결정하는 알고리즘이다. 
+ 
+ 
+ 이때 주의해야 할 점은, Z-buffer는 각 오브젝트별로 z 값을 저장하는 것이 아니라, 픽셀별로 z 값을 저장한다.
+ 
+ 처음 물체가 그려질 때에는 해당 물체가 차지하는 픽셀에, 각 필셀 마다 물체의 Z값을 Z버퍼에 저장하고, 물체를 그린다. 
+ 
+ 이후에 또 다른 물체가 그려 져야할 상황이 생기면,
+ 
+ 그 물체가 차지하는 각 픽셀에 해당하는 Z버퍼값과 새로 그려 져야할 물체의 해당 픽셀의 Z값과 비교한 후,
+ 
+사용자와 더 가까이 있는 물체를 그리고 그 값을 Z버퍼에 저장한다. 
+
+ 
+만약 사각형과 삼각형의 Z값이 모두 평행을 이룬다면,  사각형과 삼각형이 교차할 때,
+
+아래와 같이 삼각형과 사각형의  위아래가 한순간에 뒤바뀐다.
+
+![image01](./image/image01.jpeg)
+
+하지만 해당 페이지의 경우, 삼각형의 한 쪽 꼭지점을 사용자로부터 멀리 떨어뜨려 놓았기 때문에, 
+
+삼각형과 사각형이 교차할 때, 아래와 같이 사용자로부터 멀리 떨어진 지점의 픽셀부터 서서히 감추어 지는 것을  볼 수 있다. 
+
+![image02](./image/image02.jpeg)
+
+이는 Z-Buffer가 z값을 저장할 때, 오브젝트 전체의 z값을 저장하는 것이 아니라, 
+
+오브젝트가 차지하는 각 픽셀마다 z값을 저장하고 비교한다는 것을 의미한다.
+
+주제 선정 이유: 필자가 처음 Hidden Surface Removal을 공부할 때에는, Z-buffer가 픽셀이 아니라,
+
+오브젝트마다 z값을 저장하여 비교한다고 생각해서 매우 혼란을 겪었다.
+
+처음 WebGL을 공부하는 사람들이 나와 같은 실수를 범하지 않았으면 하는 마음에서 이와 같은 주제를 선정하였다. 
+
+
+해당 페이지의 기능: 삼각형과 정사각형의 z값을 변경하여 두 오브젝트가 교차할 때 어떤 반응이 일어나는 지를 보여준다.
+
+삼각형의 한 쪽 모서리의 z값을 사용자로부터 멀리 떨어뜨려 놓았기 때문에,
+
+삼각형과 정사각형이 교차할 때 이 부분부터 점차적으로 사라지는 모습을 보여줌으로써,
+
+z-buffer에 z값이 저장될 때에는 object가 아닌 픽셀 별로 데이터가 저장된다는 것을 직관적으로 보여준다.
+
+또한 Depth test 기능을 on/off 할 수 있는 버튼을 추가하여, Depth Test기능을 사용하지 않으면,
+
+그리지 말아야 할 부분도 그리게 되어 사용자에게 혼란을 가져다 줄 수 있다는 점을 보여주고 싶었다.
diff --git a/student2019/201421044/Readme.docx b/student2019/201421044/Readme.docx
new file mode 100644
index 0000000000000000000000000000000000000000..cc5b3219c13f85c4cc1773c8755318583306871d
Binary files /dev/null and b/student2019/201421044/Readme.docx differ
diff --git a/student2019/201421044/gl-matrix.js b/student2019/201421044/gl-matrix.js
new file mode 100644
index 0000000000000000000000000000000000000000..45ac91d38cf1317e802be2fe96fde99e00c68397
--- /dev/null
+++ b/student2019/201421044/gl-matrix.js
@@ -0,0 +1,7546 @@
+
+/*!
+@fileoverview gl-matrix - High performance matrix and vector operations
+@author Brandon Jones
+@author Colin MacKenzie IV
+@version 3.0.0
+
+Copyright (c) 2015-2019, 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 {Type} 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({
+    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 {mat2} 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 {mat2} 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 {mat2} 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 {mat2} 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 {mat2} 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 {mat2} 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 {mat2} a the first operand
+   * @param {mat2} 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 {mat2} 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 {mat2} a the matrix to rotate
+   * @param {vec2} 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 {vec2} 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 {mat2} 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 {mat2} 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 {mat2} L the lower triangular matrix
+   * @param {mat2} D the diagonal matrix
+   * @param {mat2} U the upper triangular matrix
+   * @param {mat2} 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 {mat2} a the first operand
+   * @param {mat2} 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 {mat2} a the first operand
+   * @param {mat2} 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 {mat2} a The first matrix.
+   * @param {mat2} 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 {mat2} a The first matrix.
+   * @param {mat2} 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 {mat2} 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 {mat2} a the first operand
+   * @param {mat2} 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({
+    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, c, tx,
+   *  b, d, ty]
+   * </pre>
+   * This is a short form for the 3x3 matrix:
+   * <pre>
+   * [a, c, tx,
+   *  b, d, ty,
+   *  0, 0, 1]
+   * </pre>
+   * The last row 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 {mat2d} 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 {mat2d} 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 {mat2d} 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 {mat2d} 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 {mat2d} a the first operand
+   * @param {mat2d} 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 {mat2d} 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 {mat2d} a the matrix to translate
+   * @param {vec2} 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 {mat2d} a the matrix to translate
+   * @param {vec2} 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 {vec2} 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 {vec2} 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 {mat2d} 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 {mat2d} 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 {mat2d} a the first operand
+   * @param {mat2d} 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 {mat2d} a the first operand
+   * @param {mat2d} 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 {mat2d} 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 {mat2d} a the first operand
+   * @param {mat2d} 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 {mat2d} a The first matrix.
+   * @param {mat2d} 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 {mat2d} a The first matrix.
+   * @param {mat2d} 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({
+    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 {mat4} 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 {mat3} 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 {mat3} 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 {mat3} 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 {mat3} 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 {mat3} 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 {mat3} 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 {mat3} a the first operand
+   * @param {mat3} 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 {mat3} a the matrix to translate
+   * @param {vec2} 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 {mat3} 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 {mat3} a the matrix to rotate
+   * @param {vec2} 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 {vec2} 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 {vec2} 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 {mat2d} 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 {quat} 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 {mat4} 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 {mat3} 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 {mat3} 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 {mat3} a the first operand
+   * @param {mat3} 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 {mat3} a the first operand
+   * @param {mat3} 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 {mat3} 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 {mat3} a the first operand
+   * @param {mat3} 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 {mat3} a The first matrix.
+   * @param {mat3} 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 {mat3} a The first matrix.
+   * @param {mat3} 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({
+    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 {mat4} 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 {mat4} 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 {mat4} 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 {mat4} 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 {mat4} 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 {mat4} 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 {mat4} a the first operand
+   * @param {mat4} 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 {mat4} a the matrix to translate
+   * @param {vec3} 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 {mat4} a the matrix to scale
+   * @param {vec3} 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 {mat4} a the matrix to rotate
+   * @param {Number} rad the angle to rotate the matrix by
+   * @param {vec3} 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 {mat4} 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 {mat4} 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 {mat4} 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 {vec3} 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 {vec3} 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 {vec3} 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 {vec3} 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 {quat2} 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  {mat4} 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  {mat4} 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 {mat4} 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 {vec3} v Translation vector
+   * @param {vec3} 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 {vec3} v Translation vector
+   * @param {vec3} s Scaling vector
+   * @param {vec3} 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 {quat} 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 {vec3} eye Position of the viewer
+   * @param {vec3} center Point the viewer is looking at
+   * @param {vec3} 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 {vec3} eye Position of the viewer
+   * @param {vec3} center Point the viewer is looking at
+   * @param {vec3} 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 {mat4} 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 {mat4} a the matrix to calculate Frobenius norm of
+   * @returns {Number} Frobenius norm
+   */
+
+  function frob$3(a) {
+    return Math.hypot(a[0], a[1], 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 {mat4} a the first operand
+   * @param {mat4} 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 {mat4} a the first operand
+   * @param {mat4} 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 {mat4} 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 {mat4} a the first operand
+   * @param {mat4} 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 {mat4} a The first matrix.
+   * @param {mat4} 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 {mat4} a The first matrix.
+   * @param {mat4} 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({
+    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 {vec3} 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 {vec3} 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 {vec3} 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 {vec3} a the first operand
+   * @param {vec3} 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 {vec3} a the first operand
+   * @param {vec3} 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 {vec3} a the first operand
+   * @param {vec3} 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 {vec3} a the first operand
+   * @param {vec3} 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 {vec3} 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 {vec3} 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 {vec3} a the first operand
+   * @param {vec3} 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 {vec3} a the first operand
+   * @param {vec3} 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 {vec3} 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 {vec3} 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 {vec3} a the first operand
+   * @param {vec3} 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 {vec3} a the first operand
+   * @param {vec3} 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 {vec3} a the first operand
+   * @param {vec3} 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 {vec3} 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 {vec3} 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 {vec3} 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 {vec3} 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 {vec3} a the first operand
+   * @param {vec3} 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 {vec3} a the first operand
+   * @param {vec3} 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 {vec3} a the first operand
+   * @param {vec3} 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 {vec3} a the first operand
+   * @param {vec3} b the second operand
+   * @param {vec3} c the third operand
+   * @param {vec3} 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 {vec3} a the first operand
+   * @param {vec3} b the second operand
+   * @param {vec3} c the third operand
+   * @param {vec3} 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 {vec3} a the vector to transform
+   * @param {mat4} 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 {vec3} a the vector to transform
+   * @param {mat3} 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 {vec3} a the vector to transform
+   * @param {quat} 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 {vec3} a The vec3 point to rotate
+   * @param {vec3} b The origin of the rotation
+   * @param {Number} c The angle of rotation
+   * @returns {vec3} out
+   */
+
+  function rotateX$1(out, a, b, c) {
+    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(c) - p[2] * Math.sin(c);
+    r[2] = p[1] * Math.sin(c) + p[2] * Math.cos(c); //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 {vec3} a The vec3 point to rotate
+   * @param {vec3} b The origin of the rotation
+   * @param {Number} c The angle of rotation
+   * @returns {vec3} out
+   */
+
+  function rotateY$1(out, a, b, c) {
+    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(c) + p[0] * Math.cos(c);
+    r[1] = p[1];
+    r[2] = p[2] * Math.cos(c) - p[0] * Math.sin(c); //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 {vec3} a The vec3 point to rotate
+   * @param {vec3} b The origin of the rotation
+   * @param {Number} c The angle of rotation
+   * @returns {vec3} out
+   */
+
+  function rotateZ$1(out, a, b, c) {
+    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(c) - p[1] * Math.sin(c);
+    r[1] = p[0] * Math.sin(c) + p[1] * Math.cos(c);
+    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 {vec3} a The first operand
+   * @param {vec3} b The second operand
+   * @returns {Number} The angle in radians
+   */
+
+  function angle(a, b) {
+    var tempA = fromValues$4(a[0], a[1], a[2]);
+    var tempB = fromValues$4(b[0], b[1], b[2]);
+    normalize(tempA, tempA);
+    normalize(tempB, tempB);
+    var cosine = dot(tempA, tempB);
+
+    if (cosine > 1.0) {
+      return 0;
+    } else if (cosine < -1.0) {
+      return Math.PI;
+    } else {
+      return Math.acos(cosine);
+    }
+  }
+  /**
+   * 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 {vec3} 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 {vec3} a The first vector.
+   * @param {vec3} 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 {vec3} a The first vector.
+   * @param {vec3} 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({
+    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 {vec4} 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 {vec4} 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 {vec4} a the first operand
+   * @param {vec4} 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 {vec4} a the first operand
+   * @param {vec4} 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 {vec4} a the first operand
+   * @param {vec4} 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 {vec4} a the first operand
+   * @param {vec4} 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 {vec4} 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 {vec4} 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 {vec4} a the first operand
+   * @param {vec4} 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 {vec4} a the first operand
+   * @param {vec4} 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 {vec4} 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 {vec4} 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 {vec4} a the first operand
+   * @param {vec4} 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 {vec4} a the first operand
+   * @param {vec4} 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 {vec4} a the first operand
+   * @param {vec4} 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 {vec4} 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 {vec4} 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 {vec4} 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 {vec4} 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 {vec4} 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 {vec4} a the first operand
+   * @param {vec4} 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 {vec4} result the receiving vector
+   * @param {vec4} U the first vector
+   * @param {vec4} V the second vector
+   * @param {vec4} 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 {vec4} a the first operand
+   * @param {vec4} 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 {vec4} a the vector to transform
+   * @param {mat4} 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 {vec4} a the vector to transform
+   * @param {quat} 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 {vec4} 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 {vec4} a The first vector.
+   * @param {vec4} 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 {vec4} a The first vector.
+   * @param {vec4} 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({
+    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 {vec3} 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  {quat} 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;
+  }
+  /**
+   * Multiplies two quat's
+   *
+   * @param {quat} out the receiving quaternion
+   * @param {quat} a the first operand
+   * @param {quat} 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 {quat} 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 {quat} 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 {quat} 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 {quat} 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;
+  }
+  /**
+   * Performs a spherical linear interpolation between two quat
+   *
+   * @param {quat} out the receiving quaternion
+   * @param {quat} a the first operand
+   * @param {quat} 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 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 {quat} 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 {quat} 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 {mat3} 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 {quat} 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 {quat} 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 {quat} 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 {quat} a the first operand
+   * @param {quat} 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 {quat} 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 {quat} a the first operand
+   * @param {quat} 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 {quat} a the first operand
+   * @param {quat} 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 {quat} 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 {quat} 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 {quat} 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 {quat} a The first quaternion.
+   * @param {quat} 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 {quat} a The first vector.
+   * @param {quat} 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 {vec3} a the initial vector
+   * @param {vec3} 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 {quat} a the first operand
+   * @param {quat} b the second operand
+   * @param {quat} c the third operand
+   * @param {quat} 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 {vec3} view  the vector representing the viewing direction
+   * @param {vec3} right the vector representing the local "right" direction
+   * @param {vec3} 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({
+    create: create$6,
+    identity: identity$4,
+    setAxisAngle: setAxisAngle,
+    getAxisAngle: getAxisAngle,
+    multiply: multiply$6,
+    rotateX: rotateX$2,
+    rotateY: rotateY$2,
+    rotateZ: rotateZ$2,
+    calculateW: calculateW,
+    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 {quat2} 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 {quat2} dual quaternion receiving operation result
+   * @param {quat} q a normalized quaternion
+   * @param {vec3} 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 {quat2} dual quaternion receiving operation result
+   * @param {vec3} 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 {quat2} dual quaternion receiving operation result
+   * @param {quat} 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 {mat4} 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 {quat2} 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  {quat2} 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  {quat2} 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 {quat} 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 {quat} 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  {quat2} 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 {quat2} a the dual quaternion to translate
+   * @param {vec3} 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 {quat2} 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 {quat2} 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 {quat2} 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 {quat2} a the dual quaternion to rotate
+   * @param {quat} 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 {quat} q quaternion to rotate by
+   * @param {quat2} 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 {quat2} a the dual quaternion to rotate
+   * @param {vec3} 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 {quat2} a the first operand
+   * @param {quat2} 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 {quat2} a the first operand
+   * @param {quat2} 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 {quat2} 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 {quat2} a the first operand
+   * @param {quat2} 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 {quat2} a the first operand
+   * @param {quat2} 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 {quat2} 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 {quat2} 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 {quat2} 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 {quat2} 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 {quat2} 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 {quat2} 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 {quat2} a the first dual quaternion.
+   * @param {quat2} 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 {quat2} a the first dual quat.
+   * @param {quat2} 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({
+    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 {vec2} 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 {vec2} 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 {vec2} a the first operand
+   * @param {vec2} 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 {vec2} a the first operand
+   * @param {vec2} 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 {vec2} a the first operand
+   * @param {vec2} 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 {vec2} a the first operand
+   * @param {vec2} 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 {vec2} 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 {vec2} 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 {vec2} a the first operand
+   * @param {vec2} 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 {vec2} a the first operand
+   * @param {vec2} 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 {vec2} 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 {vec2} 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 {vec2} a the first operand
+   * @param {vec2} 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 {vec2} a the first operand
+   * @param {vec2} 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 {vec2} a the first operand
+   * @param {vec2} 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 {vec2} 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 {vec2} 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 {vec2} 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 {vec2} 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 {vec2} 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 {vec2} a the first operand
+   * @param {vec2} 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 {vec2} a the first operand
+   * @param {vec2} 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 {vec2} a the first operand
+   * @param {vec2} 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 {vec2} a the vector to transform
+   * @param {mat2} 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 {vec2} a the vector to transform
+   * @param {mat2d} 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 {vec2} a the vector to transform
+   * @param {mat3} 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 {vec2} a the vector to transform
+   * @param {mat4} 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 {vec2} a The vec2 point to rotate
+   * @param {vec2} b The origin of the rotation
+   * @param {Number} c The angle of rotation
+   * @returns {vec2} out
+   */
+
+  function rotate$4(out, a, b, c) {
+    //Translate point to the origin
+    var p0 = a[0] - b[0],
+        p1 = a[1] - b[1],
+        sinC = Math.sin(c),
+        cosC = Math.cos(c); //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 {vec2} a The first operand
+   * @param {vec2} 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];
+    var len1 = x1 * x1 + y1 * y1;
+
+    if (len1 > 0) {
+      //TODO: evaluate use of glm_invsqrt here?
+      len1 = 1 / Math.sqrt(len1);
+    }
+
+    var len2 = x2 * x2 + y2 * y2;
+
+    if (len2 > 0) {
+      //TODO: evaluate use of glm_invsqrt here?
+      len2 = 1 / Math.sqrt(len2);
+    }
+
+    var cosine = (x1 * x2 + y1 * y2) * len1 * len2;
+
+    if (cosine > 1.0) {
+      return 0;
+    } else if (cosine < -1.0) {
+      return Math.PI;
+    } else {
+      return Math.acos(cosine);
+    }
+  }
+  /**
+   * 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 {vec2} 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 {vec2} a The first vector.
+   * @param {vec2} 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 {vec2} a The first vector.
+   * @param {vec2} 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({
+    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/student2019/201421044/trCube.html b/student2019/201421044/trCube.html
new file mode 100644
index 0000000000000000000000000000000000000000..d2d804cc112ee97565b5a43847835104f1d4919e
--- /dev/null
+++ b/student2019/201421044/trCube.html
@@ -0,0 +1,64 @@
+<!--(CC-NC-BY)LEEDONGHEON2019-->
+<html>
+<head>
+<title>Ajou-CG</title>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<script src="gl-matrix.js"></script>
+<script type="text/javascript" src="trCube
+.js">
+</script>
+<style>
+        #test_btn1{
+            border-top-left-radius: 5px;
+            border-bottom-left-radius: 5px;
+            margin-right:-4px;
+        }
+        #test_btn2{
+            border-top-right-radius: 5px;
+            border-bottom-right-radius: 5px;    
+            margin-left:-3px;
+        }
+        #btn_group button{
+            border: 1px solid skyblue;
+            background-color: rgba(0,0,0,0);
+            color: skyblue;
+            padding: 5px;
+        }
+        #btn_group button:hover{
+            color:white;
+            background-color: skyblue;
+        }
+    </style>
+</head>
+
+<body onload="main()">
+	<p style="font-size:30px";>
+	Hidden Surface Removal - Z Buffer
+	</p><br>
+	
+    <canvas id="helloapicanvas" style="border: none;" width="400" height="400"></canvas>
+	<br>
+	Z-buffer stores Z-values per pixel, not an object.<br>
+	Thus, when the triangle and square intersect,<Br>
+	the pixel of triangle which is farther from User is hidden faster<br><br>
+	<div id="btn_group">
+	
+		<!-Increase square's z-value by 0.1 -->
+        <button onclick="sq_trZinc()" style="width: 150px; height: 30px;">Square Z + 0.1</button>
+		
+		<!-Decrease square's z-value by 0.1 -->
+		<button onclick="sq_trZdec()" style="width: 150px; height: 30px;">Sqaure Z - 0.1</button><br>
+		
+		<!-Increase Triangle's z-value by 0.1 -->
+		<button onclick="tri_trZinc()" style="width: 150px; height: 30px;">Triangle Z + 0.1</button>
+		
+		<!-Decrease square's z-value by 0.1 -->
+		<button onclick="tri_trZdec()" style="width: 150px; height: 30px;">Triangle Z - 0.1</button><br>
+		
+		<!-Toggle Depth test method-->
+		<button onclick="toggle_depthtest()" style="width: 150px; height: 30px;">ON/OFF Depth test</button>
+    </div>
+
+</body>
+
+</html>
diff --git a/student2019/201421044/trCube.js b/student2019/201421044/trCube.js
new file mode 100644
index 0000000000000000000000000000000000000000..889d25088881898b624a1c11b20ea87f562ba3b9
--- /dev/null
+++ b/student2019/201421044/trCube.js
@@ -0,0 +1,377 @@
+//(CC-NC-BY)LEEDONGHEON2019
+var gl;
+
+function testGLError(functionLastCalled) {
+
+    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");
+        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;
+
+function initialiseBuffer() {
+		
+    var vertexData = [
+		//Initialize the vertex of square
+		-0.5, -0.5, 0.3,	1.0, 1.0, 1.0, 1.0,		0.0, 1.0,//3
+        0.5, -0.5, 0.3,		1.0, 1.0, 1.0, 1.0,		1.0, 1.0,//1
+		-0.5, 0.5, 0.3,		1.0, 1.0, 1.0, 1.0,		1.0, 1.0,//2
+				
+		-0.5, 0.5, 0.3,		1.0, 1.0, 1.0, 1.0,		0.0, 1.0,//3
+		0.5, -0.5, 0.3,		1.0, 1.0, 1.0, 1.0,		1.0, 1.0,//2
+		0.5, 0.5, 0.3,	    1.0, 1.0, 1.0, 1.0,		0.0, 1.0,//4
+		//Initiallize the vertex of Triangle
+		-0.6, -0.3, 0.6,	0.3, 0.0, 0.2, 0.1,		0.0, 1.0,//3
+        0.6, -0.3, 0.6,		0.3, 0.0, 0.2, 0.1,		1.0, 1.0,//1
+		0.0, 0.6, 0.3,		0.3, 0.0, 0.2, 0.1,		1.0, 1.0,//2
+    ];
+    // Generate a buffer object
+    gl.vertexBuffer = gl.createBuffer();
+    // Bind buffer as a vertex buffer so we can fill it with data
+    gl.bindBuffer(gl.ARRAY_BUFFER, gl.vertexBuffer);
+    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertexData), gl.STATIC_DRAW);
+    return testGLError("initialiseBuffers");
+}
+
+function initialiseShaders() {
+
+    var fragmentShaderSource = '\
+			varying mediump vec4 color; \
+			void main(void) \
+			{ \
+				gl_FragColor = 1.0 * color;\
+			}';
+
+    gl.fragShader = gl.createShader(gl.FRAGMENT_SHADER);
+    gl.shaderSource(gl.fragShader, fragmentShaderSource);
+    gl.compileShader(gl.fragShader);
+    if (!gl.getShaderParameter(gl.fragShader, gl.COMPILE_STATUS)) {
+        alert("Failed to compile the fragment shader.\n" + gl.getShaderInfoLog(gl.fragShader));
+        return false;
+    }
+
+    var vertexShaderSource = '\
+			attribute highp vec3 myVertex; \
+			attribute highp vec4 myColor; \
+			attribute highp vec2 myUV; \
+			uniform mediump mat4 Pmatrix; \
+			uniform mediump mat4 Vmatrix; \
+			uniform mediump mat4 Mmatrix; \
+			varying mediump vec4 color; \
+			varying mediump vec2 texCoord;\
+			void main(void)  \
+			{ \
+				gl_Position = Pmatrix*Vmatrix*Mmatrix*vec4(myVertex, 1.0);\
+				color = myColor;\
+				texCoord = myUV; \
+			}';
+
+    gl.vertexShader = gl.createShader(gl.VERTEX_SHADER);
+    gl.shaderSource(gl.vertexShader, vertexShaderSource);
+    gl.compileShader(gl.vertexShader);
+    if (!gl.getShaderParameter(gl.vertexShader, gl.COMPILE_STATUS)) {
+        alert("Failed to compile the vertex shader.\n" + gl.getShaderInfoLog(gl.vertexShader));
+        return false;
+    }
+
+    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");
+	gl.bindAttribLocation(gl.programObject, 2, "myUV");
+
+    // Link the program
+    gl.linkProgram(gl.programObject);
+
+    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");
+}
+
+// FOV, Aspect Ratio, Near, Far 
+function get_projection(angle, a, zMin, zMax) {
+    var ang = Math.tan((angle*.5)*Math.PI/180);//angle*.5
+    return [
+    	0.5/ang, 0 , 0, 0,
+        0, 0.5*a/ang, 0, 0,
+        0, 0, -(zMax+zMin)/(zMax-zMin), -1,
+        0, 0, (-2*zMax*zMin)/(zMax-zMin), 0 ];
+}
+			
+var proj_matrix = get_projection(30, 1.0, 1, 5.0);
+var mov_matrix = [1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1];
+var view_matrix = [1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1];
+// translating z
+
+view_matrix[14] = view_matrix[14]-2;//zoom
+
+
+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;
+  }
+
+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;
+  }
+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;
+  }
+  
+toggle=2; //Initialize the value of 'toggle' which used in 'toggle_depthtest()'
+
+//Variables for used in Translation.
+sq_transX = 0.0;
+sq_transZ= 0.0;
+sq_transY=0.0;
+tri_transX=0.0;
+tri_transY=0.0;
+tri_transZ=0.0;
+
+frames = 1;
+//Vector variables for used in Transalation.
+var trans_sq_vec;
+var trans_tri_vec;
+
+//method for toggle the variable-'toggle' to use 'ON/OFF DEPTH_TEST' button.
+function toggle_depthtest(){
+	if(toggle==1){
+		toggle=0;
+		return;
+	}
+	else {
+		toggle=1;
+		return;
+	}	
+}
+
+
+function sq_trZinc(){
+	sq_transZ+=0.01;
+		
+}
+function sq_trZdec(){
+	sq_transZ-=0.01;
+		
+}
+function tri_trZinc(){
+	tri_transZ+=0.01;
+		
+}
+function tri_trZdec(){
+	tri_transZ-=0.01;
+		
+}
+
+function renderScene() {
+
+    //console.log("Frame "+frames+"\n");
+    frames += 1 ;
+	rotAxis = [1,1,0];
+
+    var Pmatrix = gl.getUniformLocation(gl.programObject, "Pmatrix");
+    var Vmatrix = gl.getUniformLocation(gl.programObject, "Vmatrix");
+    var Mmatrix = gl.getUniformLocation(gl.programObject, "Mmatrix");
+	
+
+    gl.uniformMatrix4fv(Pmatrix, false, proj_matrix);
+    gl.uniformMatrix4fv(Vmatrix, false, view_matrix);
+    
+
+    if (!testGLError("gl.uniformMatrix4fv")) {
+        return false;
+    }
+
+    gl.enableVertexAttribArray(0);
+    gl.vertexAttribPointer(0, 3, gl.FLOAT, gl.FALSE, 36, 0);
+    gl.enableVertexAttribArray(1);
+    gl.vertexAttribPointer(1, 4, gl.FLOAT, gl.FALSE, 36, 12);
+	gl.enableVertexAttribArray(2);
+    gl.vertexAttribPointer(2, 2, gl.FLOAT, gl.FALSE, 36, 28);
+
+
+    if (!testGLError("gl.vertexAttribPointer")) {
+        return false;
+    }
+	if(toggle==2){
+		gl.enable(gl.DEPTH_TEST);
+		toggle=1;
+	}
+	else if(toggle==1){
+		gl.enable(gl.DEPTH_TEST);
+	}
+	else if(!toggle){
+		gl.disable(gl.DEPTH_TEST);
+	}
+    
+    gl.depthFunc(gl.LEQUAL); 
+	// gl.enable(gl.CULL_FACE);
+	// gl.enable(gl.BLEND); 
+
+    gl.clearColor(1.0, 0.8, 0.8, 1.0);
+    gl.clearDepth(1.0); 
+    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
+	
+	//DrawSquare
+    identity$3(mov_matrix); 
+	trans_sq_vec=[sq_transX,sq_transY,sq_transZ];
+	fromTranslation$2(mov_matrix,trans_sq_vec);
+	gl.uniformMatrix4fv(Mmatrix, false, mov_matrix);
+	gl.drawArrays(gl.TRIANGLES, 0, 6);
+	mov_matrix_child=mov_matrix.slice();
+
+	//DrawTriangle
+	trans_tri_vec=[tri_transX,tri_transY,tri_transZ];
+	fromTranslation$2(mov_matrix,trans_tri_vec);
+	gl.uniformMatrix4fv(Mmatrix, false, mov_matrix);
+	gl.drawArrays(gl.TRIANGLES, 6, 3);
+
+	
+  
+    if (!testGLError("gl.drawArrays")) {
+        return false;
+    }
+
+    return true;
+}
+
+function main() {
+    var canvas = document.getElementById("helloapicanvas");
+    console.log("Start");
+
+    if (!initialiseGL(canvas)) {
+        return;
+    }
+
+    if (!initialiseBuffer()) {
+        return;
+    }
+
+    if (!initialiseShaders()) {
+        return;
+    }
+
+    // Render loop
+    requestAnimFrame = (
+	function () {
+        //	return window.requestAnimationFrame || window.webkitRequestAnimationFrame 
+	//	|| window.mozRequestAnimationFrame || 
+	   	return function (callback) {
+			    // console.log("Callback is"+callback); 
+			    window.setTimeout(callback, 10, 10); };
+    })();
+
+    (function renderLoop(param) {
+        if (renderScene()) {
+            // Everything was successful, request that we redraw our scene again in the future
+            requestAnimFrame(renderLoop);
+        }
+    })();
+}
diff --git a/student2019/201421109/201421109_phong_shading.html b/student2019/201421109/201421109_phong_shading.html
new file mode 100644
index 0000000000000000000000000000000000000000..8fb4719563f27ecfdfce9bf0a2db3996810cb92a
--- /dev/null
+++ b/student2019/201421109/201421109_phong_shading.html
@@ -0,0 +1,318 @@
+<!--
+WebGL 1.0 Tutorial - Phong Shading
+CC-NC-BY SeongHeon Kim (201421109)
+
+@author SeongHeon Kim
+@date 2019-06-22
+
+@WebGL applet by Prof. Thorsten Thormahlen. Modified by SeongHeon Kim for educational purpose
+@http://www.mathematik.uni-marburg.de/~thormae/lectures/graphics1/code/WebGLShaderLightMat/ShaderLightMat.html
+-->
+<html>
+
+<head>
+<title>201421109_CGproject</title>
+<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
+
+<script type="text/javascript" src="201421109_phong_shading.js"> </script>
+
+<!-- change mode script -->
+<script type="text/javascript">
+function modeChanged() {
+  var mode = document.getElementById("select_mode").value;
+  modeVal = mode;
+}
+</script>
+
+<!-- slider style -->
+<style>
+.slidecontainer {
+  width: 100%;
+}
+
+.slider {
+  -webkit-appearance: none;
+  width: 100%;
+  height: 25px;
+  background: #d3d3d3;
+  outline: none;
+  opacity: 0.7;
+  -webkit-transition: .2s;
+  transition: opacity .2s;
+}
+
+.slider:hover {
+  opacity: 1;
+}
+
+.slider::-webkit-slider-thumb {
+  -webkit-appearance: none;
+  appearance: none;
+  width: 25px;
+  height: 25px;
+  background: #4CAF50;
+  cursor: pointer;
+}
+
+.slider::-moz-range-thumb {
+  width: 25px;
+  height: 25px;
+  background: #4CAF50;
+  cursor: pointer;
+}
+</style>
+
+<!-- tab style -->
+<style>
+body {font-family: Arial;}
+
+/* Style the tab */
+.tab {
+  overflow: hidden;
+  border: 1px solid #ccc;
+  background-color: #f1f1f1;
+}
+
+/* Style the buttons inside the tab */
+.tab button {
+  background-color: inherit;
+  float: left;
+  border: none;
+  outline: none;
+  cursor: pointer;
+  padding: 14px 16px;
+  transition: 0.3s;
+  font-size: 17px;
+}
+
+/* Change background color of buttons on hover */
+.tab button:hover {
+  background-color: #ddd;
+}
+
+/* Create an active/current tablink class */
+.tab button.active {
+  background-color: #ccc;
+}
+
+/* Style the tab content */
+.tabcontent {
+  display: none;
+  padding: 6px 12px;
+  border: 1px solid #ccc;
+  border-top: none;
+}
+</style>
+
+</head>
+
+
+<body onload="main()">
+
+  <h1>WebGL Project: Phong Shading tutorial</h1>
+
+  <div style="max-width: 550px;">
+    <p>
+      This project is webGL shading tutorial.<br>
+      You can learn about phong shading by selecting each component.
+    </p>
+  </div>
+
+  <h3>Functions</h3>
+  <p>Click and check function which tutorial provide:</p>
+
+  <!-- tab -->
+  <div class="tab">
+    <button class="tablinks" onclick="openCity(event, 'Animation')">Animation</button>
+    <button class="tablinks" onclick="openCity(event, 'Mode')">Mode</button>
+    <button class="tablinks" onclick="openCity(event, 'Coefficient')">Coefficient</button>
+    <button class="tablinks" onclick="openCity(event, 'Code')">Code</button>
+  </div>
+
+  <div id="Animation" class="tabcontent">
+    <h3>Animation</h3>
+    <p>To start or stop rotating using 'Animation Rotate' and 'Stop Rotate' button. </p>
+  </div>
+
+  <div id="Mode" class="tabcontent">
+    <h3>Mode</h3>
+    <p>To chage mode. Phong, Ambient only, Diffuse Only, Specular Only, NormalColor mode are provided.</p>
+    <p>NormalColor mode use normal vector as ambient and diffuse color. 
+  </div>
+
+  <div id="Coefficient" class="tabcontent">
+    <h3>Coefficient</h3>
+    <p>To chage Coefficient. You can learn aobut particular reflection's role in phong shading.</p>
+  </div>
+
+  <div id="Code" class="tabcontent">
+    <h3>Code</h3>
+    <p>Using provided shder code. You can implement your own phong shading.</p>
+    <p>Editing and Renadering code are not provided.</p>
+  </div>
+
+  <script>
+  function openCity(evt, cityName) {
+    var i, tabcontent, tablinks;
+    tabcontent = document.getElementsByClassName("tabcontent");
+    for (i = 0; i < tabcontent.length; i++) {
+      tabcontent[i].style.display = "none";
+    }
+    tablinks = document.getElementsByClassName("tablinks");
+    for (i = 0; i < tablinks.length; i++) {
+      tablinks[i].className = tablinks[i].className.replace(" active", "");
+    }
+    document.getElementById(cityName).style.display = "block";
+    evt.currentTarget.className += " active";
+  }
+  </script>
+
+  <!-- canvas for WebGL -->
+  <p>
+  <canvas id="helloapicanvas" style="border: none;" width="400" height="400"></canvas>
+  
+  <!-- rotation button -->
+  <p>
+  <button onclick="animRotate()">Animation Rotate</button>
+  <button onclick="stopRotate()">Stop Rotate</button>
+  
+  <!-- mode change menue -->
+  <p>
+  <select onchange="modeChanged()" id="select_mode">
+    <option value="1">normal mode</option>
+    <option value="2">ambient only</option>
+    <option value="3">diffuse only</option>
+    <option value="4">specular only</option>
+    <option value="5">NormalColor mode</option>
+  </select>
+  
+  <!-- coefficient slider -->
+  <p>
+  <div class="slidecontainer1">
+  <p>Ambient reflection coefficient (Ka): <span id="Ka"></span></p>
+  <input type="range" min="0" max="100" value="100" id="KaSlider">
+  </div>
+
+  <script>
+  var slider = document.getElementById("KaSlider");
+  var output = document.getElementById("Ka");
+
+  slider.oninput = function() {
+  KaVal = document.getElementById("KaSlider").value / 100;
+  }
+  </script>
+
+  <div class="slidecontainer2">
+  <p>Diffuse reflection coefficient (Kd): <span id="Kd"></span></p>
+  <input type="range" min="0" max="100" value="100" id="KdSlider">
+  </div>
+
+  <script>
+  var slider = document.getElementById("KdSlider");
+  var output = document.getElementById("Kd");
+
+  slider.oninput = function() {
+  KdVal = document.getElementById("KdSlider").value / 100;
+  }
+  </script>
+
+  <div class="slidecontainer3">
+  <p>Specular reflection coefficient (Ks): <span id="Ks"></span></p>
+  <input type="range" min="0" max="100" value="100" id="KsSlider">
+  </div>
+
+  <script>
+  var slider = document.getElementById("KsSlider");
+  var output = document.getElementById("Ks");
+
+  slider.oninput = function() {
+  KsVal = document.getElementById("KsSlider").value / 100;
+  }
+  </script>
+
+  <!-- shader code viewer-->
+  <P>
+    <table border="0" class="maintable" cellpadding="3px" cellspacing="3px">
+    <tr style="vertical-align: top;">
+
+    <!-- vertex shader code -->
+    <td>Vertex shader: <br>
+      <textarea cols="80" rows="30" class="code_input_vert" id="code_vert">
+      attribute highp vec3 myVertex;
+        attribute highp vec4 myColor;
+        attribute highp vec2 myUV;
+        attribute highp vec3 myNormal;
+        attribute vec2 inputTexCoord;
+        
+        uniform mediump mat4 Pmatrix;
+        uniform mediump mat4 Vmatrix;
+        uniform mediump mat4 Mmatrix;
+        uniform mediump mat4 Nmatrix;
+        
+        varying mediump vec4 color;
+        varying mediump vec2 texCoord;
+        varying mediump vec3 normalInterp;
+        varying vec3 vertPos;
+        
+        void main(void)
+        {
+            vec4 vertPos4 = Vmatrix*vec4(myVertex, 1.0);
+            vertPos = vec3(vertPos4) / vertPos4.w;
+            normalInterp = vec3(Mmatrix * vec4(myNormal, 0.0));
+
+            gl_Position = Pmatrix*Vmatrix*Mmatrix*vec4(myVertex, 1.0);
+        };  
+      </textarea>
+      <p id="code_vert_error" style="width: 300px;"></p>
+    </td>
+
+    <!-- fragment shader code -->
+    <td>Fragment shader: <br>
+      <textarea cols="80" rows="30" class="code_input" id="code_frag">
+    precision mediump float;
+      
+        varying vec3 normalInterp;
+        varying vec3 vertPos;
+      
+        uniform int mode;
+      uniform float Ka;
+        uniform float Kd;
+        uniform float Ks;
+      
+        const vec3 lightPos = vec3(5.0,1.0,1.0);
+        const vec3 ambientColor = vec3(0.3, 0.0, 0.0);
+        const vec3 diffuseColor = vec3(0.7, 0.0, 0.0);
+      const vec3 specColor = vec3(1.0, 1.0, 1.0);
+      
+      void main() 
+      {
+          vec3 normal = normalize(normalInterp);
+          vec3 lightDir = normalize(lightPos - vertPos);
+          vec3 reflectDir = reflect(-lightDir, normal);
+          vec3 viewDir = normalize(-vertPos);
+        
+          float lambertian = max(dot(normal, lightDir), 0.0);
+          float specular = 0.0;
+        
+          if(lambertian > 0.0)
+          {
+           float specAngle = max(dot(reflectDir, viewDir), 0.0);
+           specular = pow(specAngle, 4.0);
+          }
+          gl_FragColor = vec4(Ka*ambientColor + Kd*lambertian*diffuseColor + Ks*specular*specColor, 1.0);
+          
+          if(mode == 2) gl_FragColor = vec4(Ka*ambientColor, 1.0);
+          
+          if(mode == 3) gl_FragColor = vec4(Kd*lambertian*diffuseColor, 1.0);
+          
+          if(mode == 4) gl_FragColor = vec4(Ks*specular*specColor, 1.0);
+      }
+      </textarea>
+      <p id="code_frag_error" style="width: 300px;"></p>
+    </td>
+    </tr>
+    </table>
+
+</body>
+
+</html>
diff --git a/student2019/201421109/201421109_phong_shading.js b/student2019/201421109/201421109_phong_shading.js
new file mode 100644
index 0000000000000000000000000000000000000000..c63d306adcfca97a99b790c679b15c7ea057a1eb
--- /dev/null
+++ b/student2019/201421109/201421109_phong_shading.js
@@ -0,0 +1,883 @@
+/*
+WebGL 1.0 Tutorial - Phong Shading
+CC-NC-BY SeongHeon Kim (201421109)
+
+@author SeongHeon Kim
+@date 2019-06-22
+
+@WebGL applet by Prof. Thorsten Thormahlen. Modified by SeongHeon Kim for educational purpose
+@http://www.mathematik.uni-marburg.de/~thormae/lectures/graphics1/code/WebGLShaderLightMat/ShaderLightMat.html
+*/
+
+// GLM library
+
+/*!
+@fileoverview gl-matrix - High performance matrix and vector operations
+@author Brandon Jones
+@author Colin MacKenzie IV
+@version 3.0.0
+
+Copyright (c) 2015-2019, 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.
+*/
+
+/**
+   * 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;
+}
+
+/**
+   * 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;
+}
+
+/**
+   * Multiplies two mat4s
+   *
+   * @param {mat4} out the receiving matrix
+   * @param {mat4} a the first operand
+   * @param {mat4} 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 {mat4} a the matrix to translate
+   * @param {vec3} 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;
+}
+
+/**
+   * Rotates a matrix by the given angle around the X axis
+   *
+   * @param {mat4} out the receiving matrix
+   * @param {mat4} 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 {mat4} 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 {mat4} 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;
+}
+
+/**
+   * Scales the mat4 by the dimensions in the given vec3 not using vectorization
+   *
+   * @param {mat4} out the receiving matrix
+   * @param {mat4} a the matrix to scale
+   * @param {vec3} 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;
+}
+
+/**
+   * Normalize a vec3
+   *
+   * @param {vec3} out the receiving vector
+   * @param {vec3} 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;
+}
+
+// Start of project code
+
+// global variable
+var gl;
+
+// mode, Ka, Kd, Ks using HTML code and fragment shader code to change mode and coefficient of reflection
+// This 4 variables range from 0 to 1 and initial number was 1
+// modeVal is int and others are float
+var modeVal = 1;
+var KaVal = 1.0;
+var KdVal = 1.0;
+var KsVal = 1.0;
+
+// rotValue and incRotValue are used for rotating cube
+// Small is used for rotating small cube
+// 2 small cubes are start to rotate when program initialize
+// big cube is rotate when user click button in HTML code
+var rotValue = 0.0; 
+var rotValueSmall = 0.0; 
+var incRotValue = 0.0;
+var incRotValueSmall = 0.02;
+var tempRotValue = 0.0; 
+
+// Projection Matrix
+// Fov angle was 30 and it must be changed as radian
+// aspect ratio, near, far was set 1.0, 1 and 15.0
+var proj_matrix = [];
+var FOVangle = 30;
+proj_matrix = perspective(proj_matrix, FOVangle*Math.PI/180, 1.0, 1, 15.0);
+
+// Model Matrix
+var mov_matrix = [1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1];
+
+// View Matrix
+// view_matrix[14]- 5 for zoom camera
+var view_matrix = [1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1];
+view_matrix[14] = view_matrix[14]-5;
+
+// Normal Matrix
+var normal_matrix = [1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1];
+
+/**
+   * check err of last functioncall 
+   *
+   * @param functionLastCalled
+   **/
+
+function testGLError(functionLastCalled) {
+
+    var lastError = gl.getError();
+
+    if (lastError != gl.NO_ERROR) {
+        alert(functionLastCalled + " failed (" + lastError + ")");
+        return false;
+    }
+
+    return true;
+}
+
+/**
+   * initialize canas for gl program using canva.width, canvas.height
+   *
+   * @param canvas
+   **/
+
+function initialiseGL(canvas) {
+    try {
+ // Try to grab the standard context. If it fails, fallback to experimental
+        gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
+        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;
+}
+
+/**
+   * initialize and bind vertex Data 
+   *
+   **/
+
+function initialiseBuffer() {
+    
+    // vertexData for cube 
+
+    var vertexData = [
+    // x,y,z    color(r,g,b,a)    texture(u,v)    normal(x,y,z)
+    // using counter-clock-wise front face check
+    // front
+    -0.5, -0.5,  0.5,    1.0, 0.0, 1.0, 0.5,    0.0, 0.0,    0.0,  0.0,  1.0, //7
+     0.5, -0.5,  0.5,    1.0, 0.0, 1.0, 0.5,    1.0, 0.0,    0.0,  0.0,  1.0, //5
+     0.5,  0.5,  0.5,    1.0, 0.0, 1.0, 0.5,    1.0, 1.0,    0.0,  0.0,  1.0, //1
+         
+    -0.5, -0.5,  0.5,    1.0, 0.0, 1.0, 0.5,    0.0, 0.0,    0.0,  0.0,  1.0, //7
+     0.5,  0.5,  0.5,    1.0, 0.0, 1.0, 0.5,    1.0, 1.0,    0.0,  0.0,  1.0, //1
+    -0.5,  0.5,  0.5,    1.0, 0.0, 1.0, 0.5,    0.0, 1.0,    0.0,  0.0,  1.0, //3
+
+    // back
+     0.5,  0.5, -0.5,    0.0, 0.0, 0.0, 0.5,    1.0, 1.0,    0.0,  0.0, -1.0, //2
+     0.5, -0.5, -0.5,    0.0, 0.0, 0.0, 0.5,    1.0, 0.0,    0.0,  0.0, -1.0, //6
+    -0.5, -0.5, -0.5,    0.0, 0.0, 0.0, 0.5,    0.0, 0.0,    0.0,  0.0, -1.0, //8
+    
+    -0.5,  0.5, -0.5,    0.0, 0.0, 0.0, 0.5,    0.0, 1.0,    0.0,  0.0, -1.0, //4
+     0.5,  0.5, -0.5,    0.0, 0.0, 0.0, 0.5,    1.0, 1.0,    0.0,  0.0, -1.0, //2
+    -0.5, -0.5, -0.5,    0.0, 0.0, 0.0, 0.5,    0.0, 0.0,    0.0,  0.0, -1.0, //8
+
+    // up
+    -0.5,  0.5,  0.5,    1.0, 1.0, 1.0, 0.5,    0.0, 1.0,    0.0,  1.0,  0.0, //3 
+     0.5,  0.5,  0.5,    1.0, 1.0, 1.0, 0.5,    1.0, 1.0,    0.0,  1.0,  0.0, //1
+     0.5,  0.5, -0.5,    1.0, 1.0, 1.0, 0.5,    1.0, 1.0,    0.0,  1.0,  0.0, //2
+        
+    -0.5,  0.5,  0.5,    1.0, 1.0, 1.0, 0.5,    0.0, 1.0,    0.0,  1.0,  0.0, //3
+     0.5,  0.5, -0.5,    1.0, 1.0, 1.0, 0.5,    1.0, 1.0,    0.0,  1.0,  0.0, //2
+    -0.5,  0.5, -0.5,    1.0, 1.0, 1.0, 0.5,    0.0, 1.0,    0.0,  1.0,  0.0, //4
+
+    // down
+     0.5, -0.5, -0.5,    0.0, 1.0, 0.0, 0.5,    1.0, 0.0,    0.0, -1.0,  0.0, //6
+     0.5, -0.5,  0.5,    0.0, 1.0, 0.0, 0.5,    1.0, 0.0,    0.0, -1.0,  0.0, //5
+    -0.5, -0.5,  0.5,    0.0, 1.0, 0.0, 0.5,    0.0, 0.0,    0.0, -1.0,  0.0, //7
+    
+    -0.5, -0.5, -0.5,    0.0, 1.0, 0.0, 0.5,    0.0, 0.0,    0.0, -1.0,  0.0, //8
+     0.5, -0.5, -0.5,    0.0, 1.0, 0.0, 0.5,    1.0, 0.0,    0.0, -1.0,  0.0, //6
+    -0.5, -0.5,  0.5,    0.0, 1.0, 0.0, 0.5,    0.0, 0.0,    0.0, -1.0,  0.0, //7
+
+    // right
+     0.5, -0.5,  0.5,    1.0, 0.5, 0.0, 0.5,    0.0, 1.0,    1.0,  0.0,  0.0, //5
+     0.5, -0.5, -0.5,    1.0, 0.5, 0.0, 0.5,    0.0, 1.0,    1.0,  0.0,  0.0, //6
+     0.5,  0.5, -0.5,    1.0, 0.5, 0.0, 0.5,    1.0, 1.0,    1.0,  0.0,  0.0, //2
+
+     0.5, -0.5,  0.5,    1.0, 0.5, 0.0, 0.5,    0.0, 1.0,    1.0,  0.0,  0.0, //5
+     0.5,  0.5, -0.5,    1.0, 0.5, 0.0, 0.5,    1.0, 1.0,    1.0,  0.0,  0.0, //2
+     0.5,  0.5,  0.5,    1.0, 0.5, 0.0, 0.5,    1.0, 1.0,    1.0,  0.0,  0.0, //1
+
+    //left
+    -0.5,  0.5, -0.5,    1.0, 0.0, 0.0, 0.5,    0.0, 1.0,   -1.0,  0.0,  0.0, //4
+    -0.5, -0.5, -0.5,    1.0, 0.0, 0.0, 0.5,    0.0, 0.0,   -1.0,  0.0,  0.0, //8
+    -0.5, -0.5,  0.5,    1.0, 0.0, 0.0, 0.5,    0.0, 0.0,   -1.0,  0.0,  0.0, //7
+    
+    -0.5,  0.5,  0.5,    1.0, 0.0, 0.0, 0.5,    0.0, 1.0,   -1.0,  0.0,  0.0, //3
+    -0.5,  0.5, -0.5,    1.0, 0.0, 0.0, 0.5,    0.0, 1.0,   -1.0,  0.0,  0.0, //4
+    -0.5, -0.5,  0.5,    1.0, 0.0, 0.0, 0.5,    0.0, 0.0,   -1.0,  0.0,  0.0, //7
+    ];
+  
+    // Generate a buffer object
+    gl.vertexBuffer = gl.createBuffer();
+
+    // Bind buffer as a vertex buffer so we can fill it with data
+    gl.bindBuffer(gl.ARRAY_BUFFER, gl.vertexBuffer);
+    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertexData), gl.STATIC_DRAW);
+    return testGLError("initialiseBuffers");
+}
+
+
+function initialiseShaders() {
+
+    /**
+       * fragment shader source code
+       *
+       * varying comes form vertex shader
+       * normalInterp was Surfce normal vector
+       * vertPos was a Vertex Position
+       *
+       * uniform was set as global variable and it will be changed by HTML 
+       *
+       * const vec3 for light position, ambientColor, diffuseColor, specularColor
+       *
+       * normal vector was normalize to use
+       * lightDir was Light direction from obj
+       * reflectDir was reflected light vector
+       * viewDir was vector to viewer
+       *
+       * Phong shading use labercian suface which is perfectly diffuse reflector
+       * So we use Labert's cosine Law to calculate specular
+       *
+       * Phong Shading = Ambient + Diffuse + Specular 
+       * Ka, Kd, Ks was coefficient for tutorial study
+       * 
+       **/
+    var fragmentShaderSource = '\
+      precision mediump float;\
+      \
+      varying vec3 normalInterp;\
+      varying vec3 vertPos;\
+      \
+      uniform int mode;\
+      uniform float Ka;\
+      uniform float Kd;\
+      uniform float Ks;\
+      \
+      const vec3 lightPos = vec3(5.0,1.0,1.0);\
+      const vec3 ambientColor = vec3(0.3, 0.0, 0.0);\
+      const vec3 diffuseColor = vec3(0.7, 0.0, 0.0);\
+      const vec3 specColor = vec3(1.0, 1.0, 1.0);\
+      \
+      void main() {\
+        vec3 normal = normalize(normalInterp);\
+        vec3 lightDir = normalize(lightPos - vertPos);\
+        vec3 reflectDir = reflect(-lightDir, normal);\
+        vec3 viewDir = normalize(-vertPos);\
+        \
+        float lambertian = max(dot(normal, lightDir), 0.0);\
+        float specular = 0.0;\
+        \
+        if(lambertian > 0.0) {\
+           float specAngle = max(dot(reflectDir, viewDir), 0.0);\
+           specular = pow(specAngle, 4.0);\
+        }\
+        gl_FragColor = vec4(Ka*ambientColor + Kd*lambertian*diffuseColor + Ks*specular*specColor, 1.0);\
+        \
+        if(mode == 2) gl_FragColor = vec4(Ka*ambientColor, 1.0);\
+        \
+        if(mode == 3) gl_FragColor = vec4(Kd*lambertian*diffuseColor, 1.0);\
+        \
+        if(mode == 4) gl_FragColor = vec4(Ks*specular*specColor, 1.0);\
+        \
+        if(mode == 5) gl_FragColor = vec4(Ka*normal*0.1 + Kd*lambertian*normal + Ks*specular*specColor, 1.0);\
+      }';
+
+    gl.fragShader = gl.createShader(gl.FRAGMENT_SHADER);
+    gl.shaderSource(gl.fragShader, fragmentShaderSource);
+    gl.compileShader(gl.fragShader);
+    if (!gl.getShaderParameter(gl.fragShader, gl.COMPILE_STATUS)) {
+        alert("Failed to compile the fragment shader.\n" + gl.getShaderInfoLog(gl.fragShader));
+        return false;
+    }
+
+    /**
+       * vertes shader source code
+       *
+       * phong shading calculate light and color in fragment shader
+       * So vertex shader only have goal that calculate position of vertex 
+       * and assign value for varying using attribute
+       * 
+       **/
+    var vertexShaderSource = '\
+        attribute highp vec3 myVertex; \
+        attribute highp vec4 myColor; \
+        attribute highp vec2 myUV; \
+        attribute highp vec3 myNormal; \
+        attribute vec2 inputTexCoord;\
+        \
+        uniform mediump mat4 Pmatrix; \
+        uniform mediump mat4 Vmatrix; \
+        uniform mediump mat4 Mmatrix; \
+        uniform mediump mat4 Nmatrix; \
+        \
+        varying mediump vec4 color; \
+        varying mediump vec2 texCoord;\
+        varying mediump vec3 normalInterp;\
+        varying vec3 vertPos;\
+        \
+        void main(void)  \
+        { \
+          vec4 vertPos4 = Vmatrix*vec4(myVertex, 1.0);\
+          vertPos = vec3(vertPos4) / vertPos4.w;\
+          normalInterp = vec3(Mmatrix * vec4(myNormal, 0.0));\
+          \
+          gl_Position = Pmatrix*Vmatrix*Mmatrix*vec4(myVertex, 1.0);\
+        }';
+
+    gl.vertexShader = gl.createShader(gl.VERTEX_SHADER);
+    gl.shaderSource(gl.vertexShader, vertexShaderSource);
+    gl.compileShader(gl.vertexShader);
+    if (!gl.getShaderParameter(gl.vertexShader, gl.COMPILE_STATUS)) {
+        alert("Failed to compile the vertex shader.\n" + gl.getShaderInfoLog(gl.vertexShader));
+        return false;
+    }
+
+    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
+    gl.bindAttribLocation(gl.programObject, 0, "myVertex");
+    gl.bindAttribLocation(gl.programObject, 1, "myColor");
+    gl.bindAttribLocation(gl.programObject, 2, "myUV");
+    gl.bindAttribLocation(gl.programObject, 3, "myNormal"); //추가됨
+
+    // Link the program
+    gl.linkProgram(gl.programObject);
+
+    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");
+}
+
+/**
+   * stop Rotating
+   *
+   **/
+
+function stopRotate()
+{
+  if (incRotValue == 0.0)
+  {
+    incRotValue = tempRotValue; 
+  }
+  else
+  {
+    tempRotValue = incRotValue; 
+    incRotValue = 0.0; 
+  }
+}
+
+/**
+   * start Rotating
+   *
+   **/
+
+function animRotate()
+{
+  incRotValue += 0.01;
+}
+
+/**
+   * rendering Scene
+   *
+   **/
+
+function renderScene() {
+
+  rotValue += incRotValue; 
+  rotValueSmall += incRotValueSmall;
+
+  // initilize location of uniform
+  var Pmatrix = gl.getUniformLocation(gl.programObject, "Pmatrix");
+  var Vmatrix = gl.getUniformLocation(gl.programObject, "Vmatrix");
+  var Mmatrix = gl.getUniformLocation(gl.programObject, "Mmatrix");
+  var Nmatrix = gl.getUniformLocation(gl.programObject, "Nmatrix"); 
+  var modeLoc = gl.getUniformLocation(gl.programObject, "mode");
+  var KaLoc = gl.getUniformLocation(gl.programObject, "Ka");
+  var KdLoc = gl.getUniformLocation(gl.programObject, "Kd");
+  var KsLoc = gl.getUniformLocation(gl.programObject, "Ks");
+  
+  // set uniform
+  if(modeLoc != -1) gl.uniform1i(modeLoc, modeVal);
+  if(KaLoc != -1) gl.uniform1f(KaLoc, KaVal);
+  if(KdLoc != -1) gl.uniform1f(KdLoc, KdVal);
+  if(KsLoc != -1) gl.uniform1f(KsLoc, KsVal);
+  
+  // set vertex Object which bind in initialiseBuffer()
+  gl.enableVertexAttribArray(0);
+  gl.vertexAttribPointer(0, 3, gl.FLOAT, gl.FALSE, 48, 0); // x, y, z
+  gl.enableVertexAttribArray(1);
+  gl.vertexAttribPointer(1, 4, gl.FLOAT, gl.FALSE, 48, 12); // a, g, b, a
+  gl.enableVertexAttribArray(2);
+  gl.vertexAttribPointer(2, 2, gl.FLOAT, gl.FALSE, 48, 28); // u, v
+  gl.enableVertexAttribArray(3);
+  gl.vertexAttribPointer(3, 3, gl.FLOAT, gl.FALSE, 48, 36); // x, y, z (normal vector)
+
+  if (!testGLError("gl.vertexAttribPointer")) {
+      return false;
+  }
+
+  // per-fragment operation : depth test, culling, blending 
+  gl.enable(gl.DEPTH_TEST);
+  gl.depthFunc(gl.LEQUAL); 
+  // gl.enable(gl.CULL_FACE);
+  gl.enable(gl.BLEND);
+  gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
+  gl.blendEquation(gl.FUNC_ADD);
+
+  // set background color
+  gl.clearColor(0.6, 0.8, 1.0, 1.0);
+  gl.clearDepth(1.0); 
+  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
+
+  // first cube
+  // set Model Matrix for trnalsation
+  identity$3(mov_matrix);
+  
+  // copy original Model Matrix (identity)
+  var mov_matrix_ori = mov_matrix.slice();
+  
+  rotateY(mov_matrix, mov_matrix, rotValue); 
+  rotateX(mov_matrix, mov_matrix, rotValue); 
+
+  // set M, V, P Matrix
+  gl.uniformMatrix4fv(Pmatrix, false, proj_matrix);
+  gl.uniformMatrix4fv(Vmatrix, false, view_matrix);
+  gl.uniformMatrix4fv(Mmatrix, false, mov_matrix);
+  gl.uniformMatrix4fv(Nmatrix, false, normal_matrix);
+
+  if (!testGLError("gl.uniformMatrix4fv")) {
+      return false;
+  }
+  
+  gl.drawArrays(gl.TRIANGLES, 0, 36);
+  
+  // second cube
+  var mov_matrix2 = mov_matrix.slice(); 
+  translate$2(mov_matrix2, mov_matrix2, [0.75, 0.75, 0.75]);
+  rotateY(mov_matrix2, mov_matrix2, rotValueSmall); 
+  scale$3(mov_matrix2, mov_matrix2,[0.25, 0.25, 0.25]);
+  gl.uniformMatrix4fv(Mmatrix, false, mov_matrix2);
+  gl.drawArrays(gl.TRIANGLES, 0, 36);
+
+  // third cube
+  var mov_matrix3 = mov_matrix2.slice(); 
+  translate$2(mov_matrix3, mov_matrix3, [0.75, -0.75, 0.75]);
+  rotateY(mov_matrix3, mov_matrix3, rotValueSmall); 
+  scale$3(mov_matrix3, mov_matrix3,[0.25, 0.25, 0.25]);
+  gl.uniformMatrix4fv(Mmatrix, false, mov_matrix3);
+  gl.drawArrays(gl.TRIANGLES, 0, 36);
+
+  
+  if (!testGLError("gl.drawArrays")) {
+      return false;
+  }
+
+  return true;
+}
+
+/**
+   * main function
+   * using render loop draw scean
+   **/
+
+function main() {
+    var canvas = document.getElementById("helloapicanvas");
+    console.log("Start");
+
+    if (!initialiseGL(canvas)) {
+        return;
+    }
+
+    if (!initialiseBuffer()) {
+        return;
+    }
+
+    if (!initialiseShaders()) {
+        return;
+    }
+
+    // Render loop
+    requestAnimFrame = (
+    function () {
+        //  return window.requestAnimationFrame || window.webkitRequestAnimationFrame 
+       // || window.mozRequestAnimationFrame || 
+      return function (callback) {
+          // console.log("Callback is"+callback); 
+          window.setTimeout(callback, 10, 10); };
+    })();
+
+    (function renderLoop(param) {
+        if (renderScene()) {
+            // Everything was successful, request that we redraw our scene again in the future
+            requestAnimFrame(renderLoop);
+        }
+    })();
+}
diff --git a/student2019/201421109/README.md b/student2019/201421109/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..51c76a6f9bf9957f0ef93ea0b11c766bc8bfc082
--- /dev/null
+++ b/student2019/201421109/README.md
@@ -0,0 +1,159 @@
+# CG_Project : Phong Shading tutorial
+CC-NC-BY SeongHeon Kim
+
+This Porject is WebGL shading tutorial
+
+You can learn aobut 'Phong Shading 'by selecting ambient, diffuse and specular
+or control coefficient of each of them.
+You can also animate object and change color. 
+
+But, 
+select color (ambient, diffuse, specular)
+change vertex or fragment shader and re render scene are not provided.
+
+However,
+Copy vertex and fragment shader code are provided.
+You can easily implement your own Phong Shading Object ! 
+
+This program are tested in google chrome invironment only.
+other web browser would not support WebGL
+
+# Features
+
+  - Animation
+> To start or stop rotating using 'Animation Rotate' and 'Stop Rotate' button.
+> Also, you can speed up rotation of cube clicking button repetedly
+
+  - Change Mode
+> Phong shading
+> Ambient Only
+> Diffuse Only
+> Specular Only
+> Normal Color Mode (use normal vector as ambient and diffuse color)
+
+  - Control Coefficient
+> control coefficient of Ambient, Diffuse, Specular
+> You can change intensity of each color
+
+  - Shader Code
+> You can copy shader code which tutorial used 
+> Also, you can easily know about Phong shading's implementaion
+
+# What is Phong shading 
+
+![Phong](./image/phong.png)
+
+The Phong reflection model (also called Phong illumination or Phong lighting) is an empirical model of the local illumination of points on a surface. 
+
+In 3D computer graphics, it is sometimes referred to as "Phong shading", in particular if the model is used with the interpolation method of the same name and in the context of pixel shaders or other places where a lighting calculation can be referred to as “shading”.
+
+Phong interpolation is not a part of this tutorial.
+
+# Implementation of Phong shading
+## vertex shader
+* vertes shader source code
+* phong shading calculate light and color in fragment shader
+* So vertex shader only have goal that calculate position of vertex 
+* and assign value for varying using attribute
+```
+attribute highp vec3 myVertex;
+        attribute highp vec4 myColor;
+        attribute highp vec2 myUV;
+        attribute highp vec3 myNormal;
+        attribute vec2 inputTexCoord;
+        
+        uniform mediump mat4 Pmatrix;
+        uniform mediump mat4 Vmatrix;
+        uniform mediump mat4 Mmatrix;
+        uniform mediump mat4 Nmatrix;
+        
+        varying mediump vec4 color;
+        varying mediump vec2 texCoord;
+        varying mediump vec3 normalInterp;
+        varying vec3 vertPos;
+        
+        void main(void)
+        {
+            vec4 vertPos4 = Vmatrix*vec4(myVertex, 1.0);
+            vertPos = vec3(vertPos4) / vertPos4.w;
+            normalInterp = vec3(Mmatrix * vec4(myNormal, 0.0));
+
+            gl_Position = Pmatrix*Vmatrix*Mmatrix*vec4(myVertex, 1.0);
+        }
+```
+## fragment shader
+
+* varying comes form vertex shader
+* normalInterp was Surfce normal vector
+* vertPos was a Vertex Position
+
+* uniform was set as global variable and it will be changed by HTML 
+
+* const vec3 for light position, ambientColor, diffuseColor, specularColor
+
+* normal vector was normalize to use
+* lightDir was Light direction from obj
+* reflectDir was reflected light vector
+* viewDir was vector to viewer
+
+* Phong shading use labercian suface which is perfectly diffuse reflector
+So we use Labert's cosine Law to calculate specular
+
+* Phong Shading = Ambient + Diffuse + Specular 
+Ka, Kd, Ks was coefficient for tutorial study
+
+```
+precision mediump float;
+      
+      	varying vec3 normalInterp;
+      	varying vec3 vertPos;
+      
+      	uniform int mode;
+     	uniform float Ka;
+      	uniform float Kd;
+      	uniform float Ks;
+      
+      	const vec3 lightPos = vec3(5.0,1.0,1.0);
+      	const vec3 ambientColor = vec3(0.3, 0.0, 0.0);
+      	const vec3 diffuseColor = vec3(0.7, 0.0, 0.0);
+     	const vec3 specColor = vec3(1.0, 1.0, 1.0);
+      
+     	void main() 
+     	{
+        	vec3 normal = normalize(normalInterp);
+        	vec3 lightDir = normalize(lightPos - vertPos);
+        	vec3 reflectDir = reflect(-lightDir, normal);
+        	vec3 viewDir = normalize(-vertPos);
+        
+       	 	float lambertian = max(dot(normal, lightDir), 0.0);
+        	float specular = 0.0;
+        
+        	if(lambertian > 0.0)
+        	{
+		       float specAngle = max(dot(reflectDir, viewDir), 0.0);
+		       specular = pow(specAngle, 4.0);
+        	}
+	        gl_FragColor = vec4(Ka*ambientColor + Kd*lambertian*diffuseColor + Ks*specular*specColor, 1.0);
+	        
+	        if(mode == 2) gl_FragColor = vec4(Ka*ambientColor, 1.0);
+	        
+	        if(mode == 3) gl_FragColor = vec4(Kd*lambertian*diffuseColor, 1.0);
+	        
+	        if(mode == 4) gl_FragColor = vec4(Ks*specular*specColor, 1.0);
+	    }
+```
+
+# result
+## Phong
+![result_phong](./image/result_phong.png)
+## Ambient Only
+![result_ambient](./image/result_ambient.png)
+## Diffuse Only
+![result_diffuse](./image/result_diffuse.png)
+## Specular Only
+![result_specular](./image/result_specular.png)
+## Normal Color
+![result_normalColor](./image/result_normalColor.png)
+
+# reference
+WebGL applet by Prof. Thorsten Thormahlen. Modified by SeongHeon Kim for educational purpose
\ No newline at end of file
diff --git a/student2019/201421109/cg_project b/student2019/201421109/cg_project
deleted file mode 160000
index c71eb4c9d1f6781035246a9d47045d4977f63bac..0000000000000000000000000000000000000000
--- a/student2019/201421109/cg_project
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit c71eb4c9d1f6781035246a9d47045d4977f63bac
diff --git a/student2019/201421109/image/.gitkeep b/student2019/201421109/image/.gitkeep
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/student2019/201421109/image/phong.png b/student2019/201421109/image/phong.png
new file mode 100644
index 0000000000000000000000000000000000000000..698ccd76c093c18378015e7eff2e69a81bea3d59
Binary files /dev/null and b/student2019/201421109/image/phong.png differ
diff --git a/student2019/201421109/image/result_ambient.png b/student2019/201421109/image/result_ambient.png
new file mode 100644
index 0000000000000000000000000000000000000000..22a72c093f81f4c53297bdb712b17ad681c57b81
Binary files /dev/null and b/student2019/201421109/image/result_ambient.png differ
diff --git a/student2019/201421109/image/result_diffuse.png b/student2019/201421109/image/result_diffuse.png
new file mode 100644
index 0000000000000000000000000000000000000000..a5caea030031c41b893c9c2057cd7921586d094b
Binary files /dev/null and b/student2019/201421109/image/result_diffuse.png differ
diff --git a/student2019/201421109/image/result_normalColor.png b/student2019/201421109/image/result_normalColor.png
new file mode 100644
index 0000000000000000000000000000000000000000..f6e3caac4b8af77f7b477a75fcfd7828acd78860
Binary files /dev/null and b/student2019/201421109/image/result_normalColor.png differ
diff --git a/student2019/201421109/image/result_phong.png b/student2019/201421109/image/result_phong.png
new file mode 100644
index 0000000000000000000000000000000000000000..ba9b08a2166abdb62da86fe8c348f8e40cede43f
Binary files /dev/null and b/student2019/201421109/image/result_phong.png differ
diff --git a/student2019/201421109/image/result_specular.png b/student2019/201421109/image/result_specular.png
new file mode 100644
index 0000000000000000000000000000000000000000..f1cbdbea99ab3fa809667cdfa3710b7dc78bd109
Binary files /dev/null and b/student2019/201421109/image/result_specular.png differ
diff --git a/student2019/201520510/webgl-tutorial-master/README.md b/student2019/201520510/README.md
similarity index 100%
rename from student2019/201520510/webgl-tutorial-master/README.md
rename to student2019/201520510/README.md
diff --git a/student2019/201520510/webgl-tutorial-master/WebGLHelloAPI.js b/student2019/201520510/WebGLHelloAPI.js
similarity index 100%
rename from student2019/201520510/webgl-tutorial-master/WebGLHelloAPI.js
rename to student2019/201520510/WebGLHelloAPI.js
diff --git a/student2019/201520510/webgl-tutorial-master/index.css b/student2019/201520510/index.css
similarity index 100%
rename from student2019/201520510/webgl-tutorial-master/index.css
rename to student2019/201520510/index.css
diff --git a/student2019/201520510/webgl-tutorial-master/index.html b/student2019/201520510/index.html
similarity index 100%
rename from student2019/201520510/webgl-tutorial-master/index.html
rename to student2019/201520510/index.html
diff --git a/student2019/201520889/.DS_Store b/student2019/201520889/.DS_Store
new file mode 100644
index 0000000000000000000000000000000000000000..bced9a0b9525ecbb616b55eac588e4e93d40488d
Binary files /dev/null and b/student2019/201520889/.DS_Store differ
diff --git a/student2019/201520889/Readme.md b/student2019/201520889/Readme.md
new file mode 100644
index 0000000000000000000000000000000000000000..867031c7c9a8fe1e8afbcc15d9cc38c4df069617
--- /dev/null
+++ b/student2019/201520889/Readme.md
@@ -0,0 +1,46 @@
+# 기말과제 WebGL Tutorial 만들기
+(CC-NC-BY) 2019 Mingi Kim
+
+마우스 입력을 이용하는 방법을 알아봤다.
+
+Primitive Assembly를 사용한다.
+
+Bayseian Curve를 그려본다.
+
+## 목표
+
+마우스를 이용해서 점을 찍어보며 여러 형태의 Primitive Assembly를 좀더 쉽게 만들어 볼 수 있도록 했습니다.
+vertex의 색상을 정하며 interpolate되는 것을 확인해 볼 수 있습니다.
+
+마우스를 이용해서 점을 찍어보며 Bayseian Curve를 쉽게 만들어 볼 수 있도록 했습니다.
+Bayseian 커브를 다양하게 만들어보며 특징을 배울 수 있습니다.
+
+## 구현 결과
+
+![](assembly.png)
+
+마우스 클릭으로 캔버스 위를 클릭하면 Point Num이 증가하고 Points Stack 에 클릭한 점의 좌표가 표시됩니다. 캔버스에 Assembly가 만들어져서 반영됩니다.
+
+색상 버튼을 눌러서 색상을 정해 줄 수 있습니다.
+
+clear 버튼을 누르면 Points Stack 이 초기화 되고 canvas에 있던 Assembly가 사라집니다.
+
+P/L/T 버튼을 누르면 Assembly Type을 바꿀 수 있습니다.
+
+![](bayesian.png)
+
+마우스 클릭으로 캔버스 위를 클릭하면 점을 만들 수 있습니다. 파란 점은 시작점과 끝점을 나타내고 빨간점 두 개는 컨트롤 포인트 입니다.
+4개의 점을 만들고 나면 bayesian 버튼을 눌러서 커브를 그릴 수 있습니다. 
+
+Clear Curve 버튼을 누르면 커브를 지울 수 있습니다.
+
+
+## tutorial
+
+따라 해볼 수 있는 튜토리얼을 만들어서 사용법을 쉽게 익히고 학습 효과를 높이고자 했습니다.
+
+### Primitive Assembly Tutorial
+![](tutorial0.png)
+
+### Bayesian Curve Tutorial
+![](tutorial1.png)
\ No newline at end of file
diff --git a/student2019/201520889/WebGLHelloAPI.js b/student2019/201520889/WebGLHelloAPI.js
new file mode 100644
index 0000000000000000000000000000000000000000..d86b35cf6c500d42fd2818b299d1d966a3f2d4d3
--- /dev/null
+++ b/student2019/201520889/WebGLHelloAPI.js
@@ -0,0 +1,265 @@
+// WebGL 1.0 Tutorial - Mouse Events Primitive Assembly 
+// CC-NC-BY Mingi Kim
+
+var gl;
+function testGLError(functionLastCalled) {
+    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
+        initEventHandlers(canvas, mouse);
+        gl = canvas.getContext("webgl")
+            || canvas.getContext("experimental-webgl");
+        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 = [];
+
+function initialiseBuffer() {
+    // Generate a buffer object
+    gl.vertexBuffer = gl.createBuffer();
+
+    // Bind buffer as a vertex buffer so we can fill it with data
+    gl.bindBuffer(gl.ARRAY_BUFFER, gl.vertexBuffer);
+
+    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertexData), gl.STATIC_DRAW);
+    return testGLError("initialiseBuffers");
+}
+
+function initialiseShaders() {
+    var fragmentShaderSource = '\
+			varying highp vec4 color; \
+			void main(void) \
+			{ \
+				gl_FragColor = color; \
+			}';
+    gl.fragShader = gl.createShader(gl.FRAGMENT_SHADER);
+    gl.shaderSource(gl.fragShader, fragmentShaderSource);
+    gl.compileShader(gl.fragShader);
+    var vertexShaderSource = '\
+			attribute highp vec4 myVertex; \
+			attribute highp vec4 myColor; \
+			uniform mediump mat4 transformationMatrix; \
+			varying highp vec4 color; \
+			void main(void)  \
+			{ \
+				gl_Position = transformationMatrix * myVertex; \
+				color = myColor; \
+			}';
+
+    // Create the vertex shader object
+    gl.vertexShader = gl.createShader(gl.VERTEX_SHADER);
+
+    // Load the source code into it
+    gl.shaderSource(gl.vertexShader, vertexShaderSource);
+
+    // Compile the source code
+    gl.compileShader(gl.vertexShader);
+
+    // Check if compilation succeeded
+    if (!gl.getShaderParameter(gl.vertexShader, gl.COMPILE_STATUS)) {
+        // It didn't. Display the info log as to why
+        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");
+
+    // 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 modeName = ["Triangle", "Point", "Line"];
+
+var modePLT = 0;
+function changeModePLT() {
+    modePLT++;
+    modePLT %= 3;
+    document.getElementById("idPLT").innerHTML = modeName[modePLT];
+}
+
+var rot_z = 0.0;
+
+function renderScene() {
+
+    gl.clearColor(0.2, 0.2, 0.2, 1.0);
+    gl.clear(gl.COLOR_BUFFER_BIT);
+
+    var matrixLocation = gl.getUniformLocation(gl.programObject, "transformationMatrix");
+
+    // Matrix used to specify the orientation of the triangle on screen
+    var transformationMatrix = [
+        1.0, 0.0, 0.0, 0.0,
+        0.0, 1.0, 0.0, 0.0,
+        0.0, 0.0, 1.0, 0.0,
+        0.0, 0.0, 0.0, 1.0
+    ];
+
+
+    // Pass the identity transformation matrix to the shader using its location
+    gl.uniformMatrix4fv(matrixLocation, gl.FALSE, transformationMatrix);
+
+    if (!testGLError("gl.uniformMatrix4fv")) {
+        return false;
+    }
+
+    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;
+    }
+
+    var pointNum = vertexData.length / 7;
+    if (modePLT == 0)
+        gl.drawArrays(gl.TRIANGLES, 0, pointNum);
+    else if (modePLT == 1)
+        gl.drawArrays(gl.POINTS, 0, pointNum);
+    else
+        gl.drawArrays(gl.LINES, 0, pointNum);
+
+    if (!testGLError("gl.drawArrays")) {
+        return false;
+    }
+
+    return true;
+}
+
+var mouse = [250, 250];
+var mouseRadius = 10;
+var pointsClicked = [];
+
+var clickedPointNum = 0;
+function initEventHandlers(canvas, mousePosition) {
+    var dragging = false;
+
+    canvas.onmousedown = function (ev) {  //Mouse is pressed
+        clickedPointNum++;
+        document.getElementById("pointnum").innerHTML = "Point Num:"+clickedPointNum;
+
+        var x = ev.clientX;
+        var y = ev.clientY;
+
+        var rect = ev.target.getBoundingClientRect();
+
+        var centerX = (rect.right - rect.left) / 2;
+        var centerY = (rect.bottom - rect.top) / 2;
+
+        if (rect.left <= x && x <= rect.right 
+            && rect.top <= y && y <= rect.bottom
+       ) {
+            mousePosition[0] = x;
+            mousePosition[1] = rect.bottom - y;
+            dragging = true;
+
+            px = (mousePosition[0] - centerX) / centerX;
+            py = (mousePosition[1] - centerY) / centerY;
+            color = document.getElementById("colorInput").value
+            // #XXXXXX -> ["XX", "XX", "XX"]
+            var color = color.match(/[A-Za-z0-9]{2}/g);
+
+            // ["XX", "XX", "XX"] -> [n, n, n]
+            color = color.map(function(v) { return parseInt(v, 16) / 255 });
+            pointsClicked.push(px, py, 0., color[0], color[1], color[2], 1.);
+            ps = "" + px + ", " + py+ "</br>";
+            document.getElementById("AllPoints").innerHTML += ps;
+            pushPoints2vertexData()
+        }
+    };
+
+
+    canvas.onmouseup = function (ev) { //Mouse is released
+        dragging = false;
+    }
+
+    canvas.onmousemove = function (ev) { //Mouse is moved
+    }
+}
+
+function pushPoints2vertexData() {
+    vertexData = vertexData.concat(pointsClicked);
+    pointsClicked = [];
+    initialiseBuffer()
+}
+
+function clearVertexData() {
+    vertexData = [];
+    pointsClicked = [];
+    clickedPointNum = 0;
+    document.getElementById("pointnum").innerHTML = ""+clickedPointNum;
+    document.getElementById("AllPoints").innerHTML = "";
+    initialiseBuffer();
+}
+
+function main() {
+    var canvas = document.getElementById("helloapicanvas");
+
+    if (!initialiseGL(canvas)) {
+        return;
+    }
+
+    if (!initialiseBuffer()) {
+        return;
+    }
+
+    if (!initialiseShaders()) {
+        return;
+    }
+
+    // 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);
+        }
+    })();
+    doBayesian();
+}
diff --git a/student2019/201520889/__MACOSX/webglfinalproject/._.DS_Store b/student2019/201520889/__MACOSX/webglfinalproject/._.DS_Store
deleted file mode 100644
index a5b28df1cbc6e15bd0d35cdadd0c2e65d5131c7d..0000000000000000000000000000000000000000
Binary files a/student2019/201520889/__MACOSX/webglfinalproject/._.DS_Store and /dev/null differ
diff --git a/student2019/201520889/__MACOSX/webglfinalproject/._assembly.png b/student2019/201520889/__MACOSX/webglfinalproject/._assembly.png
deleted file mode 100644
index 5418d02ffa7c45aacd329500766421145db4166f..0000000000000000000000000000000000000000
Binary files a/student2019/201520889/__MACOSX/webglfinalproject/._assembly.png and /dev/null differ
diff --git a/student2019/201520889/__MACOSX/webglfinalproject/._bayesian.png b/student2019/201520889/__MACOSX/webglfinalproject/._bayesian.png
deleted file mode 100644
index 79bb31ab5ed1da50315aa30ff44e588a04ce0caa..0000000000000000000000000000000000000000
Binary files a/student2019/201520889/__MACOSX/webglfinalproject/._bayesian.png and /dev/null differ
diff --git a/student2019/201520889/__MACOSX/webglfinalproject/._img0.png b/student2019/201520889/__MACOSX/webglfinalproject/._img0.png
deleted file mode 100644
index bea7af2af8a3e090a90af11c3d37045eed6c9605..0000000000000000000000000000000000000000
Binary files a/student2019/201520889/__MACOSX/webglfinalproject/._img0.png and /dev/null differ
diff --git a/student2019/201520889/__MACOSX/webglfinalproject/._img1.png b/student2019/201520889/__MACOSX/webglfinalproject/._img1.png
deleted file mode 100644
index 91bf2a9532f6427ee23252f9fc2ecc2f68ecc909..0000000000000000000000000000000000000000
Binary files a/student2019/201520889/__MACOSX/webglfinalproject/._img1.png and /dev/null differ
diff --git a/student2019/201520889/__MACOSX/webglfinalproject/._img2.png b/student2019/201520889/__MACOSX/webglfinalproject/._img2.png
deleted file mode 100644
index c3e6142c16a3c442ab239cd3157085180caae682..0000000000000000000000000000000000000000
Binary files a/student2019/201520889/__MACOSX/webglfinalproject/._img2.png and /dev/null differ
diff --git a/student2019/201520889/__MACOSX/webglfinalproject/._img3.png b/student2019/201520889/__MACOSX/webglfinalproject/._img3.png
deleted file mode 100644
index 714fb1eaf4e87a81a82669c8e42f08afd8bc2ffd..0000000000000000000000000000000000000000
Binary files a/student2019/201520889/__MACOSX/webglfinalproject/._img3.png and /dev/null differ
diff --git a/student2019/201520889/__MACOSX/webglfinalproject/._img4.png b/student2019/201520889/__MACOSX/webglfinalproject/._img4.png
deleted file mode 100644
index 22889ede441a331ba1332fe2ee458232562c53e7..0000000000000000000000000000000000000000
Binary files a/student2019/201520889/__MACOSX/webglfinalproject/._img4.png and /dev/null differ
diff --git a/student2019/201520889/__MACOSX/webglfinalproject/._img5.png b/student2019/201520889/__MACOSX/webglfinalproject/._img5.png
deleted file mode 100644
index c8532488e3a93ad9e473ab66f5b4ac226a915fbd..0000000000000000000000000000000000000000
Binary files a/student2019/201520889/__MACOSX/webglfinalproject/._img5.png and /dev/null differ
diff --git a/student2019/201520889/__MACOSX/webglfinalproject/._img6.png b/student2019/201520889/__MACOSX/webglfinalproject/._img6.png
deleted file mode 100644
index 6426d044751aa11d46ae4e890d7a4198b4024141..0000000000000000000000000000000000000000
Binary files a/student2019/201520889/__MACOSX/webglfinalproject/._img6.png and /dev/null differ
diff --git a/student2019/201520889/__MACOSX/webglfinalproject/._img7.png b/student2019/201520889/__MACOSX/webglfinalproject/._img7.png
deleted file mode 100644
index acac891d2df548214acca1dd311ff5e2568726af..0000000000000000000000000000000000000000
Binary files a/student2019/201520889/__MACOSX/webglfinalproject/._img7.png and /dev/null differ
diff --git a/student2019/201520889/__MACOSX/webglfinalproject/._tutorial0.png b/student2019/201520889/__MACOSX/webglfinalproject/._tutorial0.png
deleted file mode 100644
index da1ed4da67322faf69ce9a00c26adcfe73a6ecf9..0000000000000000000000000000000000000000
Binary files a/student2019/201520889/__MACOSX/webglfinalproject/._tutorial0.png and /dev/null differ
diff --git a/student2019/201520889/__MACOSX/webglfinalproject/._tutorial1.png b/student2019/201520889/__MACOSX/webglfinalproject/._tutorial1.png
deleted file mode 100644
index a49f5af3c8199e2da683f67f0dc446c70a5d6cb7..0000000000000000000000000000000000000000
Binary files a/student2019/201520889/__MACOSX/webglfinalproject/._tutorial1.png and /dev/null differ
diff --git a/student2019/201520889/assembly.png b/student2019/201520889/assembly.png
new file mode 100644
index 0000000000000000000000000000000000000000..88d3bd7d28cc6885c24545baa9dfae5565443d4a
Binary files /dev/null and b/student2019/201520889/assembly.png differ
diff --git a/student2019/201520889/bayesian.png b/student2019/201520889/bayesian.png
new file mode 100644
index 0000000000000000000000000000000000000000..91685cccd258a2d8a736e01aab4e0943b51dd6e4
Binary files /dev/null and b/student2019/201520889/bayesian.png differ
diff --git a/student2019/201520889/bayesianCurve.js b/student2019/201520889/bayesianCurve.js
new file mode 100644
index 0000000000000000000000000000000000000000..258840998d33785a9bc04007652e7653958671e9
--- /dev/null
+++ b/student2019/201520889/bayesianCurve.js
@@ -0,0 +1,77 @@
+// CC-NC-BY Mingi Kim
+var bayesianPointStack = [];
+var bayesianPointNum = 0;
+
+function windowToCanvas(canvas, x, y) {
+    var bbox = canvas.getBoundingClientRect();
+ 
+    return { x: x - bbox.left * (canvas.width  / bbox.width),
+             y: y - bbox.top  * (canvas.height / bbox.height)
+           };
+ }
+
+function initBazierEventHandler(canvas, ctx) {
+    canvas.onmousedown = function (ev) {  //Mouse is pressed
+        if (bayesianPointNum > 3) {
+            alert("You only need to take 4 dots.");
+            return;
+        }
+        var loc = windowToCanvas(canvas, ev.clientX, ev.clientY);
+        var x = loc.x;
+        var y = loc.y;
+
+        if (bayesianPointNum == 0 || bayesianPointNum == 3) {
+            ctx.fillStyle = 'blue';
+        } else {
+            ctx.fillStyle = 'red';
+        }
+        ctx.beginPath();
+        ctx.arc(x, y, 5, 0, 2 * Math.PI);  // Start point
+        ctx.fill();
+        bayesianPointNum++;
+
+        bayesianPointStack.push(x);
+        bayesianPointStack.push(y);
+    };
+}
+
+
+var bmouse = [250, 250];
+var bmouseRadius = 10;
+var bpointsClicked = [];
+
+var bclickedPointNum = 0;
+
+function doBayesian() {
+    var bayesianCanvas = document.getElementById("bayesian");
+    var ctx = bayesianCanvas.getContext("2d");
+    initBazierEventHandler(bayesianCanvas, ctx);
+}
+
+function drawCurve() {
+    if (bayesianPointNum < 4) {
+        alert("You have to take four dots.");
+        return;
+    }
+    var bayesianCanvas = document.getElementById("bayesian");
+    var ctx = bayesianCanvas.getContext("2d");
+
+    let start = { x: bayesianPointStack[0], y: bayesianPointStack[1] };
+    let cp1 = { x: bayesianPointStack[2], y: bayesianPointStack[3] };
+    let cp2 = { x: bayesianPointStack[4], y: bayesianPointStack[5] };
+    let end = { x: bayesianPointStack[6], y: bayesianPointStack[7] };
+    // Cubic Bézier curve
+    ctx.beginPath();
+    ctx.moveTo(start.x, start.y);
+    ctx.bezierCurveTo(cp1.x, cp1.y, cp2.x, cp2.y, end.x, end.y);
+    ctx.stroke();
+}
+
+function clearCurve() {
+    var bayesianCanvas = document.getElementById("bayesian");
+    var ctx = bayesianCanvas.getContext("2d");
+
+    bayesianPointNum = 0;
+    bayesianPointStack = [];
+    ctx.clearRect(0, 0, bayesianCanvas.width, bayesianCanvas.height);
+}
\ No newline at end of file
diff --git a/student2019/201520889/img0.png b/student2019/201520889/img0.png
new file mode 100644
index 0000000000000000000000000000000000000000..18e41f37df85baf7590bfe8c8c1063b8659b570c
Binary files /dev/null and b/student2019/201520889/img0.png differ
diff --git a/student2019/201520889/img1.png b/student2019/201520889/img1.png
new file mode 100644
index 0000000000000000000000000000000000000000..91046bda5e8a4596374be56157601745ecd07a8f
Binary files /dev/null and b/student2019/201520889/img1.png differ
diff --git a/student2019/201520889/img2.png b/student2019/201520889/img2.png
new file mode 100644
index 0000000000000000000000000000000000000000..84ad50f93bbf129010c8f8da49804b26e5869156
Binary files /dev/null and b/student2019/201520889/img2.png differ
diff --git a/student2019/201520889/img3.png b/student2019/201520889/img3.png
new file mode 100644
index 0000000000000000000000000000000000000000..11daa1a6ccb026ce25d1fce4e58e89c5cb1865cd
Binary files /dev/null and b/student2019/201520889/img3.png differ
diff --git a/student2019/201520889/img4.png b/student2019/201520889/img4.png
new file mode 100644
index 0000000000000000000000000000000000000000..6a410ff4def2a072f7ca3b48401b972c1e09833d
Binary files /dev/null and b/student2019/201520889/img4.png differ
diff --git a/student2019/201520889/img5.png b/student2019/201520889/img5.png
new file mode 100644
index 0000000000000000000000000000000000000000..10b5e6616a70776919ae181cc725e6eecdbfa296
Binary files /dev/null and b/student2019/201520889/img5.png differ
diff --git a/student2019/201520889/img6.png b/student2019/201520889/img6.png
new file mode 100644
index 0000000000000000000000000000000000000000..5850644712d78fb1da8fd1084a1d34aac2002b2d
Binary files /dev/null and b/student2019/201520889/img6.png differ
diff --git a/student2019/201520889/img7.png b/student2019/201520889/img7.png
new file mode 100644
index 0000000000000000000000000000000000000000..bf309284107cefe5e266833d3265e95ca83d21e5
Binary files /dev/null and b/student2019/201520889/img7.png differ
diff --git a/student2019/201520889/index.html b/student2019/201520889/index.html
new file mode 100644
index 0000000000000000000000000000000000000000..280d382c74bdc09c75c66404e9a391980aadf2c2
--- /dev/null
+++ b/student2019/201520889/index.html
@@ -0,0 +1,81 @@
+<!-- (CC-NC-BY) 2019 Mingi Kim -->
+<html>
+
+<head>
+	<title>WebGL Tutorial - Primitive Assembly Example</title>
+	<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+	<script type="text/javascript" src="WebGLHelloAPI.js"></script>
+	<script type="text/javascript" src="bayesianCurve.js"></script>
+
+</head>
+
+<body onload="main();">
+	<canvas id="helloapicanvas" style="border: none; padding: 0%; margin: 0%;" width="500" height="500"></canvas>
+	<H2> WebGL - Mouse Event & Primitive Assembly </H2>
+	Primitive Assembly - Point/Line/Triangle
+	<table style="width:100%">
+		<tr>
+			<td>
+				<p>
+					<button onclick="changeModePLT()" id="butPLT"> P/L/T </button>
+					<button onclick="clearVertexData()" id="cvd"> clear </button>
+					<input type="color" name="colorInput" id="colorInput" value="#ff0000">
+					<p id="pointnum">0</p>
+					<p id="idPLT"> P/L/T </p>
+				</p>
+			</td>
+			<td>
+				<p>Points Stack(x, y)</p>
+				<p id="AllPoints"></p>
+			</td>
+		</tr>
+	</table>
+	<p>This is mouse click example with Primitive Assembly</p>
+	<p>
+		0. You can choose a type of Assembly with P/L/T button<br>
+		1. Click on the canvas with your mouse. Then you can see the cordination of the points<br>
+		2. You can choose color with Color Picker. Then you can see the Assembly.<br>
+		3. Press clear button to clear the canvas assembly.
+	</p>
+
+	<H3>Tutorial 1</H3>
+	<img src="img0.png" height="150" width="150">
+	<p>Click on the canvas and draw this red triangle.</p>
+	<H3>Tutorial 2</H3>
+	<img src="img1.png" height="150" width="150">
+	<p>Press clear button to remove. And change the color and draw a triangle.</p>
+	<H3>Tutorial 3</H3>
+	<img src="img2.png" height="150" width="150">
+	<p>Draw the triangles by making the colors of the points different.</p>
+	<H3>Tutorial 4</H3>
+	<img src="img3.png" height="150" width="150">
+	<p>Press the P / L / T button to draw the three lines.</p>
+	<H3>Tutorial 5</H3>
+	<img src="img4.png" height="150" width="150">
+	<p>Press the P / L / T button to change to triangle.</p>
+	<p></p>
+
+	<H2>WebGL - Mouse Event & Bayesian Curve</H2>
+	<canvas id="bayesian" style="border:1px solid #000000;" width="500" height="500""></canvas>
+	<button onclick="drawCurve()">bayesian</button>
+	<button onclick="clearCurve()">Clear Curve</button>
+
+	<p>
+		0. Click on the canvas with your mouse.<br>
+		1. Make 4 dots and Press the bayesian button. Then you can see the Curve<br>
+		2. Press clear button to clear the canvas.
+	</p>
+
+	<H3>Tutorial 1</H3>
+	<img src="img5.png" height="150" width="150">
+	<p>Let's take the four points and press the bayesian button.</p>
+	<H3>Tutorial 2</H3>
+	<img src="img6.png" height="150" width="150">
+	<p>To clear the curve, press the clear button and draw a curve like this.</p>
+	<H3>Tutorial 3</H3>
+	<img src="img7.png" height="150" width="150">
+	<p>To clear the curve, press the clear button and draw a curve like this</p>
+	<p> (CC-NC-BY) 2019 Mingi Kim </p>
+</body>
+
+</html>
\ No newline at end of file
diff --git a/student2019/201520889/tutorial0.png b/student2019/201520889/tutorial0.png
new file mode 100644
index 0000000000000000000000000000000000000000..446b44c9509e59700f775fecd186c4c08030a66c
Binary files /dev/null and b/student2019/201520889/tutorial0.png differ
diff --git a/student2019/201520889/tutorial1.png b/student2019/201520889/tutorial1.png
new file mode 100644
index 0000000000000000000000000000000000000000..5094cf2ce82a17b3eca0375a0f58a12c325f932c
Binary files /dev/null and b/student2019/201520889/tutorial1.png differ
diff --git a/student2019/201520889/webglfinalproject b/student2019/201520889/webglfinalproject
deleted file mode 160000
index 5904e223bb6f33849f1bc3d2702f5a1d1fa95b7c..0000000000000000000000000000000000000000
--- a/student2019/201520889/webglfinalproject
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 5904e223bb6f33849f1bc3d2702f5a1d1fa95b7c
diff --git "a/student2019/201520987/WebGL \353\263\264\352\263\240\354\204\234 - 201520987 \355\225\234\354\240\225\354\232\260.docx" b/student2019/201520987/report.docx
similarity index 100%
rename from "student2019/201520987/WebGL \353\263\264\352\263\240\354\204\234 - 201520987 \355\225\234\354\240\225\354\232\260.docx"
rename to student2019/201520987/report.docx