Source: math/mat4.js

if (typeof(define) !== "function") {
    var define = require("amdefine")(module);
}
define([
        "odin/math/mathf",
        "odin/math/vec3"
    ],
    function(Mathf, Vec3) {
        "use strict";


        var sqrt = Math.sqrt,
            cos = Math.cos,
            sin = Math.sin,
            tan = Math.tan;

        /**
         * @class Mat4
         * 4x4 matrix
         * @param Number m11
         * @param Number m12
         * @param Number m13
         * @param Number m14
         * @param Number m21
         * @param Number m22
         * @param Number m23
         * @param Number m24
         * @param Number m31
         * @param Number m32
         * @param Number m33
         * @param Number m34
         * @param Number m41
         * @param Number m42
         * @param Number m43
         * @param Number m44
         */
        function Mat4(m11, m12, m13, m14, m21, m22, m23, m24, m31, m32, m33, m34, m41, m42, m43, m44) {
            var te = new Float32Array(16);

            /**
             * @property Float32Array elements
             * @memberof Odin.Mat4
             */
            this.elements = te;

            te[0] = m11 !== undefined ? m11 : 1;
            te[4] = m12 || 0;
            te[8] = m13 || 0;
            te[12] = m14 || 0;
            te[1] = m21 || 0;
            te[5] = m22 !== undefined ? m22 : 1;
            te[9] = m23 || 0;
            te[13] = m24 || 0;
            te[2] = m31 || 0;
            te[6] = m32 || 0;
            te[10] = m33 !== undefined ? m33 : 1;
            te[14] = m34 || 0;
            te[3] = m41 || 0;
            te[7] = m42 || 0;
            te[11] = m43 || 0;
            te[15] = m44 !== undefined ? m44 : 1;
        }

        Mathf._classes["Mat4"] = Mat4;

        /**
         * @method clone
         * @memberof Odin.Mat4
         * returns new instance of this
         * @return Mat4
         */
        Mat4.prototype.clone = function() {
            var te = this.elements;

            return new Mat4(
                te[0], te[4], te[8], te[12],
                te[1], te[5], te[9], te[13],
                te[2], te[6], te[10], te[14],
                te[3], te[7], te[11], te[15]
            );
        };

        /**
         * @method copy
         * @memberof Odin.Mat4
         * copies other
         * @param Mat4 other
         * @return this
         */
        Mat4.prototype.copy = function(other) {
            var te = this.elements,
                me = other.elements;

            te[0] = me[0];
            te[1] = me[1];
            te[2] = me[2];
            te[3] = me[3];
            te[4] = me[4];
            te[5] = me[5];
            te[6] = me[6];
            te[7] = me[7];
            te[8] = me[8];
            te[9] = me[9];
            te[10] = me[10];
            te[11] = me[11];
            te[12] = me[12];
            te[13] = me[13];
            te[14] = me[14];
            te[15] = me[15];

            return this;
        };

        /**
         * @method set
         * @memberof Odin.Mat4
         * sets values of this
         * @param Number m11
         * @param Number m12
         * @param Number m13
         * @param Number m14
         * @param Number m21
         * @param Number m22
         * @param Number m23
         * @param Number m24
         * @param Number m31
         * @param Number m32
         * @param Number m33
         * @param Number m34
         * @param Number m41
         * @param Number m42
         * @param Number m43
         * @param Number m44
         * @return this
         */
        Mat4.prototype.set = function(m11, m12, m13, m14, m21, m22, m23, m24, m31, m32, m33, m34, m41, m42, m43, m44) {
            var te = this.elements;

            te[0] = m11;
            te[4] = m12;
            te[8] = m13;
            te[12] = m14;
            te[1] = m21;
            te[5] = m22;
            te[9] = m23;
            te[13] = m24;
            te[2] = m31;
            te[6] = m32;
            te[10] = m33;
            te[14] = m34;
            te[3] = m41;
            te[7] = m42;
            te[11] = m43;
            te[15] = m44;

            return this;
        };

        /**
         * @method mul
         * @memberof Odin.Mat4
         * muliples this's values by other's
         * @param Mat4 other
         * @return this
         */
        Mat4.prototype.mul = function(other) {
            var ae = this.elements,
                be = other.elements,

                a11 = ae[0],
                a12 = ae[4],
                a13 = ae[8],
                a14 = ae[12],
                a21 = ae[1],
                a22 = ae[5],
                a23 = ae[9],
                a24 = ae[13],
                a31 = ae[2],
                a32 = ae[6],
                a33 = ae[10],
                a34 = ae[14],
                a41 = ae[3],
                a42 = ae[7],
                a43 = ae[11],
                a44 = ae[15],

                b11 = be[0],
                b12 = be[4],
                b13 = be[8],
                b14 = be[12],
                b21 = be[1],
                b22 = be[5],
                b23 = be[9],
                b24 = be[13],
                b31 = be[2],
                b32 = be[6],
                b33 = be[10],
                b34 = be[14],
                b41 = be[3],
                b42 = be[7],
                b43 = be[11],
                b44 = be[15];

            ae[0] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41;
            ae[4] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42;
            ae[8] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43;
            ae[12] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44;

            ae[1] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41;
            ae[5] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42;
            ae[9] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43;
            ae[13] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44;

            ae[2] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41;
            ae[6] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42;
            ae[10] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43;
            ae[14] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44;

            ae[3] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41;
            ae[7] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42;
            ae[11] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43;
            ae[15] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44;

            return this;
        };

        /**
         * @method mmul
         * @memberof Odin.Mat4
         * muliples a and b saves it in this
         * @param Mat4 a
         * @param Mat4 b
         * @return this
         */
        Mat4.prototype.mmul = function(a, b) {
            var te = this.elements,
                ae = a.elements,
                be = b.elements,

                a11 = ae[0],
                a12 = ae[4],
                a13 = ae[8],
                a14 = ae[12],
                a21 = ae[1],
                a22 = ae[5],
                a23 = ae[9],
                a24 = ae[13],
                a31 = ae[2],
                a32 = ae[6],
                a33 = ae[10],
                a34 = ae[14],
                a41 = ae[3],
                a42 = ae[7],
                a43 = ae[11],
                a44 = ae[15],

                b11 = be[0],
                b12 = be[4],
                b13 = be[8],
                b14 = be[12],
                b21 = be[1],
                b22 = be[5],
                b23 = be[9],
                b24 = be[13],
                b31 = be[2],
                b32 = be[6],
                b33 = be[10],
                b34 = be[14],
                b41 = be[3],
                b42 = be[7],
                b43 = be[11],
                b44 = be[15];

            te[0] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41;
            te[4] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42;
            te[8] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43;
            te[12] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44;

            te[1] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41;
            te[5] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42;
            te[9] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43;
            te[13] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44;

            te[2] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41;
            te[6] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42;
            te[10] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43;
            te[14] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44;

            te[3] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41;
            te[7] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42;
            te[11] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43;
            te[15] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44;

            return this;
        };

        /**
         * @method smul
         * @memberof Odin.Mat4
         * muliples this by a scalar value
         * @param Number s
         * @return this
         */
        Mat4.prototype.smul = function(s) {
            var te = this.elements;

            te[0] *= s;
            te[1] *= s;
            te[2] *= s;
            te[3] *= s;
            te[4] *= s;
            te[5] *= s;
            te[6] *= s;
            te[7] *= s;
            te[8] *= s;
            te[9] *= s;
            te[10] *= s;
            te[11] *= s;
            te[12] *= s;
            te[13] *= s;
            te[14] *= s;
            te[15] *= s;

            return this;
        };

        /**
         * @method sdiv
         * @memberof Odin.Mat4
         * divides this by scalar value
         * @param Number s
         * @return this
         */
        Mat4.prototype.sdiv = function(s) {
            var te = this.elements;

            s = s !== 0 ? 1 / s : 1;

            te[0] *= s;
            te[1] *= s;
            te[2] *= s;
            te[3] *= s;
            te[4] *= s;
            te[5] *= s;
            te[6] *= s;
            te[7] *= s;
            te[8] *= s;
            te[9] *= s;
            te[10] *= s;
            te[11] *= s;
            te[12] *= s;
            te[13] *= s;
            te[14] *= s;
            te[15] *= s;

            return this;
        };

        /**
         * @method identity
         * @memberof Odin.Mat4
         * identity matrix
         * @return this
         */
        Mat4.prototype.identity = function() {
            var te = this.elements;

            te[0] = 1;
            te[1] = 0;
            te[2] = 0;
            te[3] = 0;
            te[4] = 0;
            te[5] = 1;
            te[6] = 0;
            te[7] = 0;
            te[8] = 0;
            te[9] = 0;
            te[10] = 1;
            te[11] = 0;
            te[12] = 0;
            te[13] = 0;
            te[14] = 0;
            te[15] = 1;

            return this;
        };

        /**
         * @method zero
         * @memberof Odin.Mat4
         * zero matrix
         * @return this
         */
        Mat4.prototype.zero = function() {
            var te = this.elements;

            te[0] = 0;
            te[1] = 0;
            te[2] = 0;
            te[3] = 0;
            te[4] = 0;
            te[5] = 0;
            te[6] = 0;
            te[7] = 0;
            te[8] = 0;
            te[9] = 0;
            te[10] = 0;
            te[11] = 0;
            te[12] = 0;
            te[13] = 0;
            te[14] = 0;
            te[15] = 0;

            return this;
        };

        /**
         * @method determinant
         * @memberof Odin.Mat4
         * returns the determinant of this
         * @return this
         */
        Mat4.prototype.determinant = function() {
            var m11 = ae[0],
                m12 = ae[4],
                m13 = ae[8],
                m14 = ae[12],
                m21 = ae[1],
                m22 = ae[5],
                m23 = ae[9],
                m24 = ae[13],
                m31 = ae[2],
                m32 = ae[6],
                m33 = ae[10],
                m34 = ae[14],
                m41 = ae[3],
                m42 = ae[7],
                m43 = ae[11],
                m44 = ae[15];

            return (
                m41 * (m14 * m23 * m32 - m13 * m24 * m32 - m14 * m22 * m33 + m12 * m24 * m33 + m13 * m22 * m34 - m12 * m23 * m34) +
                m42 * (m11 * m23 * m34 - m11 * m24 * m33 + m14 * m21 * m33 - m13 * m21 * m34 + m13 * m24 * m31 - m14 * m23 * m31) +
                m43 * (m11 * m24 * m32 - m11 * m22 * m34 - m14 * m21 * m32 + m12 * m21 * m34 + m14 * m22 * m31 - m12 * m24 * m31) +
                m44 * (-m13 * m22 * m31 - m11 * m23 * m32 + m11 * m22 * m33 + m13 * m21 * m32 - m12 * m21 * m33 + m12 * m23 * m31)
            );
        };

        /**
         * @method inverse
         * @memberof Odin.Mat4
         * returns the inverse of this
         * @return this
         */
        Mat4.prototype.inverse = function() {
            var te = this.elements,

                m11 = te[0],
                m12 = te[4],
                m13 = te[8],
                m14 = te[12],
                m21 = te[1],
                m22 = te[5],
                m23 = te[9],
                m24 = te[13],
                m31 = te[2],
                m32 = te[6],
                m33 = te[10],
                m34 = te[14],
                m41 = te[3],
                m42 = te[7],
                m43 = te[11],
                m44 = te[15],

                me0 = m23 * m34 * m42 - m24 * m33 * m42 + m24 * m32 * m43 - m22 * m34 * m43 - m23 * m32 * m44 + m22 * m33 * m44,
                me4 = m14 * m33 * m42 - m13 * m34 * m42 - m14 * m32 * m43 + m12 * m34 * m43 + m13 * m32 * m44 - m12 * m33 * m44,
                me8 = m13 * m24 * m42 - m14 * m23 * m42 + m14 * m22 * m43 - m12 * m24 * m43 - m13 * m22 * m44 + m12 * m23 * m44,
                me12 = m14 * m23 * m32 - m13 * m24 * m32 - m14 * m22 * m33 + m12 * m24 * m33 + m13 * m22 * m34 - m12 * m23 * m34,

                det = m11 * me0 + m21 * me4 + m31 * me8 + m41 * me12;

            if (det === 0) return this.identity();

            te[0] = me0 * det;
            te[4] = me4 * det;
            te[8] = me8 * det;
            te[12] = me12 * det;
            te[1] = (m24 * m33 * m41 - m23 * m34 * m41 - m24 * m31 * m43 + m21 * m34 * m43 + m23 * m31 * m44 - m21 * m33 * m44) * det;
            te[5] = (m13 * m34 * m41 - m14 * m33 * m41 + m14 * m31 * m43 - m11 * m34 * m43 - m13 * m31 * m44 + m11 * m33 * m44) * det;
            te[9] = (m14 * m23 * m41 - m13 * m24 * m41 - m14 * m21 * m43 + m11 * m24 * m43 + m13 * m21 * m44 - m11 * m23 * m44) * det;
            te[13] = (m13 * m24 * m31 - m14 * m23 * m31 + m14 * m21 * m33 - m11 * m24 * m33 - m13 * m21 * m34 + m11 * m23 * m34) * det;
            te[2] = (m22 * m34 * m41 - m24 * m32 * m41 + m24 * m31 * m42 - m21 * m34 * m42 - m22 * m31 * m44 + m21 * m32 * m44) * det;
            te[6] = (m14 * m32 * m41 - m12 * m34 * m41 - m14 * m31 * m42 + m11 * m34 * m42 + m12 * m31 * m44 - m11 * m32 * m44) * det;
            te[10] = (m12 * m24 * m41 - m14 * m22 * m41 + m14 * m21 * m42 - m11 * m24 * m42 - m12 * m21 * m44 + m11 * m22 * m44) * det;
            te[14] = (m14 * m22 * m31 - m12 * m24 * m31 - m14 * m21 * m32 + m11 * m24 * m32 + m12 * m21 * m34 - m11 * m22 * m34) * det;
            te[3] = (m23 * m32 * m41 - m22 * m33 * m41 - m23 * m31 * m42 + m21 * m33 * m42 + m22 * m31 * m43 - m21 * m32 * m43) * det;
            te[7] = (m12 * m33 * m41 - m13 * m32 * m41 + m13 * m31 * m42 - m11 * m33 * m42 - m12 * m31 * m43 + m11 * m32 * m43) * det;
            te[11] = (m13 * m22 * m41 - m12 * m23 * m41 - m13 * m21 * m42 + m11 * m23 * m42 + m12 * m21 * m43 - m11 * m22 * m43) * det;
            te[15] = (m12 * m23 * m31 - m13 * m22 * m31 + m13 * m21 * m32 - m11 * m23 * m32 - m12 * m21 * m33 + m11 * m22 * m33) * det;

            return this;
        };

        /**
         * @method inverseMat
         * @memberof Odin.Mat4
         * returns the inverse of other
         * @param Mat4 other
         * @return this
         */
        Mat4.prototype.inverseMat = function(other) {
            var te = this.elements,
                me = other.elements,

                m11 = me[0],
                m12 = me[4],
                m13 = me[8],
                m14 = me[12],
                m21 = me[1],
                m22 = me[5],
                m23 = me[9],
                m24 = me[13],
                m31 = me[2],
                m32 = me[6],
                m33 = me[10],
                m34 = me[14],
                m41 = me[3],
                m42 = me[7],
                m43 = me[11],
                m44 = me[15],

                me0 = m23 * m34 * m42 - m24 * m33 * m42 + m24 * m32 * m43 - m22 * m34 * m43 - m23 * m32 * m44 + m22 * m33 * m44,
                me4 = m14 * m33 * m42 - m13 * m34 * m42 - m14 * m32 * m43 + m12 * m34 * m43 + m13 * m32 * m44 - m12 * m33 * m44,
                me8 = m13 * m24 * m42 - m14 * m23 * m42 + m14 * m22 * m43 - m12 * m24 * m43 - m13 * m22 * m44 + m12 * m23 * m44,
                me12 = m14 * m23 * m32 - m13 * m24 * m32 - m14 * m22 * m33 + m12 * m24 * m33 + m13 * m22 * m34 - m12 * m23 * m34,

                det = m11 * me0 + m21 * me4 + m31 * me8 + m41 * me12;

            if (det === 0) return this.identity();

            te[0] = me0 * det;
            te[4] = me4 * det;
            te[8] = me8 * det;
            te[12] = me12 * det;
            te[1] = (m24 * m33 * m41 - m23 * m34 * m41 - m24 * m31 * m43 + m21 * m34 * m43 + m23 * m31 * m44 - m21 * m33 * m44) * det;
            te[5] = (m13 * m34 * m41 - m14 * m33 * m41 + m14 * m31 * m43 - m11 * m34 * m43 - m13 * m31 * m44 + m11 * m33 * m44) * det;
            te[9] = (m14 * m23 * m41 - m13 * m24 * m41 - m14 * m21 * m43 + m11 * m24 * m43 + m13 * m21 * m44 - m11 * m23 * m44) * det;
            te[13] = (m13 * m24 * m31 - m14 * m23 * m31 + m14 * m21 * m33 - m11 * m24 * m33 - m13 * m21 * m34 + m11 * m23 * m34) * det;
            te[2] = (m22 * m34 * m41 - m24 * m32 * m41 + m24 * m31 * m42 - m21 * m34 * m42 - m22 * m31 * m44 + m21 * m32 * m44) * det;
            te[6] = (m14 * m32 * m41 - m12 * m34 * m41 - m14 * m31 * m42 + m11 * m34 * m42 + m12 * m31 * m44 - m11 * m32 * m44) * det;
            te[10] = (m12 * m24 * m41 - m14 * m22 * m41 + m14 * m21 * m42 - m11 * m24 * m42 - m12 * m21 * m44 + m11 * m22 * m44) * det;
            te[14] = (m14 * m22 * m31 - m12 * m24 * m31 - m14 * m21 * m32 + m11 * m24 * m32 + m12 * m21 * m34 - m11 * m22 * m34) * det;
            te[3] = (m23 * m32 * m41 - m22 * m33 * m41 - m23 * m31 * m42 + m21 * m33 * m42 + m22 * m31 * m43 - m21 * m32 * m43) * det;
            te[7] = (m12 * m33 * m41 - m13 * m32 * m41 + m13 * m31 * m42 - m11 * m33 * m42 - m12 * m31 * m43 + m11 * m32 * m43) * det;
            te[11] = (m13 * m22 * m41 - m12 * m23 * m41 - m13 * m21 * m42 + m11 * m23 * m42 + m12 * m21 * m43 - m11 * m22 * m43) * det;
            te[15] = (m12 * m23 * m31 - m13 * m22 * m31 + m13 * m21 * m32 - m11 * m23 * m32 - m12 * m21 * m33 + m11 * m22 * m33) * det;

            return this;
        };

        /**
         * @method transpose
         * @memberof Odin.Mat4
         * transposes this matrix
         * @return this
         */
        Mat4.prototype.transpose = function() {
            var te = this.elements,
                tmp;

            tmp = te[1];
            te[1] = te[4];
            te[4] = tmp;
            tmp = te[2];
            te[2] = te[8];
            te[8] = tmp;
            tmp = te[6];
            te[6] = te[9];
            te[9] = tmp;

            tmp = te[3];
            te[3] = te[12];
            te[12] = tmp;
            tmp = te[7];
            te[7] = te[13];
            te[13] = tmp;
            tmp = te[11];
            te[11] = te[14];
            te[14] = tmp;

            return this;
        };

        /**
         * @method setTrace
         * @memberof Odin.Mat4
         * sets the diagonal of matrix
         * @param Vec4 v
         * @return this
         */
        Mat4.prototype.setTrace = function(v) {
            var te = this.elements,
                w = v.w;

            te[0] = v.x;
            te[5] = v.y;
            te[10] = v.z;
            te[15] = w !== undefined ? w : 1;

            return this;
        };

        /**
         * @method lookAt
         * @memberof Odin.Mat4
         * makes matrix look from eye at target along up vector
         * @param Vec3 eye
         * @param Vec3 target
         * @param Vec3 up
         * @return this
         */
        Mat4.prototype.lookAt = function() {
            var dup = new Vec3(0, 0, 1),
                x = new Vec3,
                y = new Vec3,
                z = new Vec3;

            return function(eye, target, up) {
                var te = this.elements;

                z.vsub(eye, target).normalize();
                if (z.length() === 0) z.z = 1;

                x.vcross(up, z).normalize();

                if (x.length() === 0) {
                    z.x += 0.0001;
                    x.vcross(up, z).normalize();
                }

                y.vcross(z, x);


                te[0] = x.x;
                te[4] = y.x;
                te[8] = z.x;
                te[1] = x.y;
                te[5] = y.y;
                te[9] = z.y;
                te[2] = x.z;
                te[6] = y.z;
                te[10] = z.z;

                return this;
            };
        }();

        /**
         * @method compose
         * @memberof Odin.Mat4
         * sets matrix from position, scale, and quaternion
         * @param Vec3 position
         * @param Vec3 scale
         * @param Quat rotation
         * @return this
         */
        Mat4.prototype.compose = function(position, scale, rotation) {
            var te = this.elements,
                x = rotation.x,
                y = rotation.y,
                z = rotation.z,
                w = rotation.w,
                x2 = x + x,
                y2 = y + y,
                z2 = z + z,
                xx = x * x2,
                xy = x * y2,
                xz = x * z2,
                yy = y * y2,
                yz = y * z2,
                zz = z * z2,
                wx = w * x2,
                wy = w * y2,
                wz = w * z2,

                sx = scale.x,
                sy = scale.y,
                sz = scale.z;

            te[0] = (1 - (yy + zz)) * sx;
            te[4] = (xy - wz) * sy;
            te[8] = (xz + wy) * sz;

            te[1] = (xy + wz) * sx;
            te[5] = (1 - (xx + zz)) * sy;
            te[9] = (yz - wx) * sz;

            te[2] = (xz - wy) * sx;
            te[6] = (yz + wx) * sy;
            te[10] = (1 - (xx + yy)) * sz;

            te[3] = 0;
            te[7] = 0;
            te[11] = 0;

            te[12] = position.x;
            te[13] = position.y;
            te[14] = position.z;
            te[15] = 1;

            return this;
        };

        /**
         * @method decompose
         * @memberof Odin.Mat4
         * gets matrix position, scale, quaternion
         * @param Vec3 position
         * @param Vec3 scale
         * @param Quat quaternion
         * @return this
         */
        Mat4.prototype.decompose = function(position, scale, quaternion) {
            var te = this.elements,

                m11 = te[0],
                m12 = te[4],
                m13 = te[8],
                m21 = te[1],
                m22 = te[5],
                m23 = te[9],
                m31 = te[2],
                m32 = te[6],
                m33 = te[10],
                trace, x = 0,
                y = 0,
                z = 0,
                w = 1,
                s,

                sx = scale.set(m11, m21, m31).length(),
                sy = scale.set(m12, m22, m32).length(),
                sz = scale.set(m13, m23, m33).length(),

                invSx = 1 / sx,
                invSy = 1 / sy,
                invSz = 1 / sz;

            scale.x = sx;
            scale.y = sy;
            scale.z = sz;

            position.x = te[12];
            position.y = te[13];
            position.z = te[14];

            m11 *= invSx;
            m12 *= invSy;
            m13 *= invSz;
            m21 *= invSx;
            m22 *= invSy;
            m23 *= invSz;
            m31 *= invSx;
            m32 *= invSy;
            m33 *= invSz;

            trace = m11 + m22 + m33;

            if (trace > 0) {
                s = 0.5 / sqrt(trace + 1.0);

                w = 0.25 / s;
                x = (m32 - m23) * s;
                y = (m13 - m31) * s;
                z = (m21 - m12) * s;
            } else if (m11 > m22 && m11 > m33) {
                s = 2.0 * sqrt(1.0 + m11 - m22 - m33);

                w = (m32 - m23) / s;
                x = 0.25 * s;
                y = (m12 + m21) / s;
                z = (m13 + m31) / s;
            } else if (m22 > m33) {
                s = 2.0 * sqrt(1.0 + m22 - m11 - m33);

                w = (m13 - m31) / s;
                x = (m12 + m21) / s;
                y = 0.25 * s;
                z = (m23 + m32) / s;
            } else {
                s = 2.0 * sqrt(1.0 + m33 - m11 - m22);

                w = (m21 - m12) / s;
                x = (m13 + m31) / s;
                y = (m23 + m32) / s;
                z = 0.25 * s;
            }

            quaternion.x = x;
            quaternion.y = y;
            quaternion.w = w;
            quaternion.z = z;

            return this;
        };

        /**
         * @method setPosition
         * @memberof Odin.Mat4
         * sets position of matrix
         * @param Vec3 v
         * @return this
         */
        Mat4.prototype.setPosition = function(v) {
            var te = this.elements,
                z = v.z;

            te[12] = v.x;
            te[13] = v.y;
            te[14] = z !== undefined ? z : 0;

            return this;
        };

        /**
         * @method extractPosition
         * @memberof Odin.Mat4
         * gets position from other saves it in this
         * @param Mat4 other
         * @return this
         */
        Mat4.prototype.extractPosition = function(other) {
            var te = this.elements,
                me = other.elements;

            te[12] = me[12];
            te[13] = me[13];
            te[14] = me[14];

            return this;
        };

        /**
         * @method extractRotation
         * @memberof Odin.Mat4
         * gets rotation from other saves it in this
         * @param Mat4 other
         * @return this
         */
        Mat4.prototype.extractRotation = function() {
            var vec = new Vec3();

            return function(other) {
                var te = this.elements,
                    me = other.elements,

                    lx = vec.set(me[0], me[1], me[2]).lengthSq(),
                    ly = vec.set(me[4], me[5], me[6]).lengthSq(),
                    lz = vec.set(me[8], me[9], me[10]).lengthSq(),

                    scaleX = lx > 0 ? 1 / sqrt(lx) : 0,
                    scaleY = ly > 0 ? 1 / sqrt(ly) : 0,
                    scaleZ = lz > 0 ? 1 / sqrt(lz) : 0;

                te[0] = me[0] * scaleX;
                te[1] = me[1] * scaleX;
                te[2] = me[2] * scaleX;

                te[4] = me[4] * scaleY;
                te[5] = me[5] * scaleY;
                te[6] = me[6] * scaleY;

                te[8] = me[8] * scaleZ;
                te[9] = me[9] * scaleZ;
                te[10] = me[10] * scaleZ;

                return this;
            };
        }();

        /**
         * @method extractRotationScale
         * @memberof Odin.Mat4
         * gets rotation with scale from other saves it in this
         * @param Mat4 other
         * @return this
         */
        Mat4.prototype.extractRotationScale = function(other) {
            var te = this.elements,
                me = other.elements;

            te[0] = me[0];
            te[1] = me[1];
            te[2] = me[2];

            te[4] = me[4];
            te[5] = me[5];
            te[6] = me[6];

            te[8] = me[8];
            te[9] = me[9];
            te[10] = me[10];

            return this;
        };

        /**
         * @method translate
         * @memberof Odin.Mat4
         * translates matrix by vector
         * @param Vec3 v
         * @return this
         */
        Mat4.prototype.translate = function(v) {
            var te = this.elements,
                x = v.x,
                y = v.y,
                z = v.z || 0;

            te[12] = te[0] * x + te[4] * y + te[8] * z + te[12];
            te[13] = te[1] * x + te[5] * y + te[9] * z + te[13];
            te[14] = te[2] * x + te[6] * y + te[10] * z + te[14];
            te[15] = te[3] * x + te[7] * y + te[11] * z + te[15];

            return this;
        };

        /**
         * @method scale
         * @memberof Odin.Mat4
         * scales matrix by vector
         * @param Vec3 v
         * @return this
         */
        Mat4.prototype.scale = function(v) {
            var te = this.elements,
                x = v.x,
                y = v.y,
                z = v.z;

            te[0] *= x;
            te[4] *= y;
            te[8] *= z;
            te[1] *= x;
            te[5] *= y;
            te[9] *= z;
            te[2] *= x;
            te[6] *= y;
            te[10] *= z;
            te[3] *= x;
            te[7] *= y;
            te[11] *= z;

            return this;
        };

        /**
         * @method rotateX
         * @memberof Odin.Mat4
         * rotates matrix along x axis by angle
         * @param Number angle
         * @return this
         */
        Mat4.prototype.rotateX = function(angle) {
            var te = this.elements,
                m12 = te[4],
                m22 = te[5],
                m32 = te[6],
                m42 = te[7],
                m13 = te[8],
                m23 = te[9],
                m33 = te[10],
                m43 = te[11],
                c = cos(angle),
                s = sin(angle);

            te[4] = c * m12 + s * m13;
            te[5] = c * m22 + s * m23;
            te[6] = c * m32 + s * m33;
            te[7] = c * m42 + s * m43;

            te[8] = c * m13 - s * m12;
            te[9] = c * m23 - s * m22;
            te[10] = c * m33 - s * m32;
            te[11] = c * m43 - s * m42;

            return this;
        };

        /**
         * @method rotateY
         * @memberof Odin.Mat4
         * rotates matrix along y axis by angle
         * @param Number angle
         * @return this
         */
        Mat4.prototype.rotateY = function(angle) {
            var te = this.elements,
                m11 = te[0],
                m21 = te[1],
                m31 = te[2],
                m41 = te[3],
                m13 = te[8],
                m23 = te[9],
                m33 = te[10],
                m43 = te[11],
                c = cos(angle),
                s = sin(angle);

            te[0] = c * m11 - s * m13;
            te[1] = c * m21 - s * m23;
            te[2] = c * m31 - s * m33;
            te[3] = c * m41 - s * m43;

            te[8] = c * m13 + s * m11;
            te[9] = c * m23 + s * m21;
            te[10] = c * m33 + s * m31;
            te[11] = c * m43 + s * m41;

            return this;
        };

        /**
         * @method rotateZ
         * @memberof Odin.Mat4
         * rotates matrix along z axis by angle
         * @param Number angle
         * @return this
         */
        Mat4.prototype.rotateZ = function(angle) {
            var te = this.elements,
                m11 = te[0],
                m21 = te[1],
                m31 = te[2],
                m41 = te[3],
                m12 = te[4],
                m22 = te[5],
                m32 = te[6],
                m42 = te[7],
                c = cos(angle),
                s = sin(angle);

            te[0] = c * m11 + s * m12;
            te[1] = c * m21 + s * m22;
            te[2] = c * m31 + s * m32;
            te[3] = c * m41 + s * m42;

            te[4] = c * m12 - s * m11;
            te[5] = c * m22 - s * m21;
            te[6] = c * m32 - s * m31;
            te[7] = c * m42 - s * m41;

            return this;
        };

        /**
         * @method makeTranslation
         * @memberof Odin.Mat4
         * makes this a translation matrix
         * @param Number x
         * @param Number y
         * @param Number z
         * @return this
         */
        Mat4.prototype.makeTranslation = function(x, y, z) {

            return this.set(
                1, 0, 0, x,
                0, 1, 0, y,
                0, 0, 1, z,
                0, 0, 0, 1
            );
        };

        /**
         * @method makeScale
         * @memberof Odin.Mat4
         * makes this a scale matrix
         * @param Number x
         * @param Number y
         * @param Number z
         * @return this
         */
        Mat4.prototype.makeScale = function(x, y, z) {

            return this.set(
                x, 0, 0, 0,
                0, y, 0, 0,
                0, 0, z, 0,
                0, 0, 0, 1
            );
        };

        /**
         * @method makeRotationX
         * @memberof Odin.Mat4
         * makes this a rotation matrix along x axis
         * @param Number angle
         * @return this
         */
        Mat4.prototype.makeRotationX = function(angle) {
            var c = cos(angle),
                s = sin(angle);

            return this.set(
                1, 0, 0, 0,
                0, c, -s, 0,
                0, s, c, 0,
                0, 0, 0, 1
            );
        };

        /**
         * @method makeRotationY
         * @memberof Odin.Mat4
         * makes this a rotation matrix along y axis
         * @param Number angle
         * @return this
         */
        Mat4.prototype.makeRotationY = function(angle) {
            var c = cos(angle),
                s = sin(angle);

            return this.set(
                c, 0, s, 0,
                0, 1, 0, 0, -s, 0, c, 0,
                0, 0, 0, 1
            );
        };

        /**
         * @method makeRotationZ
         * @memberof Odin.Mat4
         * makes this a rotation matrix along z axis
         * @param Number angle
         * @return this
         */
        Mat4.prototype.makeRotationZ = function(angle) {
            var c = cos(angle),
                s = sin(angle);

            return this.set(
                c, -s, 0, 0,
                s, c, 0, 0,
                0, 0, 1, 0,
                0, 0, 0, 1
            );
        };

        /**
         * @method frustum
         * @memberof Odin.Mat4
         * makes frustum matrix
         * @param Number left
         * @param Number right
         * @param Number bottom
         * @param Number top
         * @param Number near
         * @param Number far
         * @return this
         */
        Mat4.prototype.frustum = function(left, right, top, bottom, near, far) {
            var te = this.elements,
                x = 2 * near / (right - left),
                y = 2 * near / (top - bottom),

                a = (right + left) / (right - left),
                b = (top + bottom) / (top - bottom),
                c = -(far + near) / (far - near),
                d = -2 * far * near / (far - near);

            te[0] = x;
            te[4] = 0;
            te[8] = a;
            te[12] = 0;
            te[1] = 0;
            te[5] = y;
            te[9] = b;
            te[13] = 0;
            te[2] = 0;
            te[6] = 0;
            te[10] = c;
            te[14] = d;
            te[3] = 0;
            te[7] = 0;
            te[11] = -1;
            te[15] = 0;

            return this;
        };

        /**
         * @method perspective
         * @memberof Odin.Mat4
         * makes perspective matrix
         * @param Number fov
         * @param Number aspect
         * @param Number near
         * @param Number far
         * @return this
         */
        Mat4.prototype.perspective = function(fov, aspect, near, far) {
            var ymax = near * tan(fov * 0.5),
                ymin = -ymax,
                xmin = ymin * aspect,
                xmax = ymax * aspect;

            return this.frustum(xmin, xmax, ymax, ymin, near, far);
        };

        /**
         * @method orthographic
         * @memberof Odin.Mat4
         * makes orthographic matrix
         * @param Number left
         * @param Number right
         * @param Number bottom
         * @param Number top
         * @param Number near
         * @param Number far
         * @return this
         */
        Mat4.prototype.orthographic = function(left, right, top, bottom, near, far) {
            var te = this.elements,
                w = right - left,
                h = top - bottom,
                p = far - near,

                x = (right + left) / w,
                y = (top + bottom) / h,
                z = (far + near) / p;

            te[0] = 2 / w;
            te[1] = 0;
            te[2] = 0;
            te[3] = 0;
            te[4] = 0;
            te[5] = 2 / h;
            te[6] = 0;
            te[7] = 0
            te[8] = 0;
            te[9] = 0;
            te[10] = -2 / p;
            te[11] = 0;
            te[12] = -x;
            te[13] = -y;
            te[14] = -z;
            te[15] = 1;

            return this;
        };

        /**
         * @method fromMat2
         * @memberof Odin.Mat4
         * sets this from Mat2
         * @param Mat2 m
         * @return this
         */
        Mat4.prototype.fromMat2 = function(m) {
            var te = this.elements,
                me = m.elements;

            te[0] = me[0];
            te[1] = me[1];
            te[2] = 0;
            te[3] = 0;
            te[4] = me[2];
            te[5] = me[3];
            te[6] = 0;
            te[7] = 0;
            te[8] = 0;
            te[9] = 0;
            te[10] = 1;
            te[11] = 0;
            te[12] = 0;
            te[13] = 0;
            te[14] = 0;
            te[15] = 1;

            return this;
        };

        /**
         * @method fromMat32
         * @memberof Odin.Mat4
         * sets this from Mat32
         * @param Mat32 m
         * @return this
         */
        Mat4.prototype.fromMat32 = function(m) {
            var te = this.elements,
                me = m.elements;

            te[0] = me[0];
            te[1] = me[1];
            te[2] = 0;
            te[3] = 0;
            te[4] = me[2];
            te[5] = me[3];
            te[6] = 0;
            te[7] = 0;
            te[8] = 0;
            te[9] = 0;
            te[10] = 1;
            te[11] = 0;
            te[12] = me[4];
            te[13] = me[5];
            te[14] = 0;
            te[15] = 1;

            return this;
        };

        /**
         * @method fromMat3
         * @memberof Odin.Mat4
         * sets this from Mat3
         * @param Mat3 m
         * @return this
         */
        Mat4.prototype.fromMat3 = function(m) {
            var te = this.elements,
                me = m.elements;

            te[0] = me[0];
            te[1] = me[1];
            te[2] = me[2];
            te[3] = 0;
            te[4] = me[3];
            te[5] = me[4];
            te[6] = me[5];
            te[7] = 0;
            te[8] = me[6];
            te[9] = me[7];
            te[10] = me[8];
            te[11] = 0;
            te[12] = 0;
            te[13] = 0;
            te[14] = 0;
            te[15] = 1;

            return this;
        };

        /**
         * @method fromQuat
         * @memberof Odin.Mat4
         * sets rotation of this from quaterian
         * @param Quat q
         * @return this
         */
        Mat4.prototype.fromQuat = function(q) {
            var te = this.elements,
                x = q.x,
                y = q.y,
                z = q.z,
                w = q.w,
                x2 = x + x,
                y2 = y + y,
                z2 = z + z,
                xx = x * x2,
                xy = x * y2,
                xz = x * z2,
                yy = y * y2,
                yz = y * z2,
                zz = z * z2,
                wx = w * x2,
                wy = w * y2,
                wz = w * z2;

            te[0] = 1 - (yy + zz);
            te[4] = xy - wz;
            te[8] = xz + wy;

            te[1] = xy + wz;
            te[5] = 1 - (xx + zz);
            te[9] = yz - wx;

            te[2] = xz - wy;
            te[6] = yz + wx;
            te[10] = 1 - (xx + yy);

            te[3] = 0;
            te[7] = 0;
            te[11] = 0;

            te[12] = 0;
            te[13] = 0;
            te[14] = 0;
            te[15] = 1;

            return this;
        };

        /**
         * @memberof Odin.Mat4
         * @param Odin.Mat4 other
         * @return this
         */
        Mat4.prototype.equals = function(other) {
            var ae = this.elements,
                be = other.elements;

            return !(
                ae[0] !== be[0] ||
                ae[1] !== be[1] ||
                ae[2] !== be[2] ||
                ae[3] !== be[3] ||
                ae[4] !== be[4] ||
                ae[5] !== be[5] ||
                ae[6] !== be[6] ||
                ae[7] !== be[7] ||
                ae[8] !== be[8] ||
                ae[9] !== be[9] ||
                ae[10] !== be[10] ||
                ae[11] !== be[11] ||
                ae[12] !== be[12] ||
                ae[13] !== be[13] ||
                ae[14] !== be[14] ||
                ae[15] !== be[15]
            );
        };

        /**
         * @method fromJSON
         * @memberof Odin.Mat4
         * sets values from JSON object
         * @param Object json
         * @return this
         */
        Mat4.prototype.fromJSON = function(json) {
            var te = this.elements,
                me = json.elements;

            te[0] = me[0];
            te[1] = me[1];
            te[2] = me[2];
            te[3] = me[3];
            te[4] = me[4];
            te[5] = me[5];
            te[6] = me[6];
            te[7] = me[7];
            te[8] = me[8];
            te[9] = me[9];
            te[10] = me[10];
            te[11] = me[11];
            te[12] = me[12];
            te[13] = me[13];
            te[14] = me[14];
            te[15] = me[15];

            return this;
        };

        /**
         * @method toJSON
         * @memberof Odin.Mat4
         * returns json object of this
         * @return Object
         */
        Mat4.prototype.toJSON = function(json) {
            json || (json = {});
            var te = this.elements,
                je = json.elements || (json.elements = []);

            json._className = "Mat4";
            je[0] = te[0];
            je[1] = te[1];
            je[2] = te[2];
            je[3] = te[3];
            je[4] = te[4];
            je[5] = te[5];
            je[6] = te[6];
            je[7] = te[7];
            je[8] = te[8];

            return json;
        };

        /**
         * @method toString
         * @memberof Odin.Mat4
         * returns string of this
         * @return String
         */
        Mat4.prototype.toString = function() {
            var te = this.elements;

            return (
                "Mat4[" + te[0] + ", " + te[4] + ", " + te[8] + ", " + te[12] + "]\n" +
                "     [" + te[1] + ", " + te[5] + ", " + te[9] + ", " + te[13] + "]\n" +
                "     [" + te[2] + ", " + te[6] + ", " + te[10] + ", " + te[14] + "]\n" +
                "     [" + te[3] + ", " + te[7] + ", " + te[11] + ", " + te[15] + "]"
            );
        };


        return Mat4;
    }
);