browser/devtools/tilt/tilt-math.js

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/browser/devtools/tilt/tilt-math.js	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,2322 @@
     1.4 +/* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* vim: set ts=2 et sw=2 tw=80: */
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +"use strict";
    1.10 +
    1.11 +const {Cu} = require("chrome");
    1.12 +
    1.13 +let TiltUtils = require("devtools/tilt/tilt-utils");
    1.14 +
    1.15 +/**
    1.16 + * Module containing high performance matrix and vector operations for WebGL.
    1.17 + * Inspired by glMatrix, version 0.9.6, (c) 2011 Brandon Jones.
    1.18 + */
    1.19 +
    1.20 +let EPSILON = 0.01;
    1.21 +exports.EPSILON = EPSILON;
    1.22 +
    1.23 +const PI_OVER_180 = Math.PI / 180;
    1.24 +const INV_PI_OVER_180 = 180 / Math.PI;
    1.25 +const FIFTEEN_OVER_225 = 15 / 225;
    1.26 +const ONE_OVER_255 = 1 / 255;
    1.27 +
    1.28 +/**
    1.29 + * vec3 - 3 Dimensional Vector.
    1.30 + */
    1.31 +let vec3 = {
    1.32 +
    1.33 +  /**
    1.34 +   * Creates a new instance of a vec3 using the Float32Array type.
    1.35 +   * Any array containing at least 3 numeric elements can serve as a vec3.
    1.36 +   *
    1.37 +   * @param {Array} aVec
    1.38 +   *                optional, vec3 containing values to initialize with
    1.39 +   *
    1.40 +   * @return {Array} a new instance of a vec3
    1.41 +   */
    1.42 +  create: function V3_create(aVec)
    1.43 +  {
    1.44 +    let dest = new Float32Array(3);
    1.45 +
    1.46 +    if (aVec) {
    1.47 +      vec3.set(aVec, dest);
    1.48 +    } else {
    1.49 +      vec3.zero(dest);
    1.50 +    }
    1.51 +    return dest;
    1.52 +  },
    1.53 +
    1.54 +  /**
    1.55 +   * Copies the values of one vec3 to another.
    1.56 +   *
    1.57 +   * @param {Array} aVec
    1.58 +   *                vec3 containing values to copy
    1.59 +   * @param {Array} aDest
    1.60 +   *                vec3 receiving copied values
    1.61 +   *
    1.62 +   * @return {Array} the destination vec3 receiving copied values
    1.63 +   */
    1.64 +  set: function V3_set(aVec, aDest)
    1.65 +  {
    1.66 +    aDest[0] = aVec[0];
    1.67 +    aDest[1] = aVec[1];
    1.68 +    aDest[2] = aVec[2] || 0;
    1.69 +    return aDest;
    1.70 +  },
    1.71 +
    1.72 +  /**
    1.73 +   * Sets a vec3 to an zero vector.
    1.74 +   *
    1.75 +   * @param {Array} aDest
    1.76 +   *                vec3 to set
    1.77 +   *
    1.78 +   * @return {Array} the same vector
    1.79 +   */
    1.80 +  zero: function V3_zero(aDest)
    1.81 +  {
    1.82 +    aDest[0] = 0;
    1.83 +    aDest[1] = 0;
    1.84 +    aDest[2] = 0;
    1.85 +    return aDest;
    1.86 +  },
    1.87 +
    1.88 +  /**
    1.89 +   * Performs a vector addition.
    1.90 +   *
    1.91 +   * @param {Array} aVec
    1.92 +   *                vec3, first operand
    1.93 +   * @param {Array} aVec2
    1.94 +   *                vec3, second operand
    1.95 +   * @param {Array} aDest
    1.96 +   *                optional, vec3 receiving operation result
    1.97 +   *                if not specified result is written to the first operand
    1.98 +   *
    1.99 +   * @return {Array} the destination vec3 if specified, first operand otherwise
   1.100 +   */
   1.101 +  add: function V3_add(aVec, aVec2, aDest)
   1.102 +  {
   1.103 +    if (!aDest) {
   1.104 +      aDest = aVec;
   1.105 +    }
   1.106 +
   1.107 +    aDest[0] = aVec[0] + aVec2[0];
   1.108 +    aDest[1] = aVec[1] + aVec2[1];
   1.109 +    aDest[2] = aVec[2] + aVec2[2];
   1.110 +    return aDest;
   1.111 +  },
   1.112 +
   1.113 +  /**
   1.114 +   * Performs a vector subtraction.
   1.115 +   *
   1.116 +   * @param {Array} aVec
   1.117 +   *                vec3, first operand
   1.118 +   * @param {Array} aVec2
   1.119 +   *                vec3, second operand
   1.120 +   * @param {Array} aDest
   1.121 +   *                optional, vec3 receiving operation result
   1.122 +   *                if not specified result is written to the first operand
   1.123 +   *
   1.124 +   * @return {Array} the destination vec3 if specified, first operand otherwise
   1.125 +   */
   1.126 +  subtract: function V3_subtract(aVec, aVec2, aDest)
   1.127 +  {
   1.128 +    if (!aDest) {
   1.129 +      aDest = aVec;
   1.130 +    }
   1.131 +
   1.132 +    aDest[0] = aVec[0] - aVec2[0];
   1.133 +    aDest[1] = aVec[1] - aVec2[1];
   1.134 +    aDest[2] = aVec[2] - aVec2[2];
   1.135 +    return aDest;
   1.136 +  },
   1.137 +
   1.138 +  /**
   1.139 +   * Negates the components of a vec3.
   1.140 +   *
   1.141 +   * @param {Array} aVec
   1.142 +   *                vec3 to negate
   1.143 +   * @param {Array} aDest
   1.144 +   *                optional, vec3 receiving operation result
   1.145 +   *                if not specified result is written to the first operand
   1.146 +   *
   1.147 +   * @return {Array} the destination vec3 if specified, first operand otherwise
   1.148 +   */
   1.149 +  negate: function V3_negate(aVec, aDest)
   1.150 +  {
   1.151 +    if (!aDest) {
   1.152 +      aDest = aVec;
   1.153 +    }
   1.154 +
   1.155 +    aDest[0] = -aVec[0];
   1.156 +    aDest[1] = -aVec[1];
   1.157 +    aDest[2] = -aVec[2];
   1.158 +    return aDest;
   1.159 +  },
   1.160 +
   1.161 +  /**
   1.162 +   * Multiplies the components of a vec3 by a scalar value.
   1.163 +   *
   1.164 +   * @param {Array} aVec
   1.165 +   *                vec3 to scale
   1.166 +   * @param {Number} aVal
   1.167 +   *                 numeric value to scale by
   1.168 +   * @param {Array} aDest
   1.169 +   *                optional, vec3 receiving operation result
   1.170 +   *                if not specified result is written to the first operand
   1.171 +   *
   1.172 +   * @return {Array} the destination vec3 if specified, first operand otherwise
   1.173 +   */
   1.174 +  scale: function V3_scale(aVec, aVal, aDest)
   1.175 +  {
   1.176 +    if (!aDest) {
   1.177 +      aDest = aVec;
   1.178 +    }
   1.179 +
   1.180 +    aDest[0] = aVec[0] * aVal;
   1.181 +    aDest[1] = aVec[1] * aVal;
   1.182 +    aDest[2] = aVec[2] * aVal;
   1.183 +    return aDest;
   1.184 +  },
   1.185 +
   1.186 +  /**
   1.187 +   * Generates a unit vector of the same direction as the provided vec3.
   1.188 +   * If vector length is 0, returns [0, 0, 0].
   1.189 +   *
   1.190 +   * @param {Array} aVec
   1.191 +   *                vec3 to normalize
   1.192 +   * @param {Array} aDest
   1.193 +   *                optional, vec3 receiving operation result
   1.194 +   *                if not specified result is written to the first operand
   1.195 +   *
   1.196 +   * @return {Array} the destination vec3 if specified, first operand otherwise
   1.197 +   */
   1.198 +  normalize: function V3_normalize(aVec, aDest)
   1.199 +  {
   1.200 +    if (!aDest) {
   1.201 +      aDest = aVec;
   1.202 +    }
   1.203 +
   1.204 +    let x = aVec[0];
   1.205 +    let y = aVec[1];
   1.206 +    let z = aVec[2];
   1.207 +    let len = Math.sqrt(x * x + y * y + z * z);
   1.208 +
   1.209 +    if (Math.abs(len) < EPSILON) {
   1.210 +      aDest[0] = 0;
   1.211 +      aDest[1] = 0;
   1.212 +      aDest[2] = 0;
   1.213 +      return aDest;
   1.214 +    }
   1.215 +
   1.216 +    len = 1 / len;
   1.217 +    aDest[0] = x * len;
   1.218 +    aDest[1] = y * len;
   1.219 +    aDest[2] = z * len;
   1.220 +    return aDest;
   1.221 +  },
   1.222 +
   1.223 +  /**
   1.224 +   * Generates the cross product of two vectors.
   1.225 +   *
   1.226 +   * @param {Array} aVec
   1.227 +   *                vec3, first operand
   1.228 +   * @param {Array} aVec2
   1.229 +   *                vec3, second operand
   1.230 +   * @param {Array} aDest
   1.231 +   *                optional, vec3 receiving operation result
   1.232 +   *                if not specified result is written to the first operand
   1.233 +   *
   1.234 +   * @return {Array} the destination vec3 if specified, first operand otherwise
   1.235 +   */
   1.236 +  cross: function V3_cross(aVec, aVec2, aDest)
   1.237 +  {
   1.238 +    if (!aDest) {
   1.239 +      aDest = aVec;
   1.240 +    }
   1.241 +
   1.242 +    let x = aVec[0];
   1.243 +    let y = aVec[1];
   1.244 +    let z = aVec[2];
   1.245 +    let x2 = aVec2[0];
   1.246 +    let y2 = aVec2[1];
   1.247 +    let z2 = aVec2[2];
   1.248 +
   1.249 +    aDest[0] = y * z2 - z * y2;
   1.250 +    aDest[1] = z * x2 - x * z2;
   1.251 +    aDest[2] = x * y2 - y * x2;
   1.252 +    return aDest;
   1.253 +  },
   1.254 +
   1.255 +  /**
   1.256 +   * Caclulate the dot product of two vectors.
   1.257 +   *
   1.258 +   * @param {Array} aVec
   1.259 +   *                vec3, first operand
   1.260 +   * @param {Array} aVec2
   1.261 +   *                vec3, second operand
   1.262 +   *
   1.263 +   * @return {Array} dot product of the first and second operand
   1.264 +   */
   1.265 +  dot: function V3_dot(aVec, aVec2)
   1.266 +  {
   1.267 +    return aVec[0] * aVec2[0] + aVec[1] * aVec2[1] + aVec[2] * aVec2[2];
   1.268 +  },
   1.269 +
   1.270 +  /**
   1.271 +   * Caclulate the length of a vec3.
   1.272 +   *
   1.273 +   * @param {Array} aVec
   1.274 +   *                vec3 to calculate length of
   1.275 +   *
   1.276 +   * @return {Array} length of the vec3
   1.277 +   */
   1.278 +  length: function V3_length(aVec)
   1.279 +  {
   1.280 +    let x = aVec[0];
   1.281 +    let y = aVec[1];
   1.282 +    let z = aVec[2];
   1.283 +
   1.284 +    return Math.sqrt(x * x + y * y + z * z);
   1.285 +  },
   1.286 +
   1.287 +  /**
   1.288 +   * Generates a unit vector pointing from one vector to another.
   1.289 +   *
   1.290 +   * @param {Array} aVec
   1.291 +   *                origin vec3
   1.292 +   * @param {Array} aVec2
   1.293 +   *                vec3 to point to
   1.294 +   * @param {Array} aDest
   1.295 +   *                optional, vec3 receiving operation result
   1.296 +   *                if not specified result is written to the first operand
   1.297 +   *
   1.298 +   * @return {Array} the destination vec3 if specified, first operand otherwise
   1.299 +   */
   1.300 +  direction: function V3_direction(aVec, aVec2, aDest)
   1.301 +  {
   1.302 +    if (!aDest) {
   1.303 +      aDest = aVec;
   1.304 +    }
   1.305 +
   1.306 +    let x = aVec[0] - aVec2[0];
   1.307 +    let y = aVec[1] - aVec2[1];
   1.308 +    let z = aVec[2] - aVec2[2];
   1.309 +    let len = Math.sqrt(x * x + y * y + z * z);
   1.310 +
   1.311 +    if (Math.abs(len) < EPSILON) {
   1.312 +      aDest[0] = 0;
   1.313 +      aDest[1] = 0;
   1.314 +      aDest[2] = 0;
   1.315 +      return aDest;
   1.316 +    }
   1.317 +
   1.318 +    len = 1 / len;
   1.319 +    aDest[0] = x * len;
   1.320 +    aDest[1] = y * len;
   1.321 +    aDest[2] = z * len;
   1.322 +    return aDest;
   1.323 +  },
   1.324 +
   1.325 +  /**
   1.326 +   * Performs a linear interpolation between two vec3.
   1.327 +   *
   1.328 +   * @param {Array} aVec
   1.329 +   *                first vector
   1.330 +   * @param {Array} aVec2
   1.331 +   *                second vector
   1.332 +   * @param {Number} aLerp
   1.333 +   *                 interpolation amount between the two inputs
   1.334 +   * @param {Array} aDest
   1.335 +   *                optional, vec3 receiving operation result
   1.336 +   *                if not specified result is written to the first operand
   1.337 +   *
   1.338 +   * @return {Array} the destination vec3 if specified, first operand otherwise
   1.339 +   */
   1.340 +  lerp: function V3_lerp(aVec, aVec2, aLerp, aDest)
   1.341 +  {
   1.342 +    if (!aDest) {
   1.343 +      aDest = aVec;
   1.344 +    }
   1.345 +
   1.346 +    aDest[0] = aVec[0] + aLerp * (aVec2[0] - aVec[0]);
   1.347 +    aDest[1] = aVec[1] + aLerp * (aVec2[1] - aVec[1]);
   1.348 +    aDest[2] = aVec[2] + aLerp * (aVec2[2] - aVec[2]);
   1.349 +    return aDest;
   1.350 +  },
   1.351 +
   1.352 +  /**
   1.353 +   * Projects a 3D point on a 2D screen plane.
   1.354 +   *
   1.355 +   * @param {Array} aP
   1.356 +   *                the [x, y, z] coordinates of the point to project
   1.357 +   * @param {Array} aViewport
   1.358 +   *                the viewport [x, y, width, height] coordinates
   1.359 +   * @param {Array} aMvMatrix
   1.360 +   *                the model view matrix
   1.361 +   * @param {Array} aProjMatrix
   1.362 +   *                the projection matrix
   1.363 +   * @param {Array} aDest
   1.364 +   *                optional parameter, the array to write the values to
   1.365 +   *
   1.366 +   * @return {Array} the projected coordinates
   1.367 +   */
   1.368 +  project: function V3_project(aP, aViewport, aMvMatrix, aProjMatrix, aDest)
   1.369 +  {
   1.370 +    /*jshint undef: false */
   1.371 +
   1.372 +    let mvpMatrix = new Float32Array(16);
   1.373 +    let coordinates = new Float32Array(4);
   1.374 +
   1.375 +    // compute the perspective * model view matrix
   1.376 +    mat4.multiply(aProjMatrix, aMvMatrix, mvpMatrix);
   1.377 +
   1.378 +    // now transform that vector into homogenous coordinates
   1.379 +    coordinates[0] = aP[0];
   1.380 +    coordinates[1] = aP[1];
   1.381 +    coordinates[2] = aP[2];
   1.382 +    coordinates[3] = 1;
   1.383 +    mat4.multiplyVec4(mvpMatrix, coordinates);
   1.384 +
   1.385 +    // transform the homogenous coordinates into screen space
   1.386 +    coordinates[0] /= coordinates[3];
   1.387 +    coordinates[0] *= aViewport[2] * 0.5;
   1.388 +    coordinates[0] += aViewport[2] * 0.5;
   1.389 +    coordinates[1] /= coordinates[3];
   1.390 +    coordinates[1] *= -aViewport[3] * 0.5;
   1.391 +    coordinates[1] += aViewport[3] * 0.5;
   1.392 +    coordinates[2] = 0;
   1.393 +
   1.394 +    if (!aDest) {
   1.395 +      vec3.set(coordinates, aP);
   1.396 +    } else {
   1.397 +      vec3.set(coordinates, aDest);
   1.398 +    }
   1.399 +    return coordinates;
   1.400 +  },
   1.401 +
   1.402 +  /**
   1.403 +   * Unprojects a 2D point to 3D space.
   1.404 +   *
   1.405 +   * @param {Array} aP
   1.406 +   *                the [x, y, z] coordinates of the point to unproject;
   1.407 +   *                the z value should range between 0 and 1, as clipping plane
   1.408 +   * @param {Array} aViewport
   1.409 +   *                the viewport [x, y, width, height] coordinates
   1.410 +   * @param {Array} aMvMatrix
   1.411 +   *                the model view matrix
   1.412 +   * @param {Array} aProjMatrix
   1.413 +   *                the projection matrix
   1.414 +   * @param {Array} aDest
   1.415 +   *                optional parameter, the array to write the values to
   1.416 +   *
   1.417 +   * @return {Array} the unprojected coordinates
   1.418 +   */
   1.419 +  unproject: function V3_unproject(
   1.420 +    aP, aViewport, aMvMatrix, aProjMatrix, aDest)
   1.421 +  {
   1.422 +    /*jshint undef: false */
   1.423 +
   1.424 +    let mvpMatrix = new Float32Array(16);
   1.425 +    let coordinates = new Float32Array(4);
   1.426 +
   1.427 +    // compute the inverse of the perspective * model view matrix
   1.428 +    mat4.multiply(aProjMatrix, aMvMatrix, mvpMatrix);
   1.429 +    mat4.inverse(mvpMatrix);
   1.430 +
   1.431 +    // transformation of normalized coordinates (-1 to 1)
   1.432 +    coordinates[0] = +((aP[0] - aViewport[0]) / aViewport[2] * 2 - 1);
   1.433 +    coordinates[1] = -((aP[1] - aViewport[1]) / aViewport[3] * 2 - 1);
   1.434 +    coordinates[2] = 2 * aP[2] - 1;
   1.435 +    coordinates[3] = 1;
   1.436 +
   1.437 +    // now transform that vector into space coordinates
   1.438 +    mat4.multiplyVec4(mvpMatrix, coordinates);
   1.439 +
   1.440 +    // invert to normalize x, y, and z values
   1.441 +    coordinates[3] = 1 / coordinates[3];
   1.442 +    coordinates[0] *= coordinates[3];
   1.443 +    coordinates[1] *= coordinates[3];
   1.444 +    coordinates[2] *= coordinates[3];
   1.445 +
   1.446 +    if (!aDest) {
   1.447 +      vec3.set(coordinates, aP);
   1.448 +    } else {
   1.449 +      vec3.set(coordinates, aDest);
   1.450 +    }
   1.451 +    return coordinates;
   1.452 +  },
   1.453 +
   1.454 +  /**
   1.455 +   * Create a ray between two points using the current model view & projection
   1.456 +   * matrices. This is useful when creating a ray destined for 3D picking.
   1.457 +   *
   1.458 +   * @param {Array} aP0
   1.459 +   *                the [x, y, z] coordinates of the first point
   1.460 +   * @param {Array} aP1
   1.461 +   *                the [x, y, z] coordinates of the second point
   1.462 +   * @param {Array} aViewport
   1.463 +   *                the viewport [x, y, width, height] coordinates
   1.464 +   * @param {Array} aMvMatrix
   1.465 +   *                the model view matrix
   1.466 +   * @param {Array} aProjMatrix
   1.467 +   *                the projection matrix
   1.468 +   *
   1.469 +   * @return {Object} a ray object containing the direction vector between
   1.470 +   * the two unprojected points, the position and the lookAt
   1.471 +   */
   1.472 +  createRay: function V3_createRay(aP0, aP1, aViewport, aMvMatrix, aProjMatrix)
   1.473 +  {
   1.474 +    // unproject the two points
   1.475 +    vec3.unproject(aP0, aViewport, aMvMatrix, aProjMatrix, aP0);
   1.476 +    vec3.unproject(aP1, aViewport, aMvMatrix, aProjMatrix, aP1);
   1.477 +
   1.478 +    return {
   1.479 +      origin: aP0,
   1.480 +      direction: vec3.normalize(vec3.subtract(aP1, aP0))
   1.481 +    };
   1.482 +  },
   1.483 +
   1.484 +  /**
   1.485 +   * Returns a string representation of a vector.
   1.486 +   *
   1.487 +   * @param {Array} aVec
   1.488 +   *                vec3 to represent as a string
   1.489 +   *
   1.490 +   * @return {String} representation of the vector
   1.491 +   */
   1.492 +  str: function V3_str(aVec)
   1.493 +  {
   1.494 +    return '[' + aVec[0] + ", " + aVec[1] + ", " + aVec[2] + ']';
   1.495 +  }
   1.496 +};
   1.497 +
   1.498 +exports.vec3 = vec3;
   1.499 +
   1.500 +/**
   1.501 + * mat3 - 3x3 Matrix.
   1.502 + */
   1.503 +let mat3 = {
   1.504 +
   1.505 +  /**
   1.506 +   * Creates a new instance of a mat3 using the Float32Array array type.
   1.507 +   * Any array containing at least 9 numeric elements can serve as a mat3.
   1.508 +   *
   1.509 +   * @param {Array} aMat
   1.510 +   *                optional, mat3 containing values to initialize with
   1.511 +   *
   1.512 +   * @return {Array} a new instance of a mat3
   1.513 +   */
   1.514 +  create: function M3_create(aMat)
   1.515 +  {
   1.516 +    let dest = new Float32Array(9);
   1.517 +
   1.518 +    if (aMat) {
   1.519 +      mat3.set(aMat, dest);
   1.520 +    } else {
   1.521 +      mat3.identity(dest);
   1.522 +    }
   1.523 +    return dest;
   1.524 +  },
   1.525 +
   1.526 +  /**
   1.527 +   * Copies the values of one mat3 to another.
   1.528 +   *
   1.529 +   * @param {Array} aMat
   1.530 +   *                mat3 containing values to copy
   1.531 +   * @param {Array} aDest
   1.532 +   *                mat3 receiving copied values
   1.533 +   *
   1.534 +   * @return {Array} the destination mat3 receiving copied values
   1.535 +   */
   1.536 +  set: function M3_set(aMat, aDest)
   1.537 +  {
   1.538 +    aDest[0] = aMat[0];
   1.539 +    aDest[1] = aMat[1];
   1.540 +    aDest[2] = aMat[2];
   1.541 +    aDest[3] = aMat[3];
   1.542 +    aDest[4] = aMat[4];
   1.543 +    aDest[5] = aMat[5];
   1.544 +    aDest[6] = aMat[6];
   1.545 +    aDest[7] = aMat[7];
   1.546 +    aDest[8] = aMat[8];
   1.547 +    return aDest;
   1.548 +  },
   1.549 +
   1.550 +  /**
   1.551 +   * Sets a mat3 to an identity matrix.
   1.552 +   *
   1.553 +   * @param {Array} aDest
   1.554 +   *                mat3 to set
   1.555 +   *
   1.556 +   * @return {Array} the same matrix
   1.557 +   */
   1.558 +  identity: function M3_identity(aDest)
   1.559 +  {
   1.560 +    aDest[0] = 1;
   1.561 +    aDest[1] = 0;
   1.562 +    aDest[2] = 0;
   1.563 +    aDest[3] = 0;
   1.564 +    aDest[4] = 1;
   1.565 +    aDest[5] = 0;
   1.566 +    aDest[6] = 0;
   1.567 +    aDest[7] = 0;
   1.568 +    aDest[8] = 1;
   1.569 +    return aDest;
   1.570 +  },
   1.571 +
   1.572 +  /**
   1.573 +   * Transposes a mat3 (flips the values over the diagonal).
   1.574 +   *
   1.575 +   * @param {Array} aMat
   1.576 +   *                mat3 to transpose
   1.577 +   * @param {Array} aDest
   1.578 +   *                optional, mat3 receiving operation result
   1.579 +   *                if not specified result is written to the first operand
   1.580 +   *
   1.581 +   * @return {Array} the destination mat3 if specified, first operand otherwise
   1.582 +   */
   1.583 +  transpose: function M3_transpose(aMat, aDest)
   1.584 +  {
   1.585 +    if (!aDest || aMat === aDest) {
   1.586 +      let a01 = aMat[1];
   1.587 +      let a02 = aMat[2];
   1.588 +      let a12 = aMat[5];
   1.589 +
   1.590 +      aMat[1] = aMat[3];
   1.591 +      aMat[2] = aMat[6];
   1.592 +      aMat[3] = a01;
   1.593 +      aMat[5] = aMat[7];
   1.594 +      aMat[6] = a02;
   1.595 +      aMat[7] = a12;
   1.596 +      return aMat;
   1.597 +    }
   1.598 +
   1.599 +    aDest[0] = aMat[0];
   1.600 +    aDest[1] = aMat[3];
   1.601 +    aDest[2] = aMat[6];
   1.602 +    aDest[3] = aMat[1];
   1.603 +    aDest[4] = aMat[4];
   1.604 +    aDest[5] = aMat[7];
   1.605 +    aDest[6] = aMat[2];
   1.606 +    aDest[7] = aMat[5];
   1.607 +    aDest[8] = aMat[8];
   1.608 +    return aDest;
   1.609 +  },
   1.610 +
   1.611 +  /**
   1.612 +   * Copies the elements of a mat3 into the upper 3x3 elements of a mat4.
   1.613 +   *
   1.614 +   * @param {Array} aMat
   1.615 +   *                mat3 containing values to copy
   1.616 +   * @param {Array} aDest
   1.617 +   *                optional, mat4 receiving operation result
   1.618 +   *                if not specified result is written to the first operand
   1.619 +   *
   1.620 +   * @return {Array} the destination mat3 if specified, first operand otherwise
   1.621 +   */
   1.622 +  toMat4: function M3_toMat4(aMat, aDest)
   1.623 +  {
   1.624 +    if (!aDest) {
   1.625 +      aDest = new Float32Array(16);
   1.626 +    }
   1.627 +
   1.628 +    aDest[0] = aMat[0];
   1.629 +    aDest[1] = aMat[1];
   1.630 +    aDest[2] = aMat[2];
   1.631 +    aDest[3] = 0;
   1.632 +    aDest[4] = aMat[3];
   1.633 +    aDest[5] = aMat[4];
   1.634 +    aDest[6] = aMat[5];
   1.635 +    aDest[7] = 0;
   1.636 +    aDest[8] = aMat[6];
   1.637 +    aDest[9] = aMat[7];
   1.638 +    aDest[10] = aMat[8];
   1.639 +    aDest[11] = 0;
   1.640 +    aDest[12] = 0;
   1.641 +    aDest[13] = 0;
   1.642 +    aDest[14] = 0;
   1.643 +    aDest[15] = 1;
   1.644 +    return aDest;
   1.645 +  },
   1.646 +
   1.647 +  /**
   1.648 +   * Returns a string representation of a 3x3 matrix.
   1.649 +   *
   1.650 +   * @param {Array} aMat
   1.651 +   *                mat3 to represent as a string
   1.652 +   *
   1.653 +   * @return {String} representation of the matrix
   1.654 +   */
   1.655 +  str: function M3_str(aMat)
   1.656 +  {
   1.657 +    return "["  + aMat[0] + ", " + aMat[1] + ", " + aMat[2] +
   1.658 +           ", " + aMat[3] + ", " + aMat[4] + ", " + aMat[5] +
   1.659 +           ", " + aMat[6] + ", " + aMat[7] + ", " + aMat[8] + "]";
   1.660 +  }
   1.661 +};
   1.662 +
   1.663 +exports.mat3 = mat3;
   1.664 +
   1.665 +/**
   1.666 + * mat4 - 4x4 Matrix.
   1.667 + */
   1.668 +let mat4 = {
   1.669 +
   1.670 +  /**
   1.671 +   * Creates a new instance of a mat4 using the default Float32Array type.
   1.672 +   * Any array containing at least 16 numeric elements can serve as a mat4.
   1.673 +   *
   1.674 +   * @param {Array} aMat
   1.675 +   *                optional, mat4 containing values to initialize with
   1.676 +   *
   1.677 +   * @return {Array} a new instance of a mat4
   1.678 +   */
   1.679 +  create: function M4_create(aMat)
   1.680 +  {
   1.681 +    let dest = new Float32Array(16);
   1.682 +
   1.683 +    if (aMat) {
   1.684 +      mat4.set(aMat, dest);
   1.685 +    } else {
   1.686 +      mat4.identity(dest);
   1.687 +    }
   1.688 +    return dest;
   1.689 +  },
   1.690 +
   1.691 +  /**
   1.692 +   * Copies the values of one mat4 to another
   1.693 +   *
   1.694 +   * @param {Array} aMat
   1.695 +   *                mat4 containing values to copy
   1.696 +   * @param {Array} aDest
   1.697 +   *                mat4 receiving copied values
   1.698 +   *
   1.699 +   * @return {Array} the destination mat4 receiving copied values
   1.700 +   */
   1.701 +  set: function M4_set(aMat, aDest)
   1.702 +  {
   1.703 +    aDest[0] = aMat[0];
   1.704 +    aDest[1] = aMat[1];
   1.705 +    aDest[2] = aMat[2];
   1.706 +    aDest[3] = aMat[3];
   1.707 +    aDest[4] = aMat[4];
   1.708 +    aDest[5] = aMat[5];
   1.709 +    aDest[6] = aMat[6];
   1.710 +    aDest[7] = aMat[7];
   1.711 +    aDest[8] = aMat[8];
   1.712 +    aDest[9] = aMat[9];
   1.713 +    aDest[10] = aMat[10];
   1.714 +    aDest[11] = aMat[11];
   1.715 +    aDest[12] = aMat[12];
   1.716 +    aDest[13] = aMat[13];
   1.717 +    aDest[14] = aMat[14];
   1.718 +    aDest[15] = aMat[15];
   1.719 +    return aDest;
   1.720 +  },
   1.721 +
   1.722 +  /**
   1.723 +   * Sets a mat4 to an identity matrix.
   1.724 +   *
   1.725 +   * @param {Array} aDest
   1.726 +   *                mat4 to set
   1.727 +   *
   1.728 +   * @return {Array} the same matrix
   1.729 +   */
   1.730 +  identity: function M4_identity(aDest)
   1.731 +  {
   1.732 +    aDest[0] = 1;
   1.733 +    aDest[1] = 0;
   1.734 +    aDest[2] = 0;
   1.735 +    aDest[3] = 0;
   1.736 +    aDest[4] = 0;
   1.737 +    aDest[5] = 1;
   1.738 +    aDest[6] = 0;
   1.739 +    aDest[7] = 0;
   1.740 +    aDest[8] = 0;
   1.741 +    aDest[9] = 0;
   1.742 +    aDest[10] = 1;
   1.743 +    aDest[11] = 0;
   1.744 +    aDest[12] = 0;
   1.745 +    aDest[13] = 0;
   1.746 +    aDest[14] = 0;
   1.747 +    aDest[15] = 1;
   1.748 +    return aDest;
   1.749 +  },
   1.750 +
   1.751 +  /**
   1.752 +   * Transposes a mat4 (flips the values over the diagonal).
   1.753 +   *
   1.754 +   * @param {Array} aMat
   1.755 +   *                mat4 to transpose
   1.756 +   * @param {Array} aDest
   1.757 +   *                optional, mat4 receiving operation result
   1.758 +   *                if not specified result is written to the first operand
   1.759 +   *
   1.760 +   * @return {Array} the destination mat4 if specified, first operand otherwise
   1.761 +   */
   1.762 +  transpose: function M4_transpose(aMat, aDest)
   1.763 +  {
   1.764 +    if (!aDest || aMat === aDest) {
   1.765 +      let a01 = aMat[1];
   1.766 +      let a02 = aMat[2];
   1.767 +      let a03 = aMat[3];
   1.768 +      let a12 = aMat[6];
   1.769 +      let a13 = aMat[7];
   1.770 +      let a23 = aMat[11];
   1.771 +
   1.772 +      aMat[1] = aMat[4];
   1.773 +      aMat[2] = aMat[8];
   1.774 +      aMat[3] = aMat[12];
   1.775 +      aMat[4] = a01;
   1.776 +      aMat[6] = aMat[9];
   1.777 +      aMat[7] = aMat[13];
   1.778 +      aMat[8] = a02;
   1.779 +      aMat[9] = a12;
   1.780 +      aMat[11] = aMat[14];
   1.781 +      aMat[12] = a03;
   1.782 +      aMat[13] = a13;
   1.783 +      aMat[14] = a23;
   1.784 +      return aMat;
   1.785 +    }
   1.786 +
   1.787 +    aDest[0] = aMat[0];
   1.788 +    aDest[1] = aMat[4];
   1.789 +    aDest[2] = aMat[8];
   1.790 +    aDest[3] = aMat[12];
   1.791 +    aDest[4] = aMat[1];
   1.792 +    aDest[5] = aMat[5];
   1.793 +    aDest[6] = aMat[9];
   1.794 +    aDest[7] = aMat[13];
   1.795 +    aDest[8] = aMat[2];
   1.796 +    aDest[9] = aMat[6];
   1.797 +    aDest[10] = aMat[10];
   1.798 +    aDest[11] = aMat[14];
   1.799 +    aDest[12] = aMat[3];
   1.800 +    aDest[13] = aMat[7];
   1.801 +    aDest[14] = aMat[11];
   1.802 +    aDest[15] = aMat[15];
   1.803 +    return aDest;
   1.804 +  },
   1.805 +
   1.806 +  /**
   1.807 +   * Calculate the determinant of a mat4.
   1.808 +   *
   1.809 +   * @param {Array} aMat
   1.810 +   *                mat4 to calculate determinant of
   1.811 +   *
   1.812 +   * @return {Number} determinant of the matrix
   1.813 +   */
   1.814 +  determinant: function M4_determinant(mat)
   1.815 +  {
   1.816 +    let a00 = mat[0], a01 = mat[1], a02 = mat[2], a03 = mat[3];
   1.817 +    let a10 = mat[4], a11 = mat[5], a12 = mat[6], a13 = mat[7];
   1.818 +    let a20 = mat[8], a21 = mat[9], a22 = mat[10], a23 = mat[11];
   1.819 +    let a30 = mat[12], a31 = mat[13], a32 = mat[14], a33 = mat[15];
   1.820 +
   1.821 +    return a30 * a21 * a12 * a03 - a20 * a31 * a12 * a03 -
   1.822 +           a30 * a11 * a22 * a03 + a10 * a31 * a22 * a03 +
   1.823 +           a20 * a11 * a32 * a03 - a10 * a21 * a32 * a03 -
   1.824 +           a30 * a21 * a02 * a13 + a20 * a31 * a02 * a13 +
   1.825 +           a30 * a01 * a22 * a13 - a00 * a31 * a22 * a13 -
   1.826 +           a20 * a01 * a32 * a13 + a00 * a21 * a32 * a13 +
   1.827 +           a30 * a11 * a02 * a23 - a10 * a31 * a02 * a23 -
   1.828 +           a30 * a01 * a12 * a23 + a00 * a31 * a12 * a23 +
   1.829 +           a10 * a01 * a32 * a23 - a00 * a11 * a32 * a23 -
   1.830 +           a20 * a11 * a02 * a33 + a10 * a21 * a02 * a33 +
   1.831 +           a20 * a01 * a12 * a33 - a00 * a21 * a12 * a33 -
   1.832 +           a10 * a01 * a22 * a33 + a00 * a11 * a22 * a33;
   1.833 +  },
   1.834 +
   1.835 +  /**
   1.836 +   * Calculate the inverse of a mat4.
   1.837 +   *
   1.838 +   * @param {Array} aMat
   1.839 +   *                mat4 to calculate inverse of
   1.840 +   * @param {Array} aDest
   1.841 +   *                optional, mat4 receiving operation result
   1.842 +   *                if not specified result is written to the first operand
   1.843 +   *
   1.844 +   * @return {Array} the destination mat4 if specified, first operand otherwise
   1.845 +   */
   1.846 +  inverse: function M4_inverse(aMat, aDest)
   1.847 +  {
   1.848 +    if (!aDest) {
   1.849 +      aDest = aMat;
   1.850 +    }
   1.851 +
   1.852 +    let a00 = aMat[0], a01 = aMat[1], a02 = aMat[2], a03 = aMat[3];
   1.853 +    let a10 = aMat[4], a11 = aMat[5], a12 = aMat[6], a13 = aMat[7];
   1.854 +    let a20 = aMat[8], a21 = aMat[9], a22 = aMat[10], a23 = aMat[11];
   1.855 +    let a30 = aMat[12], a31 = aMat[13], a32 = aMat[14], a33 = aMat[15];
   1.856 +
   1.857 +    let b00 = a00 * a11 - a01 * a10;
   1.858 +    let b01 = a00 * a12 - a02 * a10;
   1.859 +    let b02 = a00 * a13 - a03 * a10;
   1.860 +    let b03 = a01 * a12 - a02 * a11;
   1.861 +    let b04 = a01 * a13 - a03 * a11;
   1.862 +    let b05 = a02 * a13 - a03 * a12;
   1.863 +    let b06 = a20 * a31 - a21 * a30;
   1.864 +    let b07 = a20 * a32 - a22 * a30;
   1.865 +    let b08 = a20 * a33 - a23 * a30;
   1.866 +    let b09 = a21 * a32 - a22 * a31;
   1.867 +    let b10 = a21 * a33 - a23 * a31;
   1.868 +    let b11 = a22 * a33 - a23 * a32;
   1.869 +    let id = 1 / ((b00 * b11 - b01 * b10 + b02 * b09 +
   1.870 +                   b03 * b08 - b04 * b07 + b05 * b06) || EPSILON);
   1.871 +
   1.872 +    aDest[0] = ( a11 * b11 - a12 * b10 + a13 * b09) * id;
   1.873 +    aDest[1] = (-a01 * b11 + a02 * b10 - a03 * b09) * id;
   1.874 +    aDest[2] = ( a31 * b05 - a32 * b04 + a33 * b03) * id;
   1.875 +    aDest[3] = (-a21 * b05 + a22 * b04 - a23 * b03) * id;
   1.876 +    aDest[4] = (-a10 * b11 + a12 * b08 - a13 * b07) * id;
   1.877 +    aDest[5] = ( a00 * b11 - a02 * b08 + a03 * b07) * id;
   1.878 +    aDest[6] = (-a30 * b05 + a32 * b02 - a33 * b01) * id;
   1.879 +    aDest[7] = ( a20 * b05 - a22 * b02 + a23 * b01) * id;
   1.880 +    aDest[8] = ( a10 * b10 - a11 * b08 + a13 * b06) * id;
   1.881 +    aDest[9] = (-a00 * b10 + a01 * b08 - a03 * b06) * id;
   1.882 +    aDest[10] = ( a30 * b04 - a31 * b02 + a33 * b00) * id;
   1.883 +    aDest[11] = (-a20 * b04 + a21 * b02 - a23 * b00) * id;
   1.884 +    aDest[12] = (-a10 * b09 + a11 * b07 - a12 * b06) * id;
   1.885 +    aDest[13] = ( a00 * b09 - a01 * b07 + a02 * b06) * id;
   1.886 +    aDest[14] = (-a30 * b03 + a31 * b01 - a32 * b00) * id;
   1.887 +    aDest[15] = ( a20 * b03 - a21 * b01 + a22 * b00) * id;
   1.888 +    return aDest;
   1.889 +  },
   1.890 +
   1.891 +  /**
   1.892 +   * Copies the upper 3x3 elements of a mat4 into another mat4.
   1.893 +   *
   1.894 +   * @param {Array} aMat
   1.895 +   *                mat4 containing values to copy
   1.896 +   * @param {Array} aDest
   1.897 +   *                optional, mat4 receiving operation result
   1.898 +   *                if not specified result is written to the first operand
   1.899 +   *
   1.900 +   * @return {Array} the destination mat4 if specified, first operand otherwise
   1.901 +   */
   1.902 +  toRotationMat: function M4_toRotationMat(aMat, aDest)
   1.903 +  {
   1.904 +    if (!aDest) {
   1.905 +      aDest = new Float32Array(16);
   1.906 +    }
   1.907 +
   1.908 +    aDest[0] = aMat[0];
   1.909 +    aDest[1] = aMat[1];
   1.910 +    aDest[2] = aMat[2];
   1.911 +    aDest[3] = aMat[3];
   1.912 +    aDest[4] = aMat[4];
   1.913 +    aDest[5] = aMat[5];
   1.914 +    aDest[6] = aMat[6];
   1.915 +    aDest[7] = aMat[7];
   1.916 +    aDest[8] = aMat[8];
   1.917 +    aDest[9] = aMat[9];
   1.918 +    aDest[10] = aMat[10];
   1.919 +    aDest[11] = aMat[11];
   1.920 +    aDest[12] = 0;
   1.921 +    aDest[13] = 0;
   1.922 +    aDest[14] = 0;
   1.923 +    aDest[15] = 1;
   1.924 +    return aDest;
   1.925 +  },
   1.926 +
   1.927 +  /**
   1.928 +   * Copies the upper 3x3 elements of a mat4 into a mat3.
   1.929 +   *
   1.930 +   * @param {Array} aMat
   1.931 +   *                mat4 containing values to copy
   1.932 +   * @param {Array} aDest
   1.933 +   *                optional, mat3 receiving operation result
   1.934 +   *                if not specified result is written to the first operand
   1.935 +   *
   1.936 +   * @return {Array} the destination mat3 if specified, first operand otherwise
   1.937 +   */
   1.938 +  toMat3: function M4_toMat3(aMat, aDest)
   1.939 +  {
   1.940 +    if (!aDest) {
   1.941 +      aDest = new Float32Array(9);
   1.942 +    }
   1.943 +
   1.944 +    aDest[0] = aMat[0];
   1.945 +    aDest[1] = aMat[1];
   1.946 +    aDest[2] = aMat[2];
   1.947 +    aDest[3] = aMat[4];
   1.948 +    aDest[4] = aMat[5];
   1.949 +    aDest[5] = aMat[6];
   1.950 +    aDest[6] = aMat[8];
   1.951 +    aDest[7] = aMat[9];
   1.952 +    aDest[8] = aMat[10];
   1.953 +    return aDest;
   1.954 +  },
   1.955 +
   1.956 +  /**
   1.957 +   * Calculate the inverse of the upper 3x3 elements of a mat4 and copies
   1.958 +   * the result into a mat3. The resulting matrix is useful for calculating
   1.959 +   * transformed normals.
   1.960 +   *
   1.961 +   * @param {Array} aMat
   1.962 +   *                mat4 containing values to invert and copy
   1.963 +   * @param {Array} aDest
   1.964 +   *                optional, mat3 receiving operation result
   1.965 +   *                if not specified result is written to the first operand
   1.966 +   *
   1.967 +   * @return {Array} the destination mat3 if specified, first operand otherwise
   1.968 +   */
   1.969 +  toInverseMat3: function M4_toInverseMat3(aMat, aDest)
   1.970 +  {
   1.971 +    if (!aDest) {
   1.972 +      aDest = new Float32Array(9);
   1.973 +    }
   1.974 +
   1.975 +    let a00 = aMat[0], a01 = aMat[1], a02 = aMat[2];
   1.976 +    let a10 = aMat[4], a11 = aMat[5], a12 = aMat[6];
   1.977 +    let a20 = aMat[8], a21 = aMat[9], a22 = aMat[10];
   1.978 +
   1.979 +    let b01 = a22 * a11 - a12 * a21;
   1.980 +    let b11 = -a22 * a10 + a12 * a20;
   1.981 +    let b21 = a21 * a10 - a11 * a20;
   1.982 +    let id = 1 / ((a00 * b01 + a01 * b11 + a02 * b21) || EPSILON);
   1.983 +
   1.984 +    aDest[0] = b01 * id;
   1.985 +    aDest[1] = (-a22 * a01 + a02 * a21) * id;
   1.986 +    aDest[2] = ( a12 * a01 - a02 * a11) * id;
   1.987 +    aDest[3] = b11 * id;
   1.988 +    aDest[4] = ( a22 * a00 - a02 * a20) * id;
   1.989 +    aDest[5] = (-a12 * a00 + a02 * a10) * id;
   1.990 +    aDest[6] = b21 * id;
   1.991 +    aDest[7] = (-a21 * a00 + a01 * a20) * id;
   1.992 +    aDest[8] = ( a11 * a00 - a01 * a10) * id;
   1.993 +    return aDest;
   1.994 +  },
   1.995 +
   1.996 +  /**
   1.997 +   * Performs a matrix multiplication.
   1.998 +   *
   1.999 +   * @param {Array} aMat
  1.1000 +   *                first operand
  1.1001 +   * @param {Array} aMat2
  1.1002 +   *                second operand
  1.1003 +   * @param {Array} aDest
  1.1004 +   *                optional, mat4 receiving operation result
  1.1005 +   *                if not specified result is written to the first operand
  1.1006 +   *
  1.1007 +   * @return {Array} the destination mat4 if specified, first operand otherwise
  1.1008 +   */
  1.1009 +  multiply: function M4_multiply(aMat, aMat2, aDest)
  1.1010 +  {
  1.1011 +    if (!aDest) {
  1.1012 +      aDest = aMat;
  1.1013 +    }
  1.1014 +
  1.1015 +    let a00 = aMat[0], a01 = aMat[1], a02 = aMat[2], a03 = aMat[3];
  1.1016 +    let a10 = aMat[4], a11 = aMat[5], a12 = aMat[6], a13 = aMat[7];
  1.1017 +    let a20 = aMat[8], a21 = aMat[9], a22 = aMat[10], a23 = aMat[11];
  1.1018 +    let a30 = aMat[12], a31 = aMat[13], a32 = aMat[14], a33 = aMat[15];
  1.1019 +
  1.1020 +    let b00 = aMat2[0], b01 = aMat2[1], b02 = aMat2[2], b03 = aMat2[3];
  1.1021 +    let b10 = aMat2[4], b11 = aMat2[5], b12 = aMat2[6], b13 = aMat2[7];
  1.1022 +    let b20 = aMat2[8], b21 = aMat2[9], b22 = aMat2[10], b23 = aMat2[11];
  1.1023 +    let b30 = aMat2[12], b31 = aMat2[13], b32 = aMat2[14], b33 = aMat2[15];
  1.1024 +
  1.1025 +    aDest[0] = b00 * a00 + b01 * a10 + b02 * a20 + b03 * a30;
  1.1026 +    aDest[1] = b00 * a01 + b01 * a11 + b02 * a21 + b03 * a31;
  1.1027 +    aDest[2] = b00 * a02 + b01 * a12 + b02 * a22 + b03 * a32;
  1.1028 +    aDest[3] = b00 * a03 + b01 * a13 + b02 * a23 + b03 * a33;
  1.1029 +    aDest[4] = b10 * a00 + b11 * a10 + b12 * a20 + b13 * a30;
  1.1030 +    aDest[5] = b10 * a01 + b11 * a11 + b12 * a21 + b13 * a31;
  1.1031 +    aDest[6] = b10 * a02 + b11 * a12 + b12 * a22 + b13 * a32;
  1.1032 +    aDest[7] = b10 * a03 + b11 * a13 + b12 * a23 + b13 * a33;
  1.1033 +    aDest[8] = b20 * a00 + b21 * a10 + b22 * a20 + b23 * a30;
  1.1034 +    aDest[9] = b20 * a01 + b21 * a11 + b22 * a21 + b23 * a31;
  1.1035 +    aDest[10] = b20 * a02 + b21 * a12 + b22 * a22 + b23 * a32;
  1.1036 +    aDest[11] = b20 * a03 + b21 * a13 + b22 * a23 + b23 * a33;
  1.1037 +    aDest[12] = b30 * a00 + b31 * a10 + b32 * a20 + b33 * a30;
  1.1038 +    aDest[13] = b30 * a01 + b31 * a11 + b32 * a21 + b33 * a31;
  1.1039 +    aDest[14] = b30 * a02 + b31 * a12 + b32 * a22 + b33 * a32;
  1.1040 +    aDest[15] = b30 * a03 + b31 * a13 + b32 * a23 + b33 * a33;
  1.1041 +    return aDest;
  1.1042 +  },
  1.1043 +
  1.1044 +  /**
  1.1045 +   * Transforms a vec3 with the given matrix.
  1.1046 +   * 4th vector component is implicitly 1.
  1.1047 +   *
  1.1048 +   * @param {Array} aMat
  1.1049 +   *                mat4 to transform the vector with
  1.1050 +   * @param {Array} aVec
  1.1051 +   *                vec3 to transform
  1.1052 +   * @param {Array} aDest
  1.1053 +   *                optional, vec3 receiving operation result
  1.1054 +   *                if not specified result is written to the first operand
  1.1055 +   *
  1.1056 +   * @return {Array} the destination vec3 if specified, aVec operand otherwise
  1.1057 +   */
  1.1058 +  multiplyVec3: function M4_multiplyVec3(aMat, aVec, aDest)
  1.1059 +  {
  1.1060 +    if (!aDest) {
  1.1061 +      aDest = aVec;
  1.1062 +    }
  1.1063 +
  1.1064 +    let x = aVec[0];
  1.1065 +    let y = aVec[1];
  1.1066 +    let z = aVec[2];
  1.1067 +
  1.1068 +    aDest[0] = aMat[0] * x + aMat[4] * y + aMat[8]  * z + aMat[12];
  1.1069 +    aDest[1] = aMat[1] * x + aMat[5] * y + aMat[9]  * z + aMat[13];
  1.1070 +    aDest[2] = aMat[2] * x + aMat[6] * y + aMat[10] * z + aMat[14];
  1.1071 +    return aDest;
  1.1072 +  },
  1.1073 +
  1.1074 +  /**
  1.1075 +   * Transforms a vec4 with the given matrix.
  1.1076 +   *
  1.1077 +   * @param {Array} aMat
  1.1078 +   *                mat4 to transform the vector with
  1.1079 +   * @param {Array} aVec
  1.1080 +   *                vec4 to transform
  1.1081 +   * @param {Array} aDest
  1.1082 +   *                optional, vec4 receiving operation result
  1.1083 +   *                if not specified result is written to the first operand
  1.1084 +   *
  1.1085 +   * @return {Array} the destination vec4 if specified, vec4 operand otherwise
  1.1086 +   */
  1.1087 +  multiplyVec4: function M4_multiplyVec4(aMat, aVec, aDest)
  1.1088 +  {
  1.1089 +    if (!aDest) {
  1.1090 +      aDest = aVec;
  1.1091 +    }
  1.1092 +
  1.1093 +    let x = aVec[0];
  1.1094 +    let y = aVec[1];
  1.1095 +    let z = aVec[2];
  1.1096 +    let w = aVec[3];
  1.1097 +
  1.1098 +    aDest[0] = aMat[0] * x + aMat[4] * y + aMat[8]  * z + aMat[12] * w;
  1.1099 +    aDest[1] = aMat[1] * x + aMat[5] * y + aMat[9]  * z + aMat[13] * w;
  1.1100 +    aDest[2] = aMat[2] * x + aMat[6] * y + aMat[10] * z + aMat[14] * w;
  1.1101 +    aDest[3] = aMat[3] * x + aMat[7] * y + aMat[11] * z + aMat[15] * w;
  1.1102 +    return aDest;
  1.1103 +  },
  1.1104 +
  1.1105 +  /**
  1.1106 +   * Translates a matrix by the given vector.
  1.1107 +   *
  1.1108 +   * @param {Array} aMat
  1.1109 +   *                mat4 to translate
  1.1110 +   * @param {Array} aVec
  1.1111 +   *                vec3 specifying the translation
  1.1112 +   * @param {Array} aDest
  1.1113 +   *                optional, mat4 receiving operation result
  1.1114 +   *                if not specified result is written to the first operand
  1.1115 +   *
  1.1116 +   * @return {Array} the destination mat4 if specified, first operand otherwise
  1.1117 +   */
  1.1118 +  translate: function M4_translate(aMat, aVec, aDest)
  1.1119 +  {
  1.1120 +    let x = aVec[0];
  1.1121 +    let y = aVec[1];
  1.1122 +    let z = aVec[2];
  1.1123 +
  1.1124 +    if (!aDest || aMat === aDest) {
  1.1125 +      aMat[12] = aMat[0] * x + aMat[4] * y + aMat[8] * z + aMat[12];
  1.1126 +      aMat[13] = aMat[1] * x + aMat[5] * y + aMat[9] * z + aMat[13];
  1.1127 +      aMat[14] = aMat[2] * x + aMat[6] * y + aMat[10] * z + aMat[14];
  1.1128 +      aMat[15] = aMat[3] * x + aMat[7] * y + aMat[11] * z + aMat[15];
  1.1129 +      return aMat;
  1.1130 +    }
  1.1131 +
  1.1132 +    let a00 = aMat[0], a01 = aMat[1], a02 = aMat[2], a03 = aMat[3];
  1.1133 +    let a10 = aMat[4], a11 = aMat[5], a12 = aMat[6], a13 = aMat[7];
  1.1134 +    let a20 = aMat[8], a21 = aMat[9], a22 = aMat[10], a23 = aMat[11];
  1.1135 +
  1.1136 +    aDest[0] = a00;
  1.1137 +    aDest[1] = a01;
  1.1138 +    aDest[2] = a02;
  1.1139 +    aDest[3] = a03;
  1.1140 +    aDest[4] = a10;
  1.1141 +    aDest[5] = a11;
  1.1142 +    aDest[6] = a12;
  1.1143 +    aDest[7] = a13;
  1.1144 +    aDest[8] = a20;
  1.1145 +    aDest[9] = a21;
  1.1146 +    aDest[10] = a22;
  1.1147 +    aDest[11] = a23;
  1.1148 +    aDest[12] = a00 * x + a10 * y + a20 * z + aMat[12];
  1.1149 +    aDest[13] = a01 * x + a11 * y + a21 * z + aMat[13];
  1.1150 +    aDest[14] = a02 * x + a12 * y + a22 * z + aMat[14];
  1.1151 +    aDest[15] = a03 * x + a13 * y + a23 * z + aMat[15];
  1.1152 +    return aDest;
  1.1153 +  },
  1.1154 +
  1.1155 +  /**
  1.1156 +   * Scales a matrix by the given vector.
  1.1157 +   *
  1.1158 +   * @param {Array} aMat
  1.1159 +   *                mat4 to translate
  1.1160 +   * @param {Array} aVec
  1.1161 +   *                vec3 specifying the scale on each axis
  1.1162 +   * @param {Array} aDest
  1.1163 +   *                optional, mat4 receiving operation result
  1.1164 +   *                if not specified result is written to the first operand
  1.1165 +   *
  1.1166 +   * @return {Array} the destination mat4 if specified, first operand otherwise
  1.1167 +   */
  1.1168 +  scale: function M4_scale(aMat, aVec, aDest)
  1.1169 +  {
  1.1170 +    let x = aVec[0];
  1.1171 +    let y = aVec[1];
  1.1172 +    let z = aVec[2];
  1.1173 +
  1.1174 +    if (!aDest || aMat === aDest) {
  1.1175 +      aMat[0] *= x;
  1.1176 +      aMat[1] *= x;
  1.1177 +      aMat[2] *= x;
  1.1178 +      aMat[3] *= x;
  1.1179 +      aMat[4] *= y;
  1.1180 +      aMat[5] *= y;
  1.1181 +      aMat[6] *= y;
  1.1182 +      aMat[7] *= y;
  1.1183 +      aMat[8] *= z;
  1.1184 +      aMat[9] *= z;
  1.1185 +      aMat[10] *= z;
  1.1186 +      aMat[11] *= z;
  1.1187 +      return aMat;
  1.1188 +    }
  1.1189 +
  1.1190 +    aDest[0] = aMat[0] * x;
  1.1191 +    aDest[1] = aMat[1] * x;
  1.1192 +    aDest[2] = aMat[2] * x;
  1.1193 +    aDest[3] = aMat[3] * x;
  1.1194 +    aDest[4] = aMat[4] * y;
  1.1195 +    aDest[5] = aMat[5] * y;
  1.1196 +    aDest[6] = aMat[6] * y;
  1.1197 +    aDest[7] = aMat[7] * y;
  1.1198 +    aDest[8] = aMat[8] * z;
  1.1199 +    aDest[9] = aMat[9] * z;
  1.1200 +    aDest[10] = aMat[10] * z;
  1.1201 +    aDest[11] = aMat[11] * z;
  1.1202 +    aDest[12] = aMat[12];
  1.1203 +    aDest[13] = aMat[13];
  1.1204 +    aDest[14] = aMat[14];
  1.1205 +    aDest[15] = aMat[15];
  1.1206 +    return aDest;
  1.1207 +  },
  1.1208 +
  1.1209 +  /**
  1.1210 +   * Rotates a matrix by the given angle around the specified axis.
  1.1211 +   * If rotating around a primary axis (x, y, z) one of the specialized
  1.1212 +   * rotation functions should be used instead for performance,
  1.1213 +   *
  1.1214 +   * @param {Array} aMat
  1.1215 +   *                mat4 to rotate
  1.1216 +   * @param {Number} aAngle
  1.1217 +   *                 the angle (in radians) to rotate
  1.1218 +   * @param {Array} aAxis
  1.1219 +   *                vec3 representing the axis to rotate around
  1.1220 +   * @param {Array} aDest
  1.1221 +   *                optional, mat4 receiving operation result
  1.1222 +   *                if not specified result is written to the first operand
  1.1223 +   *
  1.1224 +   * @return {Array} the destination mat4 if specified, first operand otherwise
  1.1225 +   */
  1.1226 +  rotate: function M4_rotate(aMat, aAngle, aAxis, aDest)
  1.1227 +  {
  1.1228 +    let x = aAxis[0];
  1.1229 +    let y = aAxis[1];
  1.1230 +    let z = aAxis[2];
  1.1231 +    let len = 1 / (Math.sqrt(x * x + y * y + z * z) || EPSILON);
  1.1232 +
  1.1233 +    x *= len;
  1.1234 +    y *= len;
  1.1235 +    z *= len;
  1.1236 +
  1.1237 +    let s = Math.sin(aAngle);
  1.1238 +    let c = Math.cos(aAngle);
  1.1239 +    let t = 1 - c;
  1.1240 +
  1.1241 +    let a00 = aMat[0], a01 = aMat[1], a02 = aMat[2], a03 = aMat[3];
  1.1242 +    let a10 = aMat[4], a11 = aMat[5], a12 = aMat[6], a13 = aMat[7];
  1.1243 +    let a20 = aMat[8], a21 = aMat[9], a22 = aMat[10], a23 = aMat[11];
  1.1244 +
  1.1245 +    let b00 = x * x * t + c, b01 = y * x * t + z * s, b02 = z * x * t - y * s;
  1.1246 +    let b10 = x * y * t - z * s, b11 = y * y * t + c, b12 = z * y * t + x * s;
  1.1247 +    let b20 = x * z * t + y * s, b21 = y * z * t - x * s, b22 = z * z * t + c;
  1.1248 +
  1.1249 +    if (!aDest) {
  1.1250 +      aDest = aMat;
  1.1251 +    } else if (aMat !== aDest) {
  1.1252 +      aDest[12] = aMat[12];
  1.1253 +      aDest[13] = aMat[13];
  1.1254 +      aDest[14] = aMat[14];
  1.1255 +      aDest[15] = aMat[15];
  1.1256 +    }
  1.1257 +
  1.1258 +    aDest[0] = a00 * b00 + a10 * b01 + a20 * b02;
  1.1259 +    aDest[1] = a01 * b00 + a11 * b01 + a21 * b02;
  1.1260 +    aDest[2] = a02 * b00 + a12 * b01 + a22 * b02;
  1.1261 +    aDest[3] = a03 * b00 + a13 * b01 + a23 * b02;
  1.1262 +    aDest[4] = a00 * b10 + a10 * b11 + a20 * b12;
  1.1263 +    aDest[5] = a01 * b10 + a11 * b11 + a21 * b12;
  1.1264 +    aDest[6] = a02 * b10 + a12 * b11 + a22 * b12;
  1.1265 +    aDest[7] = a03 * b10 + a13 * b11 + a23 * b12;
  1.1266 +    aDest[8] = a00 * b20 + a10 * b21 + a20 * b22;
  1.1267 +    aDest[9] = a01 * b20 + a11 * b21 + a21 * b22;
  1.1268 +    aDest[10] = a02 * b20 + a12 * b21 + a22 * b22;
  1.1269 +    aDest[11] = a03 * b20 + a13 * b21 + a23 * b22;
  1.1270 +    return aDest;
  1.1271 +  },
  1.1272 +
  1.1273 +  /**
  1.1274 +   * Rotates a matrix by the given angle around the X axis.
  1.1275 +   *
  1.1276 +   * @param {Array} aMat
  1.1277 +   *                mat4 to rotate
  1.1278 +   * @param {Number} aAngle
  1.1279 +   *                 the angle (in radians) to rotate
  1.1280 +   * @param {Array} aDest
  1.1281 +   *                optional, mat4 receiving operation result
  1.1282 +   *                if not specified result is written to the first operand
  1.1283 +   *
  1.1284 +   * @return {Array} the destination mat4 if specified, first operand otherwise
  1.1285 +   */
  1.1286 +  rotateX: function M4_rotateX(aMat, aAngle, aDest)
  1.1287 +  {
  1.1288 +    let s = Math.sin(aAngle);
  1.1289 +    let c = Math.cos(aAngle);
  1.1290 +
  1.1291 +    let a10 = aMat[4], a11 = aMat[5], a12 = aMat[6], a13 = aMat[7];
  1.1292 +    let a20 = aMat[8], a21 = aMat[9], a22 = aMat[10], a23 = aMat[11];
  1.1293 +
  1.1294 +    if (!aDest) {
  1.1295 +      aDest = aMat;
  1.1296 +    } else if (aMat !== aDest) {
  1.1297 +      aDest[0] = aMat[0];
  1.1298 +      aDest[1] = aMat[1];
  1.1299 +      aDest[2] = aMat[2];
  1.1300 +      aDest[3] = aMat[3];
  1.1301 +      aDest[12] = aMat[12];
  1.1302 +      aDest[13] = aMat[13];
  1.1303 +      aDest[14] = aMat[14];
  1.1304 +      aDest[15] = aMat[15];
  1.1305 +    }
  1.1306 +
  1.1307 +    aDest[4] = a10 * c + a20 * s;
  1.1308 +    aDest[5] = a11 * c + a21 * s;
  1.1309 +    aDest[6] = a12 * c + a22 * s;
  1.1310 +    aDest[7] = a13 * c + a23 * s;
  1.1311 +    aDest[8] = a10 * -s + a20 * c;
  1.1312 +    aDest[9] = a11 * -s + a21 * c;
  1.1313 +    aDest[10] = a12 * -s + a22 * c;
  1.1314 +    aDest[11] = a13 * -s + a23 * c;
  1.1315 +    return aDest;
  1.1316 +  },
  1.1317 +
  1.1318 +  /**
  1.1319 +   * Rotates a matrix by the given angle around the Y axix.
  1.1320 +   *
  1.1321 +   * @param {Array} aMat
  1.1322 +   *                mat4 to rotate
  1.1323 +   * @param {Number} aAngle
  1.1324 +   *                 the angle (in radians) to rotate
  1.1325 +   * @param {Array} aDest
  1.1326 +   *                optional, mat4 receiving operation result
  1.1327 +   *                if not specified result is written to the first operand
  1.1328 +   *
  1.1329 +   * @return {Array} the destination mat4 if specified, first operand otherwise
  1.1330 +   */
  1.1331 +  rotateY: function M4_rotateY(aMat, aAngle, aDest)
  1.1332 +  {
  1.1333 +    let s = Math.sin(aAngle);
  1.1334 +    let c = Math.cos(aAngle);
  1.1335 +
  1.1336 +    let a00 = aMat[0], a01 = aMat[1], a02 = aMat[2], a03 = aMat[3];
  1.1337 +    let a20 = aMat[8], a21 = aMat[9], a22 = aMat[10], a23 = aMat[11];
  1.1338 +
  1.1339 +    if (!aDest) {
  1.1340 +      aDest = aMat;
  1.1341 +    } else if (aMat !== aDest) {
  1.1342 +      aDest[4] = aMat[4];
  1.1343 +      aDest[5] = aMat[5];
  1.1344 +      aDest[6] = aMat[6];
  1.1345 +      aDest[7] = aMat[7];
  1.1346 +      aDest[12] = aMat[12];
  1.1347 +      aDest[13] = aMat[13];
  1.1348 +      aDest[14] = aMat[14];
  1.1349 +      aDest[15] = aMat[15];
  1.1350 +    }
  1.1351 +
  1.1352 +    aDest[0] = a00 * c + a20 * -s;
  1.1353 +    aDest[1] = a01 * c + a21 * -s;
  1.1354 +    aDest[2] = a02 * c + a22 * -s;
  1.1355 +    aDest[3] = a03 * c + a23 * -s;
  1.1356 +    aDest[8] = a00 * s + a20 *  c;
  1.1357 +    aDest[9] = a01 * s + a21 *  c;
  1.1358 +    aDest[10] = a02 * s + a22 *  c;
  1.1359 +    aDest[11] = a03 * s + a23 *  c;
  1.1360 +    return aDest;
  1.1361 +  },
  1.1362 +
  1.1363 +  /**
  1.1364 +   * Rotates a matrix by the given angle around the Z axix.
  1.1365 +   *
  1.1366 +   * @param {Array} aMat
  1.1367 +   *                mat4 to rotate
  1.1368 +   * @param {Number} aAngle
  1.1369 +   *                 the angle (in radians) to rotate
  1.1370 +   * @param {Array} aDest
  1.1371 +   *                optional, mat4 receiving operation result
  1.1372 +   *                if not specified result is written to the first operand
  1.1373 +   *
  1.1374 +   * @return {Array} the destination mat4 if specified, first operand otherwise
  1.1375 +   */
  1.1376 +  rotateZ: function M4_rotateZ(aMat, aAngle, aDest)
  1.1377 +  {
  1.1378 +    let s = Math.sin(aAngle);
  1.1379 +    let c = Math.cos(aAngle);
  1.1380 +
  1.1381 +    let a00 = aMat[0], a01 = aMat[1], a02 = aMat[2], a03 = aMat[3];
  1.1382 +    let a10 = aMat[4], a11 = aMat[5], a12 = aMat[6], a13 = aMat[7];
  1.1383 +
  1.1384 +    if (!aDest) {
  1.1385 +      aDest = aMat;
  1.1386 +    } else if (aMat !== aDest) {
  1.1387 +      aDest[8] = aMat[8];
  1.1388 +      aDest[9] = aMat[9];
  1.1389 +      aDest[10] = aMat[10];
  1.1390 +      aDest[11] = aMat[11];
  1.1391 +      aDest[12] = aMat[12];
  1.1392 +      aDest[13] = aMat[13];
  1.1393 +      aDest[14] = aMat[14];
  1.1394 +      aDest[15] = aMat[15];
  1.1395 +    }
  1.1396 +
  1.1397 +    aDest[0] = a00 * c + a10 * s;
  1.1398 +    aDest[1] = a01 * c + a11 * s;
  1.1399 +    aDest[2] = a02 * c + a12 * s;
  1.1400 +    aDest[3] = a03 * c + a13 * s;
  1.1401 +    aDest[4] = a00 * -s + a10 * c;
  1.1402 +    aDest[5] = a01 * -s + a11 * c;
  1.1403 +    aDest[6] = a02 * -s + a12 * c;
  1.1404 +    aDest[7] = a03 * -s + a13 * c;
  1.1405 +    return aDest;
  1.1406 +  },
  1.1407 +
  1.1408 +  /**
  1.1409 +   * Generates a frustum matrix with the given bounds.
  1.1410 +   *
  1.1411 +   * @param {Number} aLeft
  1.1412 +   *                 scalar, left bound of the frustum
  1.1413 +   * @param {Number} aRight
  1.1414 +   *                 scalar, right bound of the frustum
  1.1415 +   * @param {Number} aBottom
  1.1416 +   *                 scalar, bottom bound of the frustum
  1.1417 +   * @param {Number} aTop
  1.1418 +   *                 scalar, top bound of the frustum
  1.1419 +   * @param {Number} aNear
  1.1420 +   *                 scalar, near bound of the frustum
  1.1421 +   * @param {Number} aFar
  1.1422 +   *                 scalar, far bound of the frustum
  1.1423 +   * @param {Array} aDest
  1.1424 +   *                optional, mat4 frustum matrix will be written into
  1.1425 +   *                if not specified result is written to a new mat4
  1.1426 +   *
  1.1427 +   * @return {Array} the destination mat4 if specified, a new mat4 otherwise
  1.1428 +   */
  1.1429 +  frustum: function M4_frustum(
  1.1430 +    aLeft, aRight, aBottom, aTop, aNear, aFar, aDest)
  1.1431 +  {
  1.1432 +    if (!aDest) {
  1.1433 +      aDest = new Float32Array(16);
  1.1434 +    }
  1.1435 +
  1.1436 +    let rl = (aRight - aLeft);
  1.1437 +    let tb = (aTop - aBottom);
  1.1438 +    let fn = (aFar - aNear);
  1.1439 +
  1.1440 +    aDest[0] = (aNear * 2) / rl;
  1.1441 +    aDest[1] = 0;
  1.1442 +    aDest[2] = 0;
  1.1443 +    aDest[3] = 0;
  1.1444 +    aDest[4] = 0;
  1.1445 +    aDest[5] = (aNear * 2) / tb;
  1.1446 +    aDest[6] = 0;
  1.1447 +    aDest[7] = 0;
  1.1448 +    aDest[8] = (aRight + aLeft) / rl;
  1.1449 +    aDest[9] = (aTop + aBottom) / tb;
  1.1450 +    aDest[10] = -(aFar + aNear) / fn;
  1.1451 +    aDest[11] = -1;
  1.1452 +    aDest[12] = 0;
  1.1453 +    aDest[13] = 0;
  1.1454 +    aDest[14] = -(aFar * aNear * 2) / fn;
  1.1455 +    aDest[15] = 0;
  1.1456 +    return aDest;
  1.1457 +  },
  1.1458 +
  1.1459 +  /**
  1.1460 +   * Generates a perspective projection matrix with the given bounds.
  1.1461 +   *
  1.1462 +   * @param {Number} aFovy
  1.1463 +   *                 scalar, vertical field of view (degrees)
  1.1464 +   * @param {Number} aAspect
  1.1465 +   *                 scalar, aspect ratio (typically viewport width/height)
  1.1466 +   * @param {Number} aNear
  1.1467 +   *                 scalar, near bound of the frustum
  1.1468 +   * @param {Number} aFar
  1.1469 +   *                 scalar, far bound of the frustum
  1.1470 +   * @param {Array} aDest
  1.1471 +   *                optional, mat4 frustum matrix will be written into
  1.1472 +   *                if not specified result is written to a new mat4
  1.1473 +   *
  1.1474 +   * @return {Array} the destination mat4 if specified, a new mat4 otherwise
  1.1475 +   */
  1.1476 +  perspective: function M4_perspective(
  1.1477 +    aFovy, aAspect, aNear, aFar, aDest, aFlip)
  1.1478 +  {
  1.1479 +    let upper = aNear * Math.tan(aFovy * 0.00872664626); // PI * 180 / 2
  1.1480 +    let right = upper * aAspect;
  1.1481 +    let top = upper * (aFlip || 1);
  1.1482 +
  1.1483 +    return mat4.frustum(-right, right, -top, top, aNear, aFar, aDest);
  1.1484 +  },
  1.1485 +
  1.1486 +  /**
  1.1487 +   * Generates a orthogonal projection matrix with the given bounds.
  1.1488 +   *
  1.1489 +   * @param {Number} aLeft
  1.1490 +   *                 scalar, left bound of the frustum
  1.1491 +   * @param {Number} aRight
  1.1492 +   *                 scalar, right bound of the frustum
  1.1493 +   * @param {Number} aBottom
  1.1494 +   *                 scalar, bottom bound of the frustum
  1.1495 +   * @param {Number} aTop
  1.1496 +   *                 scalar, top bound of the frustum
  1.1497 +   * @param {Number} aNear
  1.1498 +   *                 scalar, near bound of the frustum
  1.1499 +   * @param {Number} aFar
  1.1500 +   *                 scalar, far bound of the frustum
  1.1501 +   * @param {Array} aDest
  1.1502 +   *                optional, mat4 frustum matrix will be written into
  1.1503 +   *                if not specified result is written to a new mat4
  1.1504 +   *
  1.1505 +   * @return {Array} the destination mat4 if specified, a new mat4 otherwise
  1.1506 +   */
  1.1507 +  ortho: function M4_ortho(aLeft, aRight, aBottom, aTop, aNear, aFar, aDest)
  1.1508 +  {
  1.1509 +    if (!aDest) {
  1.1510 +      aDest = new Float32Array(16);
  1.1511 +    }
  1.1512 +
  1.1513 +    let rl = (aRight - aLeft);
  1.1514 +    let tb = (aTop - aBottom);
  1.1515 +    let fn = (aFar - aNear);
  1.1516 +
  1.1517 +    aDest[0] = 2 / rl;
  1.1518 +    aDest[1] = 0;
  1.1519 +    aDest[2] = 0;
  1.1520 +    aDest[3] = 0;
  1.1521 +    aDest[4] = 0;
  1.1522 +    aDest[5] = 2 / tb;
  1.1523 +    aDest[6] = 0;
  1.1524 +    aDest[7] = 0;
  1.1525 +    aDest[8] = 0;
  1.1526 +    aDest[9] = 0;
  1.1527 +    aDest[10] = -2 / fn;
  1.1528 +    aDest[11] = 0;
  1.1529 +    aDest[12] = -(aLeft + aRight) / rl;
  1.1530 +    aDest[13] = -(aTop + aBottom) / tb;
  1.1531 +    aDest[14] = -(aFar + aNear) / fn;
  1.1532 +    aDest[15] = 1;
  1.1533 +    return aDest;
  1.1534 +  },
  1.1535 +
  1.1536 +  /**
  1.1537 +   * Generates a look-at matrix with the given eye position, focal point, and
  1.1538 +   * up axis.
  1.1539 +   *
  1.1540 +   * @param {Array} aEye
  1.1541 +   *                vec3, position of the viewer
  1.1542 +   * @param {Array} aCenter
  1.1543 +   *                vec3, point the viewer is looking at
  1.1544 +   * @param {Array} aUp
  1.1545 +   *                vec3 pointing up
  1.1546 +   * @param {Array} aDest
  1.1547 +   *                optional, mat4 frustum matrix will be written into
  1.1548 +   *                if not specified result is written to a new mat4
  1.1549 +   *
  1.1550 +   * @return {Array} the destination mat4 if specified, a new mat4 otherwise
  1.1551 +   */
  1.1552 +  lookAt: function M4_lookAt(aEye, aCenter, aUp, aDest)
  1.1553 +  {
  1.1554 +    if (!aDest) {
  1.1555 +      aDest = new Float32Array(16);
  1.1556 +    }
  1.1557 +
  1.1558 +    let eyex = aEye[0];
  1.1559 +    let eyey = aEye[1];
  1.1560 +    let eyez = aEye[2];
  1.1561 +    let upx = aUp[0];
  1.1562 +    let upy = aUp[1];
  1.1563 +    let upz = aUp[2];
  1.1564 +    let centerx = aCenter[0];
  1.1565 +    let centery = aCenter[1];
  1.1566 +    let centerz = aCenter[2];
  1.1567 +
  1.1568 +    let z0 = eyex - aCenter[0];
  1.1569 +    let z1 = eyey - aCenter[1];
  1.1570 +    let z2 = eyez - aCenter[2];
  1.1571 +    let len = 1 / (Math.sqrt(z0 * z0 + z1 * z1 + z2 * z2) || EPSILON);
  1.1572 +
  1.1573 +    z0 *= len;
  1.1574 +    z1 *= len;
  1.1575 +    z2 *= len;
  1.1576 +
  1.1577 +    let x0 = upy * z2 - upz * z1;
  1.1578 +    let x1 = upz * z0 - upx * z2;
  1.1579 +    let x2 = upx * z1 - upy * z0;
  1.1580 +    len = 1 / (Math.sqrt(x0 * x0 + x1 * x1 + x2 * x2) || EPSILON);
  1.1581 +
  1.1582 +    x0 *= len;
  1.1583 +    x1 *= len;
  1.1584 +    x2 *= len;
  1.1585 +
  1.1586 +    let y0 = z1 * x2 - z2 * x1;
  1.1587 +    let y1 = z2 * x0 - z0 * x2;
  1.1588 +    let y2 = z0 * x1 - z1 * x0;
  1.1589 +    len = 1 / (Math.sqrt(y0 * y0 + y1 * y1 + y2 * y2) || EPSILON);
  1.1590 +
  1.1591 +    y0 *= len;
  1.1592 +    y1 *= len;
  1.1593 +    y2 *= len;
  1.1594 +
  1.1595 +    aDest[0] = x0;
  1.1596 +    aDest[1] = y0;
  1.1597 +    aDest[2] = z0;
  1.1598 +    aDest[3] = 0;
  1.1599 +    aDest[4] = x1;
  1.1600 +    aDest[5] = y1;
  1.1601 +    aDest[6] = z1;
  1.1602 +    aDest[7] = 0;
  1.1603 +    aDest[8] = x2;
  1.1604 +    aDest[9] = y2;
  1.1605 +    aDest[10] = z2;
  1.1606 +    aDest[11] = 0;
  1.1607 +    aDest[12] = -(x0 * eyex + x1 * eyey + x2 * eyez);
  1.1608 +    aDest[13] = -(y0 * eyex + y1 * eyey + y2 * eyez);
  1.1609 +    aDest[14] = -(z0 * eyex + z1 * eyey + z2 * eyez);
  1.1610 +    aDest[15] = 1;
  1.1611 +
  1.1612 +    return aDest;
  1.1613 +  },
  1.1614 +
  1.1615 +  /**
  1.1616 +   * Returns a string representation of a 4x4 matrix.
  1.1617 +   *
  1.1618 +   * @param {Array} aMat
  1.1619 +   *                mat4 to represent as a string
  1.1620 +   *
  1.1621 +   * @return {String} representation of the matrix
  1.1622 +   */
  1.1623 +  str: function M4_str(mat)
  1.1624 +  {
  1.1625 +    return "[" + mat[0] + ", " + mat[1] + ", " + mat[2] + ", " + mat[3] +
  1.1626 +           ", "+ mat[4] + ", " + mat[5] + ", " + mat[6] + ", " + mat[7] +
  1.1627 +           ", "+ mat[8] + ", " + mat[9] + ", " + mat[10] + ", " + mat[11] +
  1.1628 +           ", "+ mat[12] + ", " + mat[13] + ", " + mat[14] + ", " + mat[15] +
  1.1629 +           "]";
  1.1630 +  }
  1.1631 +};
  1.1632 +
  1.1633 +exports.mat4 = mat4;
  1.1634 +
  1.1635 +/**
  1.1636 + * quat4 - Quaternion.
  1.1637 + */
  1.1638 +let quat4 = {
  1.1639 +
  1.1640 +  /**
  1.1641 +   * Creates a new instance of a quat4 using the default Float32Array type.
  1.1642 +   * Any array containing at least 4 numeric elements can serve as a quat4.
  1.1643 +   *
  1.1644 +   * @param {Array} aQuat
  1.1645 +   *                optional, quat4 containing values to initialize with
  1.1646 +   *
  1.1647 +   * @return {Array} a new instance of a quat4
  1.1648 +   */
  1.1649 +  create: function Q4_create(aQuat)
  1.1650 +  {
  1.1651 +    let dest = new Float32Array(4);
  1.1652 +
  1.1653 +    if (aQuat) {
  1.1654 +      quat4.set(aQuat, dest);
  1.1655 +    } else {
  1.1656 +      quat4.identity(dest);
  1.1657 +    }
  1.1658 +    return dest;
  1.1659 +  },
  1.1660 +
  1.1661 +  /**
  1.1662 +   * Copies the values of one quat4 to another.
  1.1663 +   *
  1.1664 +   * @param {Array} aQuat
  1.1665 +   *                quat4 containing values to copy
  1.1666 +   * @param {Array} aDest
  1.1667 +   *                quat4 receiving copied values
  1.1668 +   *
  1.1669 +   * @return {Array} the destination quat4 receiving copied values
  1.1670 +   */
  1.1671 +  set: function Q4_set(aQuat, aDest)
  1.1672 +  {
  1.1673 +    aDest[0] = aQuat[0];
  1.1674 +    aDest[1] = aQuat[1];
  1.1675 +    aDest[2] = aQuat[2];
  1.1676 +    aDest[3] = aQuat[3];
  1.1677 +    return aDest;
  1.1678 +  },
  1.1679 +
  1.1680 +  /**
  1.1681 +   * Sets a quat4 to an identity quaternion.
  1.1682 +   *
  1.1683 +   * @param {Array} aDest
  1.1684 +   *                quat4 to set
  1.1685 +   *
  1.1686 +   * @return {Array} the same quaternion
  1.1687 +   */
  1.1688 +  identity: function Q4_identity(aDest)
  1.1689 +  {
  1.1690 +    aDest[0] = 0;
  1.1691 +    aDest[1] = 0;
  1.1692 +    aDest[2] = 0;
  1.1693 +    aDest[3] = 1;
  1.1694 +    return aDest;
  1.1695 +  },
  1.1696 +
  1.1697 +  /**
  1.1698 +   * Calculate the W component of a quat4 from the X, Y, and Z components.
  1.1699 +   * Assumes that quaternion is 1 unit in length.
  1.1700 +   * Any existing W component will be ignored.
  1.1701 +   *
  1.1702 +   * @param {Array} aQuat
  1.1703 +   *                quat4 to calculate W component of
  1.1704 +   * @param {Array} aDest
  1.1705 +   *                optional, quat4 receiving calculated values
  1.1706 +   *                if not specified result is written to the first operand
  1.1707 +   *
  1.1708 +   * @return {Array} the destination quat if specified, first operand otherwise
  1.1709 +   */
  1.1710 +  calculateW: function Q4_calculateW(aQuat, aDest)
  1.1711 +  {
  1.1712 +    if (!aDest) {
  1.1713 +      aDest = aQuat;
  1.1714 +    }
  1.1715 +
  1.1716 +    let x = aQuat[0];
  1.1717 +    let y = aQuat[1];
  1.1718 +    let z = aQuat[2];
  1.1719 +
  1.1720 +    aDest[0] = x;
  1.1721 +    aDest[1] = y;
  1.1722 +    aDest[2] = z;
  1.1723 +    aDest[3] = -Math.sqrt(Math.abs(1 - x * x - y * y - z * z));
  1.1724 +    return aDest;
  1.1725 +  },
  1.1726 +
  1.1727 +  /**
  1.1728 +   * Calculate the inverse of a quat4.
  1.1729 +   *
  1.1730 +   * @param {Array} aQuat
  1.1731 +   *                quat4 to calculate the inverse of
  1.1732 +   * @param {Array} aDest
  1.1733 +   *                optional, quat4 receiving the inverse values
  1.1734 +   *                if not specified result is written to the first operand
  1.1735 +   *
  1.1736 +   * @return {Array} the destination quat if specified, first operand otherwise
  1.1737 +   */
  1.1738 +  inverse: function Q4_inverse(aQuat, aDest)
  1.1739 +  {
  1.1740 +    if (!aDest) {
  1.1741 +      aDest = aQuat;
  1.1742 +    }
  1.1743 +
  1.1744 +    aQuat[0] = -aQuat[0];
  1.1745 +    aQuat[1] = -aQuat[1];
  1.1746 +    aQuat[2] = -aQuat[2];
  1.1747 +    return aQuat;
  1.1748 +  },
  1.1749 +
  1.1750 +  /**
  1.1751 +   * Generates a unit quaternion of the same direction as the provided quat4.
  1.1752 +   * If quaternion length is 0, returns [0, 0, 0, 0].
  1.1753 +   *
  1.1754 +   * @param {Array} aQuat
  1.1755 +   *                quat4 to normalize
  1.1756 +   * @param {Array} aDest
  1.1757 +   *                optional, quat4 receiving the operation result
  1.1758 +   *                if not specified result is written to the first operand
  1.1759 +   *
  1.1760 +   * @return {Array} the destination quat if specified, first operand otherwise
  1.1761 +   */
  1.1762 +  normalize: function Q4_normalize(aQuat, aDest)
  1.1763 +  {
  1.1764 +    if (!aDest) {
  1.1765 +      aDest = aQuat;
  1.1766 +    }
  1.1767 +
  1.1768 +    let x = aQuat[0];
  1.1769 +    let y = aQuat[1];
  1.1770 +    let z = aQuat[2];
  1.1771 +    let w = aQuat[3];
  1.1772 +    let len = Math.sqrt(x * x + y * y + z * z + w * w);
  1.1773 +
  1.1774 +    if (Math.abs(len) < EPSILON) {
  1.1775 +      aDest[0] = 0;
  1.1776 +      aDest[1] = 0;
  1.1777 +      aDest[2] = 0;
  1.1778 +      aDest[3] = 0;
  1.1779 +      return aDest;
  1.1780 +    }
  1.1781 +
  1.1782 +    len = 1 / len;
  1.1783 +    aDest[0] = x * len;
  1.1784 +    aDest[1] = y * len;
  1.1785 +    aDest[2] = z * len;
  1.1786 +    aDest[3] = w * len;
  1.1787 +    return aDest;
  1.1788 +  },
  1.1789 +
  1.1790 +  /**
  1.1791 +   * Calculate the length of a quat4.
  1.1792 +   *
  1.1793 +   * @param {Array} aQuat
  1.1794 +   *                quat4 to calculate the length of
  1.1795 +   *
  1.1796 +   * @return {Number} length of the quaternion
  1.1797 +   */
  1.1798 +  length: function Q4_length(aQuat)
  1.1799 +  {
  1.1800 +    let x = aQuat[0];
  1.1801 +    let y = aQuat[1];
  1.1802 +    let z = aQuat[2];
  1.1803 +    let w = aQuat[3];
  1.1804 +
  1.1805 +    return Math.sqrt(x * x + y * y + z * z + w * w);
  1.1806 +  },
  1.1807 +
  1.1808 +  /**
  1.1809 +   * Performs a quaternion multiplication.
  1.1810 +   *
  1.1811 +   * @param {Array} aQuat
  1.1812 +   *                first operand
  1.1813 +   * @param {Array} aQuat2
  1.1814 +   *                second operand
  1.1815 +   * @param {Array} aDest
  1.1816 +   *                optional, quat4 receiving the operation result
  1.1817 +   *                if not specified result is written to the first operand
  1.1818 +   *
  1.1819 +   * @return {Array} the destination quat if specified, first operand otherwise
  1.1820 +   */
  1.1821 +  multiply: function Q4_multiply(aQuat, aQuat2, aDest)
  1.1822 +  {
  1.1823 +    if (!aDest) {
  1.1824 +      aDest = aQuat;
  1.1825 +    }
  1.1826 +
  1.1827 +    let qax = aQuat[0];
  1.1828 +    let qay = aQuat[1];
  1.1829 +    let qaz = aQuat[2];
  1.1830 +    let qaw = aQuat[3];
  1.1831 +    let qbx = aQuat2[0];
  1.1832 +    let qby = aQuat2[1];
  1.1833 +    let qbz = aQuat2[2];
  1.1834 +    let qbw = aQuat2[3];
  1.1835 +
  1.1836 +    aDest[0] = qax * qbw + qaw * qbx + qay * qbz - qaz * qby;
  1.1837 +    aDest[1] = qay * qbw + qaw * qby + qaz * qbx - qax * qbz;
  1.1838 +    aDest[2] = qaz * qbw + qaw * qbz + qax * qby - qay * qbx;
  1.1839 +    aDest[3] = qaw * qbw - qax * qbx - qay * qby - qaz * qbz;
  1.1840 +    return aDest;
  1.1841 +  },
  1.1842 +
  1.1843 +  /**
  1.1844 +   * Transforms a vec3 with the given quaternion.
  1.1845 +   *
  1.1846 +   * @param {Array} aQuat
  1.1847 +   *                quat4 to transform the vector with
  1.1848 +   * @param {Array} aVec
  1.1849 +   *                vec3 to transform
  1.1850 +   * @param {Array} aDest
  1.1851 +   *                optional, vec3 receiving the operation result
  1.1852 +   *                if not specified result is written to the first operand
  1.1853 +   *
  1.1854 +   * @return {Array} the destination vec3 if specified, aVec operand otherwise
  1.1855 +   */
  1.1856 +  multiplyVec3: function Q4_multiplyVec3(aQuat, aVec, aDest)
  1.1857 +  {
  1.1858 +    if (!aDest) {
  1.1859 +      aDest = aVec;
  1.1860 +    }
  1.1861 +
  1.1862 +    let x = aVec[0];
  1.1863 +    let y = aVec[1];
  1.1864 +    let z = aVec[2];
  1.1865 +
  1.1866 +    let qx = aQuat[0];
  1.1867 +    let qy = aQuat[1];
  1.1868 +    let qz = aQuat[2];
  1.1869 +    let qw = aQuat[3];
  1.1870 +
  1.1871 +    let ix = qw * x + qy * z - qz * y;
  1.1872 +    let iy = qw * y + qz * x - qx * z;
  1.1873 +    let iz = qw * z + qx * y - qy * x;
  1.1874 +    let iw = -qx * x - qy * y - qz * z;
  1.1875 +
  1.1876 +    aDest[0] = ix * qw + iw * -qx + iy * -qz - iz * -qy;
  1.1877 +    aDest[1] = iy * qw + iw * -qy + iz * -qx - ix * -qz;
  1.1878 +    aDest[2] = iz * qw + iw * -qz + ix * -qy - iy * -qx;
  1.1879 +    return aDest;
  1.1880 +  },
  1.1881 +
  1.1882 +  /**
  1.1883 +   * Performs a spherical linear interpolation between two quat4.
  1.1884 +   *
  1.1885 +   * @param {Array} aQuat
  1.1886 +   *                first quaternion
  1.1887 +   * @param {Array} aQuat2
  1.1888 +   *                second quaternion
  1.1889 +   * @param {Number} aSlerp
  1.1890 +   *                 interpolation amount between the two inputs
  1.1891 +   * @param {Array} aDest
  1.1892 +   *                 optional, quat4 receiving the operation result
  1.1893 +   *                 if not specified result is written to the first operand
  1.1894 +   *
  1.1895 +   * @return {Array} the destination quat if specified, first operand otherwise
  1.1896 +   */
  1.1897 +  slerp: function Q4_slerp(aQuat, aQuat2, aSlerp, aDest)
  1.1898 +  {
  1.1899 +    if (!aDest) {
  1.1900 +      aDest = aQuat;
  1.1901 +    }
  1.1902 +
  1.1903 +    let cosHalfTheta = aQuat[0] * aQuat2[0] +
  1.1904 +                       aQuat[1] * aQuat2[1] +
  1.1905 +                       aQuat[2] * aQuat2[2] +
  1.1906 +                       aQuat[3] * aQuat2[3];
  1.1907 +
  1.1908 +    if (Math.abs(cosHalfTheta) >= 1) {
  1.1909 +      aDest[0] = aQuat[0];
  1.1910 +      aDest[1] = aQuat[1];
  1.1911 +      aDest[2] = aQuat[2];
  1.1912 +      aDest[3] = aQuat[3];
  1.1913 +      return aDest;
  1.1914 +    }
  1.1915 +
  1.1916 +    let halfTheta = Math.acos(cosHalfTheta);
  1.1917 +    let sinHalfTheta = Math.sqrt(1 - cosHalfTheta * cosHalfTheta);
  1.1918 +
  1.1919 +    if (Math.abs(sinHalfTheta) < EPSILON) {
  1.1920 +      aDest[0] = (aQuat[0] * 0.5 + aQuat2[0] * 0.5);
  1.1921 +      aDest[1] = (aQuat[1] * 0.5 + aQuat2[1] * 0.5);
  1.1922 +      aDest[2] = (aQuat[2] * 0.5 + aQuat2[2] * 0.5);
  1.1923 +      aDest[3] = (aQuat[3] * 0.5 + aQuat2[3] * 0.5);
  1.1924 +      return aDest;
  1.1925 +    }
  1.1926 +
  1.1927 +    let ratioA = Math.sin((1 - aSlerp) * halfTheta) / sinHalfTheta;
  1.1928 +    let ratioB = Math.sin(aSlerp * halfTheta) / sinHalfTheta;
  1.1929 +
  1.1930 +    aDest[0] = (aQuat[0] * ratioA + aQuat2[0] * ratioB);
  1.1931 +    aDest[1] = (aQuat[1] * ratioA + aQuat2[1] * ratioB);
  1.1932 +    aDest[2] = (aQuat[2] * ratioA + aQuat2[2] * ratioB);
  1.1933 +    aDest[3] = (aQuat[3] * ratioA + aQuat2[3] * ratioB);
  1.1934 +    return aDest;
  1.1935 +  },
  1.1936 +
  1.1937 +  /**
  1.1938 +   * Calculates a 3x3 matrix from the given quat4.
  1.1939 +   *
  1.1940 +   * @param {Array} aQuat
  1.1941 +   *                quat4 to create matrix from
  1.1942 +   * @param {Array} aDest
  1.1943 +   *                optional, mat3 receiving the initialization result
  1.1944 +   *                if not specified, a new matrix is created
  1.1945 +   *
  1.1946 +   * @return {Array} the destination mat3 if specified, first operand otherwise
  1.1947 +   */
  1.1948 +  toMat3: function Q4_toMat3(aQuat, aDest)
  1.1949 +  {
  1.1950 +    if (!aDest) {
  1.1951 +      aDest = new Float32Array(9);
  1.1952 +    }
  1.1953 +
  1.1954 +    let x = aQuat[0];
  1.1955 +    let y = aQuat[1];
  1.1956 +    let z = aQuat[2];
  1.1957 +    let w = aQuat[3];
  1.1958 +
  1.1959 +    let x2 = x + x;
  1.1960 +    let y2 = y + y;
  1.1961 +    let z2 = z + z;
  1.1962 +    let xx = x * x2;
  1.1963 +    let xy = x * y2;
  1.1964 +    let xz = x * z2;
  1.1965 +    let yy = y * y2;
  1.1966 +    let yz = y * z2;
  1.1967 +    let zz = z * z2;
  1.1968 +    let wx = w * x2;
  1.1969 +    let wy = w * y2;
  1.1970 +    let wz = w * z2;
  1.1971 +
  1.1972 +    aDest[0] = 1 - (yy + zz);
  1.1973 +    aDest[1] = xy - wz;
  1.1974 +    aDest[2] = xz + wy;
  1.1975 +    aDest[3] = xy + wz;
  1.1976 +    aDest[4] = 1 - (xx + zz);
  1.1977 +    aDest[5] = yz - wx;
  1.1978 +    aDest[6] = xz - wy;
  1.1979 +    aDest[7] = yz + wx;
  1.1980 +    aDest[8] = 1 - (xx + yy);
  1.1981 +    return aDest;
  1.1982 +  },
  1.1983 +
  1.1984 +  /**
  1.1985 +   * Calculates a 4x4 matrix from the given quat4.
  1.1986 +   *
  1.1987 +   * @param {Array} aQuat
  1.1988 +   *                quat4 to create matrix from
  1.1989 +   * @param {Array} aDest
  1.1990 +   *                optional, mat4 receiving the initialization result
  1.1991 +   *                if not specified, a new matrix is created
  1.1992 +   *
  1.1993 +   * @return {Array} the destination mat4 if specified, first operand otherwise
  1.1994 +   */
  1.1995 +  toMat4: function Q4_toMat4(aQuat, aDest)
  1.1996 +  {
  1.1997 +    if (!aDest) {
  1.1998 +      aDest = new Float32Array(16);
  1.1999 +    }
  1.2000 +
  1.2001 +    let x = aQuat[0];
  1.2002 +    let y = aQuat[1];
  1.2003 +    let z = aQuat[2];
  1.2004 +    let w = aQuat[3];
  1.2005 +
  1.2006 +    let x2 = x + x;
  1.2007 +    let y2 = y + y;
  1.2008 +    let z2 = z + z;
  1.2009 +    let xx = x * x2;
  1.2010 +    let xy = x * y2;
  1.2011 +    let xz = x * z2;
  1.2012 +    let yy = y * y2;
  1.2013 +    let yz = y * z2;
  1.2014 +    let zz = z * z2;
  1.2015 +    let wx = w * x2;
  1.2016 +    let wy = w * y2;
  1.2017 +    let wz = w * z2;
  1.2018 +
  1.2019 +    aDest[0] = 1 - (yy + zz);
  1.2020 +    aDest[1] = xy - wz;
  1.2021 +    aDest[2] = xz + wy;
  1.2022 +    aDest[3] = 0;
  1.2023 +    aDest[4] = xy + wz;
  1.2024 +    aDest[5] = 1 - (xx + zz);
  1.2025 +    aDest[6] = yz - wx;
  1.2026 +    aDest[7] = 0;
  1.2027 +    aDest[8] = xz - wy;
  1.2028 +    aDest[9] = yz + wx;
  1.2029 +    aDest[10] = 1 - (xx + yy);
  1.2030 +    aDest[11] = 0;
  1.2031 +    aDest[12] = 0;
  1.2032 +    aDest[13] = 0;
  1.2033 +    aDest[14] = 0;
  1.2034 +    aDest[15] = 1;
  1.2035 +    return aDest;
  1.2036 +  },
  1.2037 +
  1.2038 +  /**
  1.2039 +   * Creates a rotation quaternion from axis-angle.
  1.2040 +   * This function expects that the axis is a normalized vector.
  1.2041 +   *
  1.2042 +   * @param {Array} aAxis
  1.2043 +   *                an array of elements representing the [x, y, z] axis
  1.2044 +   * @param {Number} aAngle
  1.2045 +   *                 the angle of rotation
  1.2046 +   * @param {Array} aDest
  1.2047 +   *                optional, quat4 receiving the initialization result
  1.2048 +   *                if not specified, a new quaternion is created
  1.2049 +   *
  1.2050 +   * @return {Array} the quaternion as [x, y, z, w]
  1.2051 +   */
  1.2052 +  fromAxis: function Q4_fromAxis(aAxis, aAngle, aDest)
  1.2053 +  {
  1.2054 +    if (!aDest) {
  1.2055 +      aDest = new Float32Array(4);
  1.2056 +    }
  1.2057 +
  1.2058 +    let ang = aAngle * 0.5;
  1.2059 +    let sin = Math.sin(ang);
  1.2060 +    let cos = Math.cos(ang);
  1.2061 +
  1.2062 +    aDest[0] = aAxis[0] * sin;
  1.2063 +    aDest[1] = aAxis[1] * sin;
  1.2064 +    aDest[2] = aAxis[2] * sin;
  1.2065 +    aDest[3] = cos;
  1.2066 +    return aDest;
  1.2067 +  },
  1.2068 +
  1.2069 +  /**
  1.2070 +   * Creates a rotation quaternion from Euler angles.
  1.2071 +   *
  1.2072 +   * @param {Number} aYaw
  1.2073 +   *                 the yaw angle of rotation
  1.2074 +   * @param {Number} aPitch
  1.2075 +   *                 the pitch angle of rotation
  1.2076 +   * @param {Number} aRoll
  1.2077 +   *                 the roll angle of rotation
  1.2078 +   * @param {Array} aDest
  1.2079 +   *                optional, quat4 receiving the initialization result
  1.2080 +   *                if not specified, a new quaternion is created
  1.2081 +   *
  1.2082 +   * @return {Array} the quaternion as [x, y, z, w]
  1.2083 +   */
  1.2084 +  fromEuler: function Q4_fromEuler(aYaw, aPitch, aRoll, aDest)
  1.2085 +  {
  1.2086 +    if (!aDest) {
  1.2087 +      aDest = new Float32Array(4);
  1.2088 +    }
  1.2089 +
  1.2090 +    let x = aPitch * 0.5;
  1.2091 +    let y = aYaw * 0.5;
  1.2092 +    let z = aRoll * 0.5;
  1.2093 +
  1.2094 +    let sinr = Math.sin(x);
  1.2095 +    let sinp = Math.sin(y);
  1.2096 +    let siny = Math.sin(z);
  1.2097 +    let cosr = Math.cos(x);
  1.2098 +    let cosp = Math.cos(y);
  1.2099 +    let cosy = Math.cos(z);
  1.2100 +
  1.2101 +    aDest[0] = sinr * cosp * cosy - cosr * sinp * siny;
  1.2102 +    aDest[1] = cosr * sinp * cosy + sinr * cosp * siny;
  1.2103 +    aDest[2] = cosr * cosp * siny - sinr * sinp * cosy;
  1.2104 +    aDest[3] = cosr * cosp * cosy + sinr * sinp * siny;
  1.2105 +    return aDest;
  1.2106 +  },
  1.2107 +
  1.2108 +  /**
  1.2109 +   * Returns a string representation of a quaternion.
  1.2110 +   *
  1.2111 +   * @param {Array} aQuat
  1.2112 +   *                quat4 to represent as a string
  1.2113 +   *
  1.2114 +   * @return {String} representation of the quaternion
  1.2115 +   */
  1.2116 +  str: function Q4_str(aQuat) {
  1.2117 +    return "[" + aQuat[0] + ", " +
  1.2118 +                 aQuat[1] + ", " +
  1.2119 +                 aQuat[2] + ", " +
  1.2120 +                 aQuat[3] + "]";
  1.2121 +  }
  1.2122 +};
  1.2123 +
  1.2124 +exports.quat4 = quat4;
  1.2125 +
  1.2126 +/**
  1.2127 + * Various algebraic math functions required by the engine.
  1.2128 + */
  1.2129 +let TiltMath = {
  1.2130 +
  1.2131 +  /**
  1.2132 +   * Helper function, converts degrees to radians.
  1.2133 +   *
  1.2134 +   * @param {Number} aDegrees
  1.2135 +   *                 the degrees to be converted to radians
  1.2136 +   *
  1.2137 +   * @return {Number} the degrees converted to radians
  1.2138 +   */
  1.2139 +  radians: function TM_radians(aDegrees)
  1.2140 +  {
  1.2141 +    return aDegrees * PI_OVER_180;
  1.2142 +  },
  1.2143 +
  1.2144 +  /**
  1.2145 +   * Helper function, converts radians to degrees.
  1.2146 +   *
  1.2147 +   * @param {Number} aRadians
  1.2148 +   *                 the radians to be converted to degrees
  1.2149 +   *
  1.2150 +   * @return {Number} the radians converted to degrees
  1.2151 +   */
  1.2152 +  degrees: function TM_degrees(aRadians)
  1.2153 +  {
  1.2154 +    return aRadians * INV_PI_OVER_180;
  1.2155 +  },
  1.2156 +
  1.2157 +  /**
  1.2158 +   * Re-maps a number from one range to another.
  1.2159 +   *
  1.2160 +   * @param {Number} aValue
  1.2161 +   *                 the number to map
  1.2162 +   * @param {Number} aLow1
  1.2163 +   *                 the normal lower bound of the number
  1.2164 +   * @param {Number} aHigh1
  1.2165 +   *                 the normal upper bound of the number
  1.2166 +   * @param {Number} aLow2
  1.2167 +   *                 the new lower bound of the number
  1.2168 +   * @param {Number} aHigh2
  1.2169 +   *                 the new upper bound of the number
  1.2170 +   *
  1.2171 +   * @return {Number} the remapped number
  1.2172 +   */
  1.2173 +  map: function TM_map(aValue, aLow1, aHigh1, aLow2, aHigh2)
  1.2174 +  {
  1.2175 +    return aLow2 + (aHigh2 - aLow2) * ((aValue - aLow1) / (aHigh1 - aLow1));
  1.2176 +  },
  1.2177 +
  1.2178 +  /**
  1.2179 +   * Returns if number is power of two.
  1.2180 +   *
  1.2181 +   * @param {Number} aNumber
  1.2182 +   *                 the number to be verified
  1.2183 +   *
  1.2184 +   * @return {Boolean} true if x is power of two
  1.2185 +   */
  1.2186 +  isPowerOfTwo: function TM_isPowerOfTwo(aNumber)
  1.2187 +  {
  1.2188 +    return !(aNumber & (aNumber - 1));
  1.2189 +  },
  1.2190 +
  1.2191 +  /**
  1.2192 +   * Returns the next closest power of two greater than a number.
  1.2193 +   *
  1.2194 +   * @param {Number} aNumber
  1.2195 +   *                 the number to be converted
  1.2196 +   *
  1.2197 +   * @return {Number} the next closest power of two for x
  1.2198 +   */
  1.2199 +  nextPowerOfTwo: function TM_nextPowerOfTwo(aNumber)
  1.2200 +  {
  1.2201 +    --aNumber;
  1.2202 +
  1.2203 +    for (let i = 1; i < 32; i <<= 1) {
  1.2204 +      aNumber = aNumber | aNumber >> i;
  1.2205 +    }
  1.2206 +    return aNumber + 1;
  1.2207 +  },
  1.2208 +
  1.2209 +  /**
  1.2210 +   * A convenient way of limiting values to a set boundary.
  1.2211 +   *
  1.2212 +   * @param {Number} aValue
  1.2213 +   *                 the number to be limited
  1.2214 +   * @param {Number} aMin
  1.2215 +   *                 the minimum allowed value for the number
  1.2216 +   * @param {Number} aMax
  1.2217 +   *                 the maximum allowed value for the number
  1.2218 +   */
  1.2219 +  clamp: function TM_clamp(aValue, aMin, aMax)
  1.2220 +  {
  1.2221 +    return Math.max(aMin, Math.min(aMax, aValue));
  1.2222 +  },
  1.2223 +
  1.2224 +  /**
  1.2225 +   * Convenient way to clamp a value to 0..1
  1.2226 +   *
  1.2227 +   * @param {Number} aValue
  1.2228 +   *                 the number to be limited
  1.2229 +   */
  1.2230 +  saturate: function TM_saturate(aValue)
  1.2231 +  {
  1.2232 +    return Math.max(0, Math.min(1, aValue));
  1.2233 +  },
  1.2234 +
  1.2235 +  /**
  1.2236 +   * Converts a hex color to rgba.
  1.2237 +   * If the passed param is invalid, it will be converted to [0, 0, 0, 1];
  1.2238 +   *
  1.2239 +   * @param {String} aColor
  1.2240 +   *                 color expressed in hex, or using rgb() or rgba()
  1.2241 +   *
  1.2242 +   * @return {Array} with 4 color 0..1 components: [red, green, blue, alpha]
  1.2243 +   */
  1.2244 +  hex2rgba: (function()
  1.2245 +  {
  1.2246 +    let cache = {};
  1.2247 +
  1.2248 +    return function TM_hex2rgba(aColor) {
  1.2249 +      let hex = aColor.charAt(0) === "#" ? aColor.substring(1) : aColor;
  1.2250 +
  1.2251 +      // check the cache to see if this color wasn't converted already
  1.2252 +      if (cache[hex] !== undefined) {
  1.2253 +        return cache[hex];
  1.2254 +      }
  1.2255 +
  1.2256 +      // e.g. "f00"
  1.2257 +      if (hex.length === 3) {
  1.2258 +        let r = parseInt(hex.substring(0, 1), 16) * FIFTEEN_OVER_225;
  1.2259 +        let g = parseInt(hex.substring(1, 2), 16) * FIFTEEN_OVER_225;
  1.2260 +        let b = parseInt(hex.substring(2, 3), 16) * FIFTEEN_OVER_225;
  1.2261 +
  1.2262 +        return (cache[hex] = [r, g, b, 1]);
  1.2263 +      }
  1.2264 +      // e.g. "f008"
  1.2265 +      if (hex.length === 4) {
  1.2266 +        let r = parseInt(hex.substring(0, 1), 16) * FIFTEEN_OVER_225;
  1.2267 +        let g = parseInt(hex.substring(1, 2), 16) * FIFTEEN_OVER_225;
  1.2268 +        let b = parseInt(hex.substring(2, 3), 16) * FIFTEEN_OVER_225;
  1.2269 +        let a = parseInt(hex.substring(3, 4), 16) * FIFTEEN_OVER_225;
  1.2270 +
  1.2271 +        return (cache[hex] = [r, g, b, a]);
  1.2272 +      }
  1.2273 +      // e.g. "ff0000"
  1.2274 +      if (hex.length === 6) {
  1.2275 +        let r = parseInt(hex.substring(0, 2), 16) * ONE_OVER_255;
  1.2276 +        let g = parseInt(hex.substring(2, 4), 16) * ONE_OVER_255;
  1.2277 +        let b = parseInt(hex.substring(4, 6), 16) * ONE_OVER_255;
  1.2278 +        let a = 1;
  1.2279 +
  1.2280 +        return (cache[hex] = [r, g, b, a]);
  1.2281 +      }
  1.2282 +      // e.g "ff0000aa"
  1.2283 +      if (hex.length === 8) {
  1.2284 +        let r = parseInt(hex.substring(0, 2), 16) * ONE_OVER_255;
  1.2285 +        let g = parseInt(hex.substring(2, 4), 16) * ONE_OVER_255;
  1.2286 +        let b = parseInt(hex.substring(4, 6), 16) * ONE_OVER_255;
  1.2287 +        let a = parseInt(hex.substring(6, 8), 16) * ONE_OVER_255;
  1.2288 +
  1.2289 +        return (cache[hex] = [r, g, b, a]);
  1.2290 +      }
  1.2291 +      // e.g. "rgba(255, 0, 0, 0.5)"
  1.2292 +      if (hex.match("^rgba")) {
  1.2293 +        let rgba = hex.substring(5, hex.length - 1).split(",");
  1.2294 +        rgba[0] *= ONE_OVER_255;
  1.2295 +        rgba[1] *= ONE_OVER_255;
  1.2296 +        rgba[2] *= ONE_OVER_255;
  1.2297 +        // in CSS, the alpha component of rgba() is already in the range 0..1
  1.2298 +
  1.2299 +        return (cache[hex] = rgba);
  1.2300 +      }
  1.2301 +      // e.g. "rgb(255, 0, 0)"
  1.2302 +      if (hex.match("^rgb")) {
  1.2303 +        let rgba = hex.substring(4, hex.length - 1).split(",");
  1.2304 +        rgba[0] *= ONE_OVER_255;
  1.2305 +        rgba[1] *= ONE_OVER_255;
  1.2306 +        rgba[2] *= ONE_OVER_255;
  1.2307 +        rgba[3] = 1;
  1.2308 +
  1.2309 +        return (cache[hex] = rgba);
  1.2310 +      }
  1.2311 +
  1.2312 +      // your argument is invalid
  1.2313 +      return (cache[hex] = [0, 0, 0, 1]);
  1.2314 +    };
  1.2315 +  }())
  1.2316 +};
  1.2317 +
  1.2318 +exports.TiltMath = TiltMath;
  1.2319 +
  1.2320 +// bind the owner object to the necessary functions
  1.2321 +TiltUtils.bindObjectFunc(vec3);
  1.2322 +TiltUtils.bindObjectFunc(mat3);
  1.2323 +TiltUtils.bindObjectFunc(mat4);
  1.2324 +TiltUtils.bindObjectFunc(quat4);
  1.2325 +TiltUtils.bindObjectFunc(TiltMath);

mercurial