/* * Copyright 2021 GFXFundamentals. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * Neither the name of GFXFundamentals. nor the names of his * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * Various 3d math functions. * * @module webgl-3d-math */ (function(root, factory) { // eslint-disable-line if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module. define([], factory); } else { // Browser globals root.m4 = factory(); } }(this, function() { "use strict"; /** * An array or typed array with 3 values. * @typedef {number[]|TypedArray} Vector3 * @memberOf module:webgl-3d-math */ /** * An array or typed array with 4 values. * @typedef {number[]|TypedArray} Vector4 * @memberOf module:webgl-3d-math */ /** * An array or typed array with 16 values. * @typedef {number[]|TypedArray} Matrix4 * @memberOf module:webgl-3d-math */ let MatType = Float32Array; /** * Sets the type this library creates for a Mat4 * @param {constructor} Ctor the constructor for the type. Either `Float32Array` or `Array` * @return {constructor} previous constructor for Mat4 */ function setDefaultType(Ctor) { const OldType = MatType; MatType = Ctor; return OldType; } /** * Takes two 4-by-4 matrices, a and b, and computes the product in the order * that pre-composes b with a. In other words, the matrix returned will * transform by b first and then a. Note this is subtly different from just * multiplying the matrices together. For given a and b, this function returns * the same object in both row-major and column-major mode. * @param {Matrix4} a A matrix. * @param {Matrix4} b A matrix. * @param {Matrix4} [dst] optional matrix to store result * @return {Matrix4} dst or a new matrix if none provided */ function multiply(a, b, dst) { dst = dst || new MatType(16); var b00 = b[0 * 4 + 0]; var b01 = b[0 * 4 + 1]; var b02 = b[0 * 4 + 2]; var b03 = b[0 * 4 + 3]; var b10 = b[1 * 4 + 0]; var b11 = b[1 * 4 + 1]; var b12 = b[1 * 4 + 2]; var b13 = b[1 * 4 + 3]; var b20 = b[2 * 4 + 0]; var b21 = b[2 * 4 + 1]; var b22 = b[2 * 4 + 2]; var b23 = b[2 * 4 + 3]; var b30 = b[3 * 4 + 0]; var b31 = b[3 * 4 + 1]; var b32 = b[3 * 4 + 2]; var b33 = b[3 * 4 + 3]; var a00 = a[0 * 4 + 0]; var a01 = a[0 * 4 + 1]; var a02 = a[0 * 4 + 2]; var a03 = a[0 * 4 + 3]; var a10 = a[1 * 4 + 0]; var a11 = a[1 * 4 + 1]; var a12 = a[1 * 4 + 2]; var a13 = a[1 * 4 + 3]; var a20 = a[2 * 4 + 0]; var a21 = a[2 * 4 + 1]; var a22 = a[2 * 4 + 2]; var a23 = a[2 * 4 + 3]; var a30 = a[3 * 4 + 0]; var a31 = a[3 * 4 + 1]; var a32 = a[3 * 4 + 2]; var a33 = a[3 * 4 + 3]; dst[ 0] = b00 * a00 + b01 * a10 + b02 * a20 + b03 * a30; dst[ 1] = b00 * a01 + b01 * a11 + b02 * a21 + b03 * a31; dst[ 2] = b00 * a02 + b01 * a12 + b02 * a22 + b03 * a32; dst[ 3] = b00 * a03 + b01 * a13 + b02 * a23 + b03 * a33; dst[ 4] = b10 * a00 + b11 * a10 + b12 * a20 + b13 * a30; dst[ 5] = b10 * a01 + b11 * a11 + b12 * a21 + b13 * a31; dst[ 6] = b10 * a02 + b11 * a12 + b12 * a22 + b13 * a32; dst[ 7] = b10 * a03 + b11 * a13 + b12 * a23 + b13 * a33; dst[ 8] = b20 * a00 + b21 * a10 + b22 * a20 + b23 * a30; dst[ 9] = b20 * a01 + b21 * a11 + b22 * a21 + b23 * a31; dst[10] = b20 * a02 + b21 * a12 + b22 * a22 + b23 * a32; dst[11] = b20 * a03 + b21 * a13 + b22 * a23 + b23 * a33; dst[12] = b30 * a00 + b31 * a10 + b32 * a20 + b33 * a30; dst[13] = b30 * a01 + b31 * a11 + b32 * a21 + b33 * a31; dst[14] = b30 * a02 + b31 * a12 + b32 * a22 + b33 * a32; dst[15] = b30 * a03 + b31 * a13 + b32 * a23 + b33 * a33; return dst; } /** * adds 2 vectors3s * @param {Vector3} a a * @param {Vector3} b b * @param {Vector3} dst optional vector3 to store result * @return {Vector3} dst or new Vector3 if not provided * @memberOf module:webgl-3d-math */ function addVectors(a, b, dst) { dst = dst || new MatType(3); dst[0] = a[0] + b[0]; dst[1] = a[1] + b[1]; dst[2] = a[2] + b[2]; return dst; } /** * subtracts 2 vectors3s * @param {Vector3} a a * @param {Vector3} b b * @param {Vector3} dst optional vector3 to store result * @return {Vector3} dst or new Vector3 if not provided * @memberOf module:webgl-3d-math */ function subtractVectors(a, b, dst) { dst = dst || new MatType(3); dst[0] = a[0] - b[0]; dst[1] = a[1] - b[1]; dst[2] = a[2] - b[2]; return dst; } /** * scale vectors3 * @param {Vector3} v vector * @param {Number} s scale * @param {Vector3} dst optional vector3 to store result * @return {Vector3} dst or new Vector3 if not provided * @memberOf module:webgl-3d-math */ function scaleVector(v, s, dst) { dst = dst || new MatType(3); dst[0] = v[0] * s; dst[1] = v[1] * s; dst[2] = v[2] * s; return dst; } /** * normalizes a vector. * @param {Vector3} v vector to normalize * @param {Vector3} dst optional vector3 to store result * @return {Vector3} dst or new Vector3 if not provided * @memberOf module:webgl-3d-math */ function normalize(v, dst) { dst = dst || new MatType(3); var length = Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); // make sure we don't divide by 0. if (length > 0.00001) { dst[0] = v[0] / length; dst[1] = v[1] / length; dst[2] = v[2] / length; } return dst; } /** * Computes the length of a vector * @param {Vector3} v vector to take length of * @return {number} length of vector */ function length(v) { return Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); } /** * Computes the length squared of a vector * @param {Vector3} v vector to take length of * @return {number} length sqaured of vector */ function lengthSq(v) { return v[0] * v[0] + v[1] * v[1] + v[2] * v[2]; } /** * Computes the cross product of 2 vectors3s * @param {Vector3} a a * @param {Vector3} b b * @param {Vector3} dst optional vector3 to store result * @return {Vector3} dst or new Vector3 if not provided * @memberOf module:webgl-3d-math */ function cross(a, b, dst) { dst = dst || new MatType(3); dst[0] = a[1] * b[2] - a[2] * b[1]; dst[1] = a[2] * b[0] - a[0] * b[2]; dst[2] = a[0] * b[1] - a[1] * b[0]; return dst; } /** * Computes the dot product of two vectors; assumes both vectors have * three entries. * @param {Vector3} a Operand vector. * @param {Vector3} b Operand vector. * @return {number} dot product * @memberOf module:webgl-3d-math */ function dot(a, b) { return (a[0] * b[0]) + (a[1] * b[1]) + (a[2] * b[2]); } /** * Computes the distance squared between 2 points * @param {Vector3} a * @param {Vector3} b * @return {number} distance squared between a and b */ function distanceSq(a, b) { const dx = a[0] - b[0]; const dy = a[1] - b[1]; const dz = a[2] - b[2]; return dx * dx + dy * dy + dz * dz; } /** * Computes the distance between 2 points * @param {Vector3} a * @param {Vector3} b * @return {number} distance between a and b */ function distance(a, b) { return Math.sqrt(distanceSq(a, b)); } /** * Makes an identity matrix. * @param {Matrix4} [dst] optional matrix to store result * @return {Matrix4} dst or a new matrix if none provided * @memberOf module:webgl-3d-math */ function identity(dst) { dst = dst || new MatType(16); dst[ 0] = 1; dst[ 1] = 0; dst[ 2] = 0; dst[ 3] = 0; dst[ 4] = 0; dst[ 5] = 1; dst[ 6] = 0; dst[ 7] = 0; dst[ 8] = 0; dst[ 9] = 0; dst[10] = 1; dst[11] = 0; dst[12] = 0; dst[13] = 0; dst[14] = 0; dst[15] = 1; return dst; } /** * Transposes a matrix. * @param {Matrix4} m matrix to transpose. * @param {Matrix4} [dst] optional matrix to store result * @return {Matrix4} dst or a new matrix if none provided * @memberOf module:webgl-3d-math */ function transpose(m, dst) { dst = dst || new MatType(16); dst[ 0] = m[0]; dst[ 1] = m[4]; dst[ 2] = m[8]; dst[ 3] = m[12]; dst[ 4] = m[1]; dst[ 5] = m[5]; dst[ 6] = m[9]; dst[ 7] = m[13]; dst[ 8] = m[2]; dst[ 9] = m[6]; dst[10] = m[10]; dst[11] = m[14]; dst[12] = m[3]; dst[13] = m[7]; dst[14] = m[11]; dst[15] = m[15]; return dst; } /** * Creates a lookAt matrix. * This is a world matrix for a camera. In other words it will transform * from the origin to a place and orientation in the world. For a view * matrix take the inverse of this. * @param {Vector3} cameraPosition position of the camera * @param {Vector3} target position of the target * @param {Vector3} up direction * @param {Matrix4} [dst] optional matrix to store result * @return {Matrix4} dst or a new matrix if none provided * @memberOf module:webgl-3d-math */ function lookAt(cameraPosition, target, up, dst) { dst = dst || new MatType(16); var zAxis = normalize( subtractVectors(cameraPosition, target)); var xAxis = normalize(cross(up, zAxis)); var yAxis = normalize(cross(zAxis, xAxis)); dst[ 0] = xAxis[0]; dst[ 1] = xAxis[1]; dst[ 2] = xAxis[2]; dst[ 3] = 0; dst[ 4] = yAxis[0]; dst[ 5] = yAxis[1]; dst[ 6] = yAxis[2]; dst[ 7] = 0; dst[ 8] = zAxis[0]; dst[ 9] = zAxis[1]; dst[10] = zAxis[2]; dst[11] = 0; dst[12] = cameraPosition[0]; dst[13] = cameraPosition[1]; dst[14] = cameraPosition[2]; dst[15] = 1; return dst; } /** * Computes a 4-by-4 perspective transformation matrix given the angular height * of the frustum, the aspect ratio, and the near and far clipping planes. The * arguments define a frustum extending in the negative z direction. The given * angle is the vertical angle of the frustum, and the horizontal angle is * determined to produce the given aspect ratio. The arguments near and far are * the distances to the near and far clipping planes. Note that near and far * are not z coordinates, but rather they are distances along the negative * z-axis. The matrix generated sends the viewing frustum to the unit box. * We assume a unit box extending from -1 to 1 in the x and y dimensions and * from -1 to 1 in the z dimension. * @param {number} fieldOfViewInRadians field of view in y axis. * @param {number} aspect aspect of viewport (width / height) * @param {number} near near Z clipping plane * @param {number} far far Z clipping plane * @param {Matrix4} [dst] optional matrix to store result * @return {Matrix4} dst or a new matrix if none provided * @memberOf module:webgl-3d-math */ function perspective(fieldOfViewInRadians, aspect, near, far, dst) { dst = dst || new MatType(16); var f = Math.tan(Math.PI * 0.5 - 0.5 * fieldOfViewInRadians); var rangeInv = 1.0 / (near - far); dst[ 0] = f / aspect; dst[ 1] = 0; dst[ 2] = 0; dst[ 3] = 0; dst[ 4] = 0; dst[ 5] = f; dst[ 6] = 0; dst[ 7] = 0; dst[ 8] = 0; dst[ 9] = 0; dst[10] = (near + far) * rangeInv; dst[11] = -1; dst[12] = 0; dst[13] = 0; dst[14] = near * far * rangeInv * 2; dst[15] = 0; return dst; } /** * Computes a 4-by-4 orthographic projection matrix given the coordinates of the * planes defining the axis-aligned, box-shaped viewing volume. The matrix * generated sends that box to the unit box. Note that although left and right * are x coordinates and bottom and top are y coordinates, near and far * are not z coordinates, but rather they are distances along the negative * z-axis. We assume a unit box extending from -1 to 1 in the x and y * dimensions and from -1 to 1 in the z dimension. * @param {number} left The x coordinate of the left plane of the box. * @param {number} right The x coordinate of the right plane of the box. * @param {number} bottom The y coordinate of the bottom plane of the box. * @param {number} top The y coordinate of the right plane of the box. * @param {number} near The negative z coordinate of the near plane of the box. * @param {number} far The negative z coordinate of the far plane of the box. * @param {Matrix4} [dst] optional matrix to store result * @return {Matrix4} dst or a new matrix if none provided * @memberOf module:webgl-3d-math */ function orthographic(left, right, bottom, top, near, far, dst) { dst = dst || new MatType(16); dst[ 0] = 2 / (right - left); dst[ 1] = 0; dst[ 2] = 0; dst[ 3] = 0; dst[ 4] = 0; dst[ 5] = 2 / (top - bottom); dst[ 6] = 0; dst[ 7] = 0; dst[ 8] = 0; dst[ 9] = 0; dst[10] = 2 / (near - far); dst[11] = 0; dst[12] = (left + right) / (left - right); dst[13] = (bottom + top) / (bottom - top); dst[14] = (near + far) / (near - far); dst[15] = 1; return dst; } /** * Computes a 4-by-4 perspective transformation matrix given the left, right, * top, bottom, near and far clipping planes. The arguments define a frustum * extending in the negative z direction. The arguments near and far are the * distances to the near and far clipping planes. Note that near and far are not * z coordinates, but rather they are distances along the negative z-axis. The * matrix generated sends the viewing frustum to the unit box. We assume a unit * box extending from -1 to 1 in the x and y dimensions and from -1 to 1 in the z * dimension. * @param {number} left The x coordinate of the left plane of the box. * @param {number} right The x coordinate of the right plane of the box. * @param {number} bottom The y coordinate of the bottom plane of the box. * @param {number} top The y coordinate of the right plane of the box. * @param {number} near The negative z coordinate of the near plane of the box. * @param {number} far The negative z coordinate of the far plane of the box. * @param {Matrix4} [dst] optional matrix to store result * @return {Matrix4} dst or a new matrix if none provided * @memberOf module:webgl-3d-math */ function frustum(left, right, bottom, top, near, far, dst) { dst = dst || new MatType(16); var dx = right - left; var dy = top - bottom; var dz = far - near; dst[ 0] = 2 * near / dx; dst[ 1] = 0; dst[ 2] = 0; dst[ 3] = 0; dst[ 4] = 0; dst[ 5] = 2 * near / dy; dst[ 6] = 0; dst[ 7] = 0; dst[ 8] = (left + right) / dx; dst[ 9] = (top + bottom) / dy; dst[10] = -(far + near) / dz; dst[11] = -1; dst[12] = 0; dst[13] = 0; dst[14] = -2 * near * far / dz; dst[15] = 0; return dst; } /** * Makes a translation matrix * @param {number} tx x translation. * @param {number} ty y translation. * @param {number} tz z translation. * @param {Matrix4} [dst] optional matrix to store result * @return {Matrix4} dst or a new matrix if none provided * @memberOf module:webgl-3d-math */ function translation(tx, ty, tz, dst) { dst = dst || new MatType(16); dst[ 0] = 1; dst[ 1] = 0; dst[ 2] = 0; dst[ 3] = 0; dst[ 4] = 0; dst[ 5] = 1; dst[ 6] = 0; dst[ 7] = 0; dst[ 8] = 0; dst[ 9] = 0; dst[10] = 1; dst[11] = 0; dst[12] = tx; dst[13] = ty; dst[14] = tz; dst[15] = 1; return dst; } /** * Multiply by translation matrix. * @param {Matrix4} m matrix to multiply * @param {number} tx x translation. * @param {number} ty y translation. * @param {number} tz z translation. * @param {Matrix4} [dst] optional matrix to store result * @return {Matrix4} dst or a new matrix if none provided * @memberOf module:webgl-3d-math */ function translate(m, tx, ty, tz, dst) { // This is the optimized version of // return multiply(m, translation(tx, ty, tz), dst); dst = dst || new MatType(16); var m00 = m[0]; var m01 = m[1]; var m02 = m[2]; var m03 = m[3]; var m10 = m[1 * 4 + 0]; var m11 = m[1 * 4 + 1]; var m12 = m[1 * 4 + 2]; var m13 = m[1 * 4 + 3]; var m20 = m[2 * 4 + 0]; var m21 = m[2 * 4 + 1]; var m22 = m[2 * 4 + 2]; var m23 = m[2 * 4 + 3]; var m30 = m[3 * 4 + 0]; var m31 = m[3 * 4 + 1]; var m32 = m[3 * 4 + 2]; var m33 = m[3 * 4 + 3]; if (m !== dst) { dst[ 0] = m00; dst[ 1] = m01; dst[ 2] = m02; dst[ 3] = m03; dst[ 4] = m10; dst[ 5] = m11; dst[ 6] = m12; dst[ 7] = m13; dst[ 8] = m20; dst[ 9] = m21; dst[10] = m22; dst[11] = m23; } dst[12] = m00 * tx + m10 * ty + m20 * tz + m30; dst[13] = m01 * tx + m11 * ty + m21 * tz + m31; dst[14] = m02 * tx + m12 * ty + m22 * tz + m32; dst[15] = m03 * tx + m13 * ty + m23 * tz + m33; return dst; } /** * Makes an x rotation matrix * @param {number} angleInRadians amount to rotate * @param {Matrix4} [dst] optional matrix to store result * @return {Matrix4} dst or a new matrix if none provided * @memberOf module:webgl-3d-math */ function xRotation(angleInRadians, dst) { dst = dst || new MatType(16); var c = Math.cos(angleInRadians); var s = Math.sin(angleInRadians); dst[ 0] = 1; dst[ 1] = 0; dst[ 2] = 0; dst[ 3] = 0; dst[ 4] = 0; dst[ 5] = c; dst[ 6] = s; dst[ 7] = 0; dst[ 8] = 0; dst[ 9] = -s; dst[10] = c; dst[11] = 0; dst[12] = 0; dst[13] = 0; dst[14] = 0; dst[15] = 1; return dst; } /** * Multiply by an x rotation matrix * @param {Matrix4} m matrix to multiply * @param {number} angleInRadians amount to rotate * @param {Matrix4} [dst] optional matrix to store result * @return {Matrix4} dst or a new matrix if none provided * @memberOf module:webgl-3d-math */ function xRotate(m, angleInRadians, dst) { // this is the optimized version of // return multiply(m, xRotation(angleInRadians), dst); dst = dst || new MatType(16); var m10 = m[4]; var m11 = m[5]; var m12 = m[6]; var m13 = m[7]; var m20 = m[8]; var m21 = m[9]; var m22 = m[10]; var m23 = m[11]; var c = Math.cos(angleInRadians); var s = Math.sin(angleInRadians); dst[4] = c * m10 + s * m20; dst[5] = c * m11 + s * m21; dst[6] = c * m12 + s * m22; dst[7] = c * m13 + s * m23; dst[8] = c * m20 - s * m10; dst[9] = c * m21 - s * m11; dst[10] = c * m22 - s * m12; dst[11] = c * m23 - s * m13; if (m !== dst) { dst[ 0] = m[ 0]; dst[ 1] = m[ 1]; dst[ 2] = m[ 2]; dst[ 3] = m[ 3]; dst[12] = m[12]; dst[13] = m[13]; dst[14] = m[14]; dst[15] = m[15]; } return dst; } /** * Makes an y rotation matrix * @param {number} angleInRadians amount to rotate * @param {Matrix4} [dst] optional matrix to store result * @return {Matrix4} dst or a new matrix if none provided * @memberOf module:webgl-3d-math */ function yRotation(angleInRadians, dst) { dst = dst || new MatType(16); var c = Math.cos(angleInRadians); var s = Math.sin(angleInRadians); dst[ 0] = c; dst[ 1] = 0; dst[ 2] = -s; dst[ 3] = 0; dst[ 4] = 0; dst[ 5] = 1; dst[ 6] = 0; dst[ 7] = 0; dst[ 8] = s; dst[ 9] = 0; dst[10] = c; dst[11] = 0; dst[12] = 0; dst[13] = 0; dst[14] = 0; dst[15] = 1; return dst; } /** * Multiply by an y rotation matrix * @param {Matrix4} m matrix to multiply * @param {number} angleInRadians amount to rotate * @param {Matrix4} [dst] optional matrix to store result * @return {Matrix4} dst or a new matrix if none provided * @memberOf module:webgl-3d-math */ function yRotate(m, angleInRadians, dst) { // this is the optimized version of // return multiply(m, yRotation(angleInRadians), dst); dst = dst || new MatType(16); var m00 = m[0 * 4 + 0]; var m01 = m[0 * 4 + 1]; var m02 = m[0 * 4 + 2]; var m03 = m[0 * 4 + 3]; var m20 = m[2 * 4 + 0]; var m21 = m[2 * 4 + 1]; var m22 = m[2 * 4 + 2]; var m23 = m[2 * 4 + 3]; var c = Math.cos(angleInRadians); var s = Math.sin(angleInRadians); dst[ 0] = c * m00 - s * m20; dst[ 1] = c * m01 - s * m21; dst[ 2] = c * m02 - s * m22; dst[ 3] = c * m03 - s * m23; dst[ 8] = c * m20 + s * m00; dst[ 9] = c * m21 + s * m01; dst[10] = c * m22 + s * m02; dst[11] = c * m23 + s * m03; if (m !== dst) { dst[ 4] = m[ 4]; dst[ 5] = m[ 5]; dst[ 6] = m[ 6]; dst[ 7] = m[ 7]; dst[12] = m[12]; dst[13] = m[13]; dst[14] = m[14]; dst[15] = m[15]; } return dst; } /** * Makes an z rotation matrix * @param {number} angleInRadians amount to rotate * @param {Matrix4} [dst] optional matrix to store result * @return {Matrix4} dst or a new matrix if none provided * @memberOf module:webgl-3d-math */ function zRotation(angleInRadians, dst) { dst = dst || new MatType(16); var c = Math.cos(angleInRadians); var s = Math.sin(angleInRadians); dst[ 0] = c; dst[ 1] = s; dst[ 2] = 0; dst[ 3] = 0; dst[ 4] = -s; dst[ 5] = c; dst[ 6] = 0; dst[ 7] = 0; dst[ 8] = 0; dst[ 9] = 0; dst[10] = 1; dst[11] = 0; dst[12] = 0; dst[13] = 0; dst[14] = 0; dst[15] = 1; return dst; } /** * Multiply by an z rotation matrix * @param {Matrix4} m matrix to multiply * @param {number} angleInRadians amount to rotate * @param {Matrix4} [dst] optional matrix to store result * @return {Matrix4} dst or a new matrix if none provided * @memberOf module:webgl-3d-math */ function zRotate(m, angleInRadians, dst) { // This is the optimized version of // return multiply(m, zRotation(angleInRadians), dst); dst = dst || new MatType(16); var m00 = m[0 * 4 + 0]; var m01 = m[0 * 4 + 1]; var m02 = m[0 * 4 + 2]; var m03 = m[0 * 4 + 3]; var m10 = m[1 * 4 + 0]; var m11 = m[1 * 4 + 1]; var m12 = m[1 * 4 + 2]; var m13 = m[1 * 4 + 3]; var c = Math.cos(angleInRadians); var s = Math.sin(angleInRadians); dst[ 0] = c * m00 + s * m10; dst[ 1] = c * m01 + s * m11; dst[ 2] = c * m02 + s * m12; dst[ 3] = c * m03 + s * m13; dst[ 4] = c * m10 - s * m00; dst[ 5] = c * m11 - s * m01; dst[ 6] = c * m12 - s * m02; dst[ 7] = c * m13 - s * m03; if (m !== dst) { dst[ 8] = m[ 8]; dst[ 9] = m[ 9]; dst[10] = m[10]; dst[11] = m[11]; dst[12] = m[12]; dst[13] = m[13]; dst[14] = m[14]; dst[15] = m[15]; } return dst; } /** * Makes an rotation matrix around an arbitrary axis * @param {Vector3} axis axis to rotate around * @param {number} angleInRadians amount to rotate * @param {Matrix4} [dst] optional matrix to store result * @return {Matrix4} dst or a new matrix if none provided * @memberOf module:webgl-3d-math */ function axisRotation(axis, angleInRadians, dst) { dst = dst || new MatType(16); var x = axis[0]; var y = axis[1]; var z = axis[2]; var n = Math.sqrt(x * x + y * y + z * z); x /= n; y /= n; z /= n; var xx = x * x; var yy = y * y; var zz = z * z; var c = Math.cos(angleInRadians); var s = Math.sin(angleInRadians); var oneMinusCosine = 1 - c; dst[ 0] = xx + (1 - xx) * c; dst[ 1] = x * y * oneMinusCosine + z * s; dst[ 2] = x * z * oneMinusCosine - y * s; dst[ 3] = 0; dst[ 4] = x * y * oneMinusCosine - z * s; dst[ 5] = yy + (1 - yy) * c; dst[ 6] = y * z * oneMinusCosine + x * s; dst[ 7] = 0; dst[ 8] = x * z * oneMinusCosine + y * s; dst[ 9] = y * z * oneMinusCosine - x * s; dst[10] = zz + (1 - zz) * c; dst[11] = 0; dst[12] = 0; dst[13] = 0; dst[14] = 0; dst[15] = 1; return dst; } /** * Multiply by an axis rotation matrix * @param {Matrix4} m matrix to multiply * @param {Vector3} axis axis to rotate around * @param {number} angleInRadians amount to rotate * @param {Matrix4} [dst] optional matrix to store result * @return {Matrix4} dst or a new matrix if none provided * @memberOf module:webgl-3d-math */ function axisRotate(m, axis, angleInRadians, dst) { // This is the optimized version of // return multiply(m, axisRotation(axis, angleInRadians), dst); dst = dst || new MatType(16); var x = axis[0]; var y = axis[1]; var z = axis[2]; var n = Math.sqrt(x * x + y * y + z * z); x /= n; y /= n; z /= n; var xx = x * x; var yy = y * y; var zz = z * z; var c = Math.cos(angleInRadians); var s = Math.sin(angleInRadians); var oneMinusCosine = 1 - c; var r00 = xx + (1 - xx) * c; var r01 = x * y * oneMinusCosine + z * s; var r02 = x * z * oneMinusCosine - y * s; var r10 = x * y * oneMinusCosine - z * s; var r11 = yy + (1 - yy) * c; var r12 = y * z * oneMinusCosine + x * s; var r20 = x * z * oneMinusCosine + y * s; var r21 = y * z * oneMinusCosine - x * s; var r22 = zz + (1 - zz) * c; var m00 = m[0]; var m01 = m[1]; var m02 = m[2]; var m03 = m[3]; var m10 = m[4]; var m11 = m[5]; var m12 = m[6]; var m13 = m[7]; var m20 = m[8]; var m21 = m[9]; var m22 = m[10]; var m23 = m[11]; dst[ 0] = r00 * m00 + r01 * m10 + r02 * m20; dst[ 1] = r00 * m01 + r01 * m11 + r02 * m21; dst[ 2] = r00 * m02 + r01 * m12 + r02 * m22; dst[ 3] = r00 * m03 + r01 * m13 + r02 * m23; dst[ 4] = r10 * m00 + r11 * m10 + r12 * m20; dst[ 5] = r10 * m01 + r11 * m11 + r12 * m21; dst[ 6] = r10 * m02 + r11 * m12 + r12 * m22; dst[ 7] = r10 * m03 + r11 * m13 + r12 * m23; dst[ 8] = r20 * m00 + r21 * m10 + r22 * m20; dst[ 9] = r20 * m01 + r21 * m11 + r22 * m21; dst[10] = r20 * m02 + r21 * m12 + r22 * m22; dst[11] = r20 * m03 + r21 * m13 + r22 * m23; if (m !== dst) { dst[12] = m[12]; dst[13] = m[13]; dst[14] = m[14]; dst[15] = m[15]; } return dst; } /** * Makes a scale matrix * @param {number} sx x scale. * @param {number} sy y scale. * @param {number} sz z scale. * @param {Matrix4} [dst] optional matrix to store result * @return {Matrix4} dst or a new matrix if none provided * @memberOf module:webgl-3d-math */ function scaling(sx, sy, sz, dst) { dst = dst || new MatType(16); dst[ 0] = sx; dst[ 1] = 0; dst[ 2] = 0; dst[ 3] = 0; dst[ 4] = 0; dst[ 5] = sy; dst[ 6] = 0; dst[ 7] = 0; dst[ 8] = 0; dst[ 9] = 0; dst[10] = sz; dst[11] = 0; dst[12] = 0; dst[13] = 0; dst[14] = 0; dst[15] = 1; return dst; } /** * Multiply by a scaling matrix * @param {Matrix4} m matrix to multiply * @param {number} sx x scale. * @param {number} sy y scale. * @param {number} sz z scale. * @param {Matrix4} [dst] optional matrix to store result * @return {Matrix4} dst or a new matrix if none provided * @memberOf module:webgl-3d-math */ function scale(m, sx, sy, sz, dst) { // This is the optimized version of // return multiply(m, scaling(sx, sy, sz), dst); dst = dst || new MatType(16); dst[ 0] = sx * m[0 * 4 + 0]; dst[ 1] = sx * m[0 * 4 + 1]; dst[ 2] = sx * m[0 * 4 + 2]; dst[ 3] = sx * m[0 * 4 + 3]; dst[ 4] = sy * m[1 * 4 + 0]; dst[ 5] = sy * m[1 * 4 + 1]; dst[ 6] = sy * m[1 * 4 + 2]; dst[ 7] = sy * m[1 * 4 + 3]; dst[ 8] = sz * m[2 * 4 + 0]; dst[ 9] = sz * m[2 * 4 + 1]; dst[10] = sz * m[2 * 4 + 2]; dst[11] = sz * m[2 * 4 + 3]; if (m !== dst) { dst[12] = m[12]; dst[13] = m[13]; dst[14] = m[14]; dst[15] = m[15]; } return dst; } /** * creates a matrix from translation, quaternion, scale * @param {Number[]} translation [x, y, z] translation * @param {Number[]} quaternion [x, y, z, z] quaternion rotation * @param {Number[]} scale [x, y, z] scale * @param {Matrix4} [dst] optional matrix to store result * @return {Matrix4} dst or a new matrix if none provided */ function compose(translation, quaternion, scale, dst) { dst = dst || new MatType(16); const x = quaternion[0]; const y = quaternion[1]; const z = quaternion[2]; const w = quaternion[3]; const x2 = x + x; const y2 = y + y; const z2 = z + z; const xx = x * x2; const xy = x * y2; const xz = x * z2; const yy = y * y2; const yz = y * z2; const zz = z * z2; const wx = w * x2; const wy = w * y2; const wz = w * z2; const sx = scale[0]; const sy = scale[1]; const sz = scale[2]; dst[0] = (1 - (yy + zz)) * sx; dst[1] = (xy + wz) * sx; dst[2] = (xz - wy) * sx; dst[3] = 0; dst[4] = (xy - wz) * sy; dst[5] = (1 - (xx + zz)) * sy; dst[6] = (yz + wx) * sy; dst[7] = 0; dst[ 8] = (xz + wy) * sz; dst[ 9] = (yz - wx) * sz; dst[10] = (1 - (xx + yy)) * sz; dst[11] = 0; dst[12] = translation[0]; dst[13] = translation[1]; dst[14] = translation[2]; dst[15] = 1; return dst; } function quatFromRotationMatrix(m, dst) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) const m11 = m[0]; const m12 = m[4]; const m13 = m[8]; const m21 = m[1]; const m22 = m[5]; const m23 = m[9]; const m31 = m[2]; const m32 = m[6]; const m33 = m[10]; const trace = m11 + m22 + m33; if (trace > 0) { const s = 0.5 / Math.sqrt(trace + 1); dst[3] = 0.25 / s; dst[0] = (m32 - m23) * s; dst[1] = (m13 - m31) * s; dst[2] = (m21 - m12) * s; } else if (m11 > m22 && m11 > m33) { const s = 2 * Math.sqrt(1 + m11 - m22 - m33); dst[3] = (m32 - m23) / s; dst[0] = 0.25 * s; dst[1] = (m12 + m21) / s; dst[2] = (m13 + m31) / s; } else if (m22 > m33) { const s = 2 * Math.sqrt(1 + m22 - m11 - m33); dst[3] = (m13 - m31) / s; dst[0] = (m12 + m21) / s; dst[1] = 0.25 * s; dst[2] = (m23 + m32) / s; } else { const s = 2 * Math.sqrt(1 + m33 - m11 - m22); dst[3] = (m21 - m12) / s; dst[0] = (m13 + m31) / s; dst[1] = (m23 + m32) / s; dst[2] = 0.25 * s; } } function decompose(mat, translation, quaternion, scale) { let sx = m4.length(mat.slice(0, 3)); const sy = m4.length(mat.slice(4, 7)); const sz = m4.length(mat.slice(8, 11)); // if determinate is negative, we need to invert one scale const det = determinate(mat); if (det < 0) { sx = -sx; } translation[0] = mat[12]; translation[1] = mat[13]; translation[2] = mat[14]; // scale the rotation part const matrix = m4.copy(mat); const invSX = 1 / sx; const invSY = 1 / sy; const invSZ = 1 / sz; matrix[0] *= invSX; matrix[1] *= invSX; matrix[2] *= invSX; matrix[4] *= invSY; matrix[5] *= invSY; matrix[6] *= invSY; matrix[8] *= invSZ; matrix[9] *= invSZ; matrix[10] *= invSZ; quatFromRotationMatrix(matrix, quaternion); scale[0] = sx; scale[1] = sy; scale[2] = sz; } function determinate(m) { var m00 = m[0 * 4 + 0]; var m01 = m[0 * 4 + 1]; var m02 = m[0 * 4 + 2]; var m03 = m[0 * 4 + 3]; var m10 = m[1 * 4 + 0]; var m11 = m[1 * 4 + 1]; var m12 = m[1 * 4 + 2]; var m13 = m[1 * 4 + 3]; var m20 = m[2 * 4 + 0]; var m21 = m[2 * 4 + 1]; var m22 = m[2 * 4 + 2]; var m23 = m[2 * 4 + 3]; var m30 = m[3 * 4 + 0]; var m31 = m[3 * 4 + 1]; var m32 = m[3 * 4 + 2]; var m33 = m[3 * 4 + 3]; var tmp_0 = m22 * m33; var tmp_1 = m32 * m23; var tmp_2 = m12 * m33; var tmp_3 = m32 * m13; var tmp_4 = m12 * m23; var tmp_5 = m22 * m13; var tmp_6 = m02 * m33; var tmp_7 = m32 * m03; var tmp_8 = m02 * m23; var tmp_9 = m22 * m03; var tmp_10 = m02 * m13; var tmp_11 = m12 * m03; var t0 = (tmp_0 * m11 + tmp_3 * m21 + tmp_4 * m31) - (tmp_1 * m11 + tmp_2 * m21 + tmp_5 * m31); var t1 = (tmp_1 * m01 + tmp_6 * m21 + tmp_9 * m31) - (tmp_0 * m01 + tmp_7 * m21 + tmp_8 * m31); var t2 = (tmp_2 * m01 + tmp_7 * m11 + tmp_10 * m31) - (tmp_3 * m01 + tmp_6 * m11 + tmp_11 * m31); var t3 = (tmp_5 * m01 + tmp_8 * m11 + tmp_11 * m21) - (tmp_4 * m01 + tmp_9 * m11 + tmp_10 * m21); return 1.0 / (m00 * t0 + m10 * t1 + m20 * t2 + m30 * t3); } /** * Computes the inverse of a matrix. * @param {Matrix4} m matrix to compute inverse of * @param {Matrix4} [dst] optional matrix to store result * @return {Matrix4} dst or a new matrix if none provided * @memberOf module:webgl-3d-math */ function inverse(m, dst) { dst = dst || new MatType(16); var m00 = m[0 * 4 + 0]; var m01 = m[0 * 4 + 1]; var m02 = m[0 * 4 + 2]; var m03 = m[0 * 4 + 3]; var m10 = m[1 * 4 + 0]; var m11 = m[1 * 4 + 1]; var m12 = m[1 * 4 + 2]; var m13 = m[1 * 4 + 3]; var m20 = m[2 * 4 + 0]; var m21 = m[2 * 4 + 1]; var m22 = m[2 * 4 + 2]; var m23 = m[2 * 4 + 3]; var m30 = m[3 * 4 + 0]; var m31 = m[3 * 4 + 1]; var m32 = m[3 * 4 + 2]; var m33 = m[3 * 4 + 3]; var tmp_0 = m22 * m33; var tmp_1 = m32 * m23; var tmp_2 = m12 * m33; var tmp_3 = m32 * m13; var tmp_4 = m12 * m23; var tmp_5 = m22 * m13; var tmp_6 = m02 * m33; var tmp_7 = m32 * m03; var tmp_8 = m02 * m23; var tmp_9 = m22 * m03; var tmp_10 = m02 * m13; var tmp_11 = m12 * m03; var tmp_12 = m20 * m31; var tmp_13 = m30 * m21; var tmp_14 = m10 * m31; var tmp_15 = m30 * m11; var tmp_16 = m10 * m21; var tmp_17 = m20 * m11; var tmp_18 = m00 * m31; var tmp_19 = m30 * m01; var tmp_20 = m00 * m21; var tmp_21 = m20 * m01; var tmp_22 = m00 * m11; var tmp_23 = m10 * m01; var t0 = (tmp_0 * m11 + tmp_3 * m21 + tmp_4 * m31) - (tmp_1 * m11 + tmp_2 * m21 + tmp_5 * m31); var t1 = (tmp_1 * m01 + tmp_6 * m21 + tmp_9 * m31) - (tmp_0 * m01 + tmp_7 * m21 + tmp_8 * m31); var t2 = (tmp_2 * m01 + tmp_7 * m11 + tmp_10 * m31) - (tmp_3 * m01 + tmp_6 * m11 + tmp_11 * m31); var t3 = (tmp_5 * m01 + tmp_8 * m11 + tmp_11 * m21) - (tmp_4 * m01 + tmp_9 * m11 + tmp_10 * m21); var d = 1.0 / (m00 * t0 + m10 * t1 + m20 * t2 + m30 * t3); dst[0] = d * t0; dst[1] = d * t1; dst[2] = d * t2; dst[3] = d * t3; dst[4] = d * ((tmp_1 * m10 + tmp_2 * m20 + tmp_5 * m30) - (tmp_0 * m10 + tmp_3 * m20 + tmp_4 * m30)); dst[5] = d * ((tmp_0 * m00 + tmp_7 * m20 + tmp_8 * m30) - (tmp_1 * m00 + tmp_6 * m20 + tmp_9 * m30)); dst[6] = d * ((tmp_3 * m00 + tmp_6 * m10 + tmp_11 * m30) - (tmp_2 * m00 + tmp_7 * m10 + tmp_10 * m30)); dst[7] = d * ((tmp_4 * m00 + tmp_9 * m10 + tmp_10 * m20) - (tmp_5 * m00 + tmp_8 * m10 + tmp_11 * m20)); dst[8] = d * ((tmp_12 * m13 + tmp_15 * m23 + tmp_16 * m33) - (tmp_13 * m13 + tmp_14 * m23 + tmp_17 * m33)); dst[9] = d * ((tmp_13 * m03 + tmp_18 * m23 + tmp_21 * m33) - (tmp_12 * m03 + tmp_19 * m23 + tmp_20 * m33)); dst[10] = d * ((tmp_14 * m03 + tmp_19 * m13 + tmp_22 * m33) - (tmp_15 * m03 + tmp_18 * m13 + tmp_23 * m33)); dst[11] = d * ((tmp_17 * m03 + tmp_20 * m13 + tmp_23 * m23) - (tmp_16 * m03 + tmp_21 * m13 + tmp_22 * m23)); dst[12] = d * ((tmp_14 * m22 + tmp_17 * m32 + tmp_13 * m12) - (tmp_16 * m32 + tmp_12 * m12 + tmp_15 * m22)); dst[13] = d * ((tmp_20 * m32 + tmp_12 * m02 + tmp_19 * m22) - (tmp_18 * m22 + tmp_21 * m32 + tmp_13 * m02)); dst[14] = d * ((tmp_18 * m12 + tmp_23 * m32 + tmp_15 * m02) - (tmp_22 * m32 + tmp_14 * m02 + tmp_19 * m12)); dst[15] = d * ((tmp_22 * m22 + tmp_16 * m02 + tmp_21 * m12) - (tmp_20 * m12 + tmp_23 * m22 + tmp_17 * m02)); return dst; } /** * Takes a matrix and a vector with 4 entries, transforms that vector by * the matrix, and returns the result as a vector with 4 entries. * @param {Matrix4} m The matrix. * @param {Vector4} v The point in homogenous coordinates. * @param {Vector4} dst optional vector4 to store result * @return {Vector4} dst or new Vector4 if not provided * @memberOf module:webgl-3d-math */ function transformVector(m, v, dst) { dst = dst || new MatType(4); for (var i = 0; i < 4; ++i) { dst[i] = 0.0; for (var j = 0; j < 4; ++j) { dst[i] += v[j] * m[j * 4 + i]; } } return dst; } /** * Takes a 4-by-4 matrix and a vector with 3 entries, * interprets the vector as a point, transforms that point by the matrix, and * returns the result as a vector with 3 entries. * @param {Matrix4} m The matrix. * @param {Vector3} v The point. * @param {Vector4} dst optional vector4 to store result * @return {Vector4} dst or new Vector4 if not provided * @memberOf module:webgl-3d-math */ function transformPoint(m, v, dst) { dst = dst || new MatType(3); var v0 = v[0]; var v1 = v[1]; var v2 = v[2]; var d = v0 * m[0 * 4 + 3] + v1 * m[1 * 4 + 3] + v2 * m[2 * 4 + 3] + m[3 * 4 + 3]; dst[0] = (v0 * m[0 * 4 + 0] + v1 * m[1 * 4 + 0] + v2 * m[2 * 4 + 0] + m[3 * 4 + 0]) / d; dst[1] = (v0 * m[0 * 4 + 1] + v1 * m[1 * 4 + 1] + v2 * m[2 * 4 + 1] + m[3 * 4 + 1]) / d; dst[2] = (v0 * m[0 * 4 + 2] + v1 * m[1 * 4 + 2] + v2 * m[2 * 4 + 2] + m[3 * 4 + 2]) / d; return dst; } /** * Takes a 4-by-4 matrix and a vector with 3 entries, interprets the vector as a * direction, transforms that direction by the matrix, and returns the result; * assumes the transformation of 3-dimensional space represented by the matrix * is parallel-preserving, i.e. any combination of rotation, scaling and * translation, but not a perspective distortion. Returns a vector with 3 * entries. * @param {Matrix4} m The matrix. * @param {Vector3} v The direction. * @param {Vector4} dst optional vector4 to store result * @return {Vector4} dst or new Vector4 if not provided * @memberOf module:webgl-3d-math */ function transformDirection(m, v, dst) { dst = dst || new MatType(3); var v0 = v[0]; var v1 = v[1]; var v2 = v[2]; dst[0] = v0 * m[0 * 4 + 0] + v1 * m[1 * 4 + 0] + v2 * m[2 * 4 + 0]; dst[1] = v0 * m[0 * 4 + 1] + v1 * m[1 * 4 + 1] + v2 * m[2 * 4 + 1]; dst[2] = v0 * m[0 * 4 + 2] + v1 * m[1 * 4 + 2] + v2 * m[2 * 4 + 2]; return dst; } /** * Takes a 4-by-4 matrix m and a vector v with 3 entries, interprets the vector * as a normal to a surface, and computes a vector which is normal upon * transforming that surface by the matrix. The effect of this function is the * same as transforming v (as a direction) by the inverse-transpose of m. This * function assumes the transformation of 3-dimensional space represented by the * matrix is parallel-preserving, i.e. any combination of rotation, scaling and * translation, but not a perspective distortion. Returns a vector with 3 * entries. * @param {Matrix4} m The matrix. * @param {Vector3} v The normal. * @param {Vector3} [dst] The direction. * @return {Vector3} The transformed direction. * @memberOf module:webgl-3d-math */ function transformNormal(m, v, dst) { dst = dst || new MatType(3); var mi = inverse(m); var v0 = v[0]; var v1 = v[1]; var v2 = v[2]; dst[0] = v0 * mi[0 * 4 + 0] + v1 * mi[0 * 4 + 1] + v2 * mi[0 * 4 + 2]; dst[1] = v0 * mi[1 * 4 + 0] + v1 * mi[1 * 4 + 1] + v2 * mi[1 * 4 + 2]; dst[2] = v0 * mi[2 * 4 + 0] + v1 * mi[2 * 4 + 1] + v2 * mi[2 * 4 + 2]; return dst; } function copy(src, dst) { dst = dst || new MatType(16); dst[ 0] = src[ 0]; dst[ 1] = src[ 1]; dst[ 2] = src[ 2]; dst[ 3] = src[ 3]; dst[ 4] = src[ 4]; dst[ 5] = src[ 5]; dst[ 6] = src[ 6]; dst[ 7] = src[ 7]; dst[ 8] = src[ 8]; dst[ 9] = src[ 9]; dst[10] = src[10]; dst[11] = src[11]; dst[12] = src[12]; dst[13] = src[13]; dst[14] = src[14]; dst[15] = src[15]; return dst; } return { copy: copy, lookAt: lookAt, addVectors: addVectors, subtractVectors: subtractVectors, scaleVector: scaleVector, distance: distance, distanceSq: distanceSq, normalize: normalize, compose: compose, cross: cross, decompose: decompose, dot: dot, identity: identity, transpose: transpose, length: length, lengthSq: lengthSq, orthographic: orthographic, frustum: frustum, perspective: perspective, translation: translation, translate: translate, xRotation: xRotation, yRotation: yRotation, zRotation: zRotation, xRotate: xRotate, yRotate: yRotate, zRotate: zRotate, axisRotation: axisRotation, axisRotate: axisRotate, scaling: scaling, scale: scale, multiply: multiply, inverse: inverse, transformVector: transformVector, transformPoint: transformPoint, transformDirection: transformDirection, transformNormal: transformNormal, setDefaultType: setDefaultType, }; }));