Home | History | Annotate | Download | only in resources
      1 /*
      2  * Copyright (C) 2009 Apple Inc. All Rights Reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions
      6  * are met:
      7  * 1. Redistributions of source code must retain the above copyright
      8  *    notice, this list of conditions and the following disclaimer.
      9  * 2. Redistributions in binary form must reproduce the above copyright
     10  *    notice, this list of conditions and the following disclaimer in the
     11  *    documentation and/or other materials provided with the distribution.
     12  *
     13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
     14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
     17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
     21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     24  */
     25 
     26  // J3DI (Jedi) - A support library for WebGL.
     27 
     28 /*
     29     J3DI Math Classes. Currently includes:
     30 
     31         J3DIMatrix4 - A 4x4 Matrix
     32 */
     33 
     34 /*
     35     J3DIMatrix4 class
     36 
     37     This class implements a 4x4 matrix. It has functions which duplicate the
     38     functionality of the OpenGL matrix stack and glut functions. On browsers
     39     that support it, CSSMatrix is used to accelerate operations.
     40 
     41     IDL:
     42 
     43     [
     44         Constructor(in J3DIMatrix4 matrix),                 // copy passed matrix into new J3DIMatrix4
     45         Constructor(in sequence<float> array)               // create new J3DIMatrix4 with 16 floats (row major)
     46         Constructor()                                       // create new J3DIMatrix4 with identity matrix
     47     ]
     48     interface J3DIMatrix4 {
     49         void load(in J3DIMatrix4 matrix);                   // copy the values from the passed matrix
     50         void load(in sequence<float> array);                // copy 16 floats into the matrix
     51         sequence<float> getAsArray();                       // return the matrix as an array of 16 floats
     52         Float32Array getAsFloat32Array();             // return the matrix as a Float32Array with 16 values
     53         void setUniform(in WebGLRenderingContext ctx,       // Send the matrix to the passed uniform location in the passed context
     54                         in WebGLUniformLocation loc,
     55                         in boolean transpose);
     56         void makeIdentity();                                // replace the matrix with identity
     57         void transpose();                                   // replace the matrix with its transpose
     58         void invert();                                      // replace the matrix with its inverse
     59 
     60         void translate(in float x, in float y, in float z); // multiply the matrix by passed translation values on the right
     61         void translate(in J3DVector3 v);                    // multiply the matrix by passed translation values on the right
     62         void scale(in float x, in float y, in float z);     // multiply the matrix by passed scale values on the right
     63         void scale(in J3DVector3 v);                        // multiply the matrix by passed scale values on the right
     64         void rotate(in float angle,                         // multiply the matrix by passed rotation values on the right
     65                     in float x, in float y, in float z);    // (angle is in degrees)
     66         void rotate(in float angle, in J3DVector3 v);       // multiply the matrix by passed rotation values on the right
     67                                                             // (angle is in degrees)
     68         void multiply(in CanvasMatrix matrix);              // multiply the matrix by the passed matrix on the right
     69         void divide(in float divisor);                      // divide the matrix by the passed divisor
     70         void ortho(in float left, in float right,           // multiply the matrix by the passed ortho values on the right
     71                    in float bottom, in float top,
     72                    in float near, in float far);
     73         void frustum(in float left, in float right,         // multiply the matrix by the passed frustum values on the right
     74                      in float bottom, in float top,
     75                      in float near, in float far);
     76         void perspective(in float fovy, in float aspect,    // multiply the matrix by the passed perspective values on the right
     77                          in float zNear, in float zFar);
     78         void lookat(in J3DVector3 eye,                      // multiply the matrix by the passed lookat
     79                 in J3DVector3 center,  in J3DVector3 up);   // values on the right
     80          bool decompose(in J3DVector3 translate,            // decompose the matrix into the passed vector
     81                         in J3DVector3 rotate,
     82                         in J3DVector3 scale,
     83                         in J3DVector3 skew,
     84                         in sequence<float> perspective);
     85     }
     86 
     87     [
     88         Constructor(in J3DVector3 vector),                  // copy passed vector into new J3DVector3
     89         Constructor(in sequence<float> array)               // create new J3DVector3 with 3 floats from array
     90         Constructor(in float x, in float y, in float z)     // create new J3DVector3 with 3 floats
     91         Constructor()                                       // create new J3DVector3 with (0,0,0)
     92     ]
     93     interface J3DVector3 {
     94         void load(in J3DVector3 vector);                    // copy the values from the passed vector
     95         void load(in sequence<float> array);                // copy 3 floats into the vector from array
     96         void load(in float x, in float y, in float z);      // copy 3 floats into the vector
     97         sequence<float> getAsArray();                       // return the vector as an array of 3 floats
     98         Float32Array getAsFloat32Array();             // return the matrix as a Float32Array with 16 values
     99         void multMatrix(in J3DIMatrix4 matrix);             // multiply the vector by the passed matrix (on the right)
    100         float vectorLength();                               // return the length of the vector
    101         float dot();                                        // return the dot product of the vector
    102         void cross(in J3DVector3 v);                        // replace the vector with vector x v
    103         void divide(in float divisor);                      // divide the vector by the passed divisor
    104     }
    105 */
    106 
    107 J3DIHasCSSMatrix = false;
    108 J3DIHasCSSMatrixCopy = false;
    109 /*
    110 if ("WebKitCSSMatrix" in window && ("media" in window && window.media.matchMedium("(-webkit-transform-3d)")) ||
    111                                    ("styleMedia" in window && window.styleMedia.matchMedium("(-webkit-transform-3d)"))) {
    112     J3DIHasCSSMatrix = true;
    113     if ("copy" in WebKitCSSMatrix.prototype)
    114         J3DIHasCSSMatrixCopy = true;
    115 }
    116 */
    117 
    118 //  console.log("J3DIHasCSSMatrix="+J3DIHasCSSMatrix);
    119 //  console.log("J3DIHasCSSMatrixCopy="+J3DIHasCSSMatrixCopy);
    120 
    121 //
    122 // J3DIMatrix4
    123 //
    124 J3DIMatrix4 = function(m)
    125 {
    126     if (J3DIHasCSSMatrix)
    127         this.$matrix = new WebKitCSSMatrix;
    128     else
    129         this.$matrix = new Object;
    130 
    131     if (typeof m == 'object') {
    132         if ("length" in m && m.length >= 16) {
    133             this.load(m);
    134             return;
    135         }
    136         else if (m instanceof J3DIMatrix4) {
    137             this.load(m);
    138             return;
    139         }
    140     }
    141     this.makeIdentity();
    142 }
    143 
    144 J3DIMatrix4.prototype.load = function()
    145 {
    146     if (arguments.length == 1 && typeof arguments[0] == 'object') {
    147         var matrix;
    148 
    149         if (arguments[0] instanceof J3DIMatrix4) {
    150             matrix = arguments[0].$matrix;
    151 
    152             this.$matrix.m11 = matrix.m11;
    153             this.$matrix.m12 = matrix.m12;
    154             this.$matrix.m13 = matrix.m13;
    155             this.$matrix.m14 = matrix.m14;
    156 
    157             this.$matrix.m21 = matrix.m21;
    158             this.$matrix.m22 = matrix.m22;
    159             this.$matrix.m23 = matrix.m23;
    160             this.$matrix.m24 = matrix.m24;
    161 
    162             this.$matrix.m31 = matrix.m31;
    163             this.$matrix.m32 = matrix.m32;
    164             this.$matrix.m33 = matrix.m33;
    165             this.$matrix.m34 = matrix.m34;
    166 
    167             this.$matrix.m41 = matrix.m41;
    168             this.$matrix.m42 = matrix.m42;
    169             this.$matrix.m43 = matrix.m43;
    170             this.$matrix.m44 = matrix.m44;
    171             return;
    172         }
    173         else
    174             matrix = arguments[0];
    175 
    176         if ("length" in matrix && matrix.length >= 16) {
    177             this.$matrix.m11 = matrix[0];
    178             this.$matrix.m12 = matrix[1];
    179             this.$matrix.m13 = matrix[2];
    180             this.$matrix.m14 = matrix[3];
    181 
    182             this.$matrix.m21 = matrix[4];
    183             this.$matrix.m22 = matrix[5];
    184             this.$matrix.m23 = matrix[6];
    185             this.$matrix.m24 = matrix[7];
    186 
    187             this.$matrix.m31 = matrix[8];
    188             this.$matrix.m32 = matrix[9];
    189             this.$matrix.m33 = matrix[10];
    190             this.$matrix.m34 = matrix[11];
    191 
    192             this.$matrix.m41 = matrix[12];
    193             this.$matrix.m42 = matrix[13];
    194             this.$matrix.m43 = matrix[14];
    195             this.$matrix.m44 = matrix[15];
    196             return;
    197         }
    198     }
    199 
    200     this.makeIdentity();
    201 }
    202 
    203 J3DIMatrix4.prototype.getAsArray = function()
    204 {
    205     return [
    206         this.$matrix.m11, this.$matrix.m12, this.$matrix.m13, this.$matrix.m14,
    207         this.$matrix.m21, this.$matrix.m22, this.$matrix.m23, this.$matrix.m24,
    208         this.$matrix.m31, this.$matrix.m32, this.$matrix.m33, this.$matrix.m34,
    209         this.$matrix.m41, this.$matrix.m42, this.$matrix.m43, this.$matrix.m44
    210     ];
    211 }
    212 
    213 J3DIMatrix4.prototype.getAsFloat32Array = function()
    214 {
    215     if (J3DIHasCSSMatrixCopy) {
    216         var array = new Float32Array(16);
    217         this.$matrix.copy(array);
    218         return array;
    219     }
    220     return new Float32Array(this.getAsArray());
    221 }
    222 
    223 J3DIMatrix4.prototype.setUniform = function(ctx, loc, transpose)
    224 {
    225     if (J3DIMatrix4.setUniformArray == undefined) {
    226         J3DIMatrix4.setUniformWebGLArray = new Float32Array(16);
    227         J3DIMatrix4.setUniformArray = new Array(16);
    228     }
    229 
    230     if (J3DIHasCSSMatrixCopy)
    231         this.$matrix.copy(J3DIMatrix4.setUniformWebGLArray);
    232     else {
    233         J3DIMatrix4.setUniformArray[0] = this.$matrix.m11;
    234         J3DIMatrix4.setUniformArray[1] = this.$matrix.m12;
    235         J3DIMatrix4.setUniformArray[2] = this.$matrix.m13;
    236         J3DIMatrix4.setUniformArray[3] = this.$matrix.m14;
    237         J3DIMatrix4.setUniformArray[4] = this.$matrix.m21;
    238         J3DIMatrix4.setUniformArray[5] = this.$matrix.m22;
    239         J3DIMatrix4.setUniformArray[6] = this.$matrix.m23;
    240         J3DIMatrix4.setUniformArray[7] = this.$matrix.m24;
    241         J3DIMatrix4.setUniformArray[8] = this.$matrix.m31;
    242         J3DIMatrix4.setUniformArray[9] = this.$matrix.m32;
    243         J3DIMatrix4.setUniformArray[10] = this.$matrix.m33;
    244         J3DIMatrix4.setUniformArray[11] = this.$matrix.m34;
    245         J3DIMatrix4.setUniformArray[12] = this.$matrix.m41;
    246         J3DIMatrix4.setUniformArray[13] = this.$matrix.m42;
    247         J3DIMatrix4.setUniformArray[14] = this.$matrix.m43;
    248         J3DIMatrix4.setUniformArray[15] = this.$matrix.m44;
    249 
    250         J3DIMatrix4.setUniformWebGLArray.set(J3DIMatrix4.setUniformArray);
    251     }
    252 
    253     ctx.uniformMatrix4fv(loc, transpose, J3DIMatrix4.setUniformWebGLArray);
    254 }
    255 
    256 J3DIMatrix4.prototype.makeIdentity = function()
    257 {
    258     this.$matrix.m11 = 1;
    259     this.$matrix.m12 = 0;
    260     this.$matrix.m13 = 0;
    261     this.$matrix.m14 = 0;
    262 
    263     this.$matrix.m21 = 0;
    264     this.$matrix.m22 = 1;
    265     this.$matrix.m23 = 0;
    266     this.$matrix.m24 = 0;
    267 
    268     this.$matrix.m31 = 0;
    269     this.$matrix.m32 = 0;
    270     this.$matrix.m33 = 1;
    271     this.$matrix.m34 = 0;
    272 
    273     this.$matrix.m41 = 0;
    274     this.$matrix.m42 = 0;
    275     this.$matrix.m43 = 0;
    276     this.$matrix.m44 = 1;
    277 }
    278 
    279 J3DIMatrix4.prototype.transpose = function()
    280 {
    281     var tmp = this.$matrix.m12;
    282     this.$matrix.m12 = this.$matrix.m21;
    283     this.$matrix.m21 = tmp;
    284 
    285     tmp = this.$matrix.m13;
    286     this.$matrix.m13 = this.$matrix.m31;
    287     this.$matrix.m31 = tmp;
    288 
    289     tmp = this.$matrix.m14;
    290     this.$matrix.m14 = this.$matrix.m41;
    291     this.$matrix.m41 = tmp;
    292 
    293     tmp = this.$matrix.m23;
    294     this.$matrix.m23 = this.$matrix.m32;
    295     this.$matrix.m32 = tmp;
    296 
    297     tmp = this.$matrix.m24;
    298     this.$matrix.m24 = this.$matrix.m42;
    299     this.$matrix.m42 = tmp;
    300 
    301     tmp = this.$matrix.m34;
    302     this.$matrix.m34 = this.$matrix.m43;
    303     this.$matrix.m43 = tmp;
    304 }
    305 
    306 J3DIMatrix4.prototype.invert = function()
    307 {
    308     if (J3DIHasCSSMatrix) {
    309         this.$matrix = this.$matrix.inverse();
    310         return;
    311     }
    312 
    313     // Calculate the 4x4 determinant
    314     // If the determinant is zero,
    315     // then the inverse matrix is not unique.
    316     var det = this._determinant4x4();
    317 
    318     if (Math.abs(det) < 1e-8)
    319         return null;
    320 
    321     this._makeAdjoint();
    322 
    323     // Scale the adjoint matrix to get the inverse
    324     this.$matrix.m11 /= det;
    325     this.$matrix.m12 /= det;
    326     this.$matrix.m13 /= det;
    327     this.$matrix.m14 /= det;
    328 
    329     this.$matrix.m21 /= det;
    330     this.$matrix.m22 /= det;
    331     this.$matrix.m23 /= det;
    332     this.$matrix.m24 /= det;
    333 
    334     this.$matrix.m31 /= det;
    335     this.$matrix.m32 /= det;
    336     this.$matrix.m33 /= det;
    337     this.$matrix.m34 /= det;
    338 
    339     this.$matrix.m41 /= det;
    340     this.$matrix.m42 /= det;
    341     this.$matrix.m43 /= det;
    342     this.$matrix.m44 /= det;
    343 }
    344 
    345 J3DIMatrix4.prototype.translate = function(x,y,z)
    346 {
    347     if (typeof x == 'object' && "length" in x) {
    348         var t = x;
    349         x = t[0];
    350         y = t[1];
    351         z = t[2];
    352     }
    353     else {
    354         if (x == undefined)
    355             x = 0;
    356         if (y == undefined)
    357             y = 0;
    358         if (z == undefined)
    359             z = 0;
    360     }
    361 
    362     if (J3DIHasCSSMatrix) {
    363         this.$matrix = this.$matrix.translate(x, y, z);
    364         return;
    365     }
    366 
    367     var matrix = new J3DIMatrix4();
    368     matrix.$matrix.m41 = x;
    369     matrix.$matrix.m42 = y;
    370     matrix.$matrix.m43 = z;
    371 
    372     this.multiply(matrix);
    373 }
    374 
    375 J3DIMatrix4.prototype.scale = function(x,y,z)
    376 {
    377     if (typeof x == 'object' && "length" in x) {
    378         var t = x;
    379         x = t[0];
    380         y = t[1];
    381         z = t[2];
    382     }
    383     else {
    384         if (x == undefined)
    385             x = 1;
    386         if (z == undefined) {
    387             if (y == undefined) {
    388                 y = x;
    389                 z = x;
    390             }
    391             else
    392                 z = 1;
    393         }
    394         else if (y == undefined)
    395             y = x;
    396     }
    397 
    398     if (J3DIHasCSSMatrix) {
    399         this.$matrix = this.$matrix.scale(x, y, z);
    400         return;
    401     }
    402 
    403     var matrix = new J3DIMatrix4();
    404     matrix.$matrix.m11 = x;
    405     matrix.$matrix.m22 = y;
    406     matrix.$matrix.m33 = z;
    407 
    408     this.multiply(matrix);
    409 }
    410 
    411 J3DIMatrix4.prototype.rotate = function(angle,x,y,z)
    412 {
    413     // Forms are (angle, x,y,z), (angle,vector), (angleX, angleY, angleZ), (angle)
    414     if (typeof x == 'object' && "length" in x) {
    415         var t = x;
    416         x = t[0];
    417         y = t[1];
    418         z = t[2];
    419     }
    420     else {
    421         if (arguments.length == 1) {
    422             x = 0;
    423             y = 0;
    424             z = 1;
    425         }
    426         else if (arguments.length == 3) {
    427             this.rotate(angle, 1,0,0); // about X axis
    428             this.rotate(x, 0,1,0); // about Y axis
    429             this.rotate(y, 0,0,1); // about Z axis
    430             return;
    431         }
    432     }
    433 
    434     if (J3DIHasCSSMatrix) {
    435         this.$matrix = this.$matrix.rotateAxisAngle(x, y, z, angle);
    436         return;
    437     }
    438 
    439     // angles are in degrees. Switch to radians
    440     angle = angle / 180 * Math.PI;
    441 
    442     angle /= 2;
    443     var sinA = Math.sin(angle);
    444     var cosA = Math.cos(angle);
    445     var sinA2 = sinA * sinA;
    446 
    447     // normalize
    448     var len = Math.sqrt(x * x + y * y + z * z);
    449     if (len == 0) {
    450         // bad vector, just use something reasonable
    451         x = 0;
    452         y = 0;
    453         z = 1;
    454     } else if (len != 1) {
    455         x /= len;
    456         y /= len;
    457         z /= len;
    458     }
    459 
    460     var mat = new J3DIMatrix4();
    461 
    462     // optimize case where axis is along major axis
    463     if (x == 1 && y == 0 && z == 0) {
    464         mat.$matrix.m11 = 1;
    465         mat.$matrix.m12 = 0;
    466         mat.$matrix.m13 = 0;
    467         mat.$matrix.m21 = 0;
    468         mat.$matrix.m22 = 1 - 2 * sinA2;
    469         mat.$matrix.m23 = 2 * sinA * cosA;
    470         mat.$matrix.m31 = 0;
    471         mat.$matrix.m32 = -2 * sinA * cosA;
    472         mat.$matrix.m33 = 1 - 2 * sinA2;
    473         mat.$matrix.m14 = mat.$matrix.m24 = mat.$matrix.m34 = 0;
    474         mat.$matrix.m41 = mat.$matrix.m42 = mat.$matrix.m43 = 0;
    475         mat.$matrix.m44 = 1;
    476     } else if (x == 0 && y == 1 && z == 0) {
    477         mat.$matrix.m11 = 1 - 2 * sinA2;
    478         mat.$matrix.m12 = 0;
    479         mat.$matrix.m13 = -2 * sinA * cosA;
    480         mat.$matrix.m21 = 0;
    481         mat.$matrix.m22 = 1;
    482         mat.$matrix.m23 = 0;
    483         mat.$matrix.m31 = 2 * sinA * cosA;
    484         mat.$matrix.m32 = 0;
    485         mat.$matrix.m33 = 1 - 2 * sinA2;
    486         mat.$matrix.m14 = mat.$matrix.m24 = mat.$matrix.m34 = 0;
    487         mat.$matrix.m41 = mat.$matrix.m42 = mat.$matrix.m43 = 0;
    488         mat.$matrix.m44 = 1;
    489     } else if (x == 0 && y == 0 && z == 1) {
    490         mat.$matrix.m11 = 1 - 2 * sinA2;
    491         mat.$matrix.m12 = 2 * sinA * cosA;
    492         mat.$matrix.m13 = 0;
    493         mat.$matrix.m21 = -2 * sinA * cosA;
    494         mat.$matrix.m22 = 1 - 2 * sinA2;
    495         mat.$matrix.m23 = 0;
    496         mat.$matrix.m31 = 0;
    497         mat.$matrix.m32 = 0;
    498         mat.$matrix.m33 = 1;
    499         mat.$matrix.m14 = mat.$matrix.m24 = mat.$matrix.m34 = 0;
    500         mat.$matrix.m41 = mat.$matrix.m42 = mat.$matrix.m43 = 0;
    501         mat.$matrix.m44 = 1;
    502     } else {
    503         var x2 = x*x;
    504         var y2 = y*y;
    505         var z2 = z*z;
    506 
    507         mat.$matrix.m11 = 1 - 2 * (y2 + z2) * sinA2;
    508         mat.$matrix.m12 = 2 * (x * y * sinA2 + z * sinA * cosA);
    509         mat.$matrix.m13 = 2 * (x * z * sinA2 - y * sinA * cosA);
    510         mat.$matrix.m21 = 2 * (y * x * sinA2 - z * sinA * cosA);
    511         mat.$matrix.m22 = 1 - 2 * (z2 + x2) * sinA2;
    512         mat.$matrix.m23 = 2 * (y * z * sinA2 + x * sinA * cosA);
    513         mat.$matrix.m31 = 2 * (z * x * sinA2 + y * sinA * cosA);
    514         mat.$matrix.m32 = 2 * (z * y * sinA2 - x * sinA * cosA);
    515         mat.$matrix.m33 = 1 - 2 * (x2 + y2) * sinA2;
    516         mat.$matrix.m14 = mat.$matrix.m24 = mat.$matrix.m34 = 0;
    517         mat.$matrix.m41 = mat.$matrix.m42 = mat.$matrix.m43 = 0;
    518         mat.$matrix.m44 = 1;
    519     }
    520     this.multiply(mat);
    521 }
    522 
    523 J3DIMatrix4.prototype.multiply = function(mat)
    524 {
    525     if (J3DIHasCSSMatrix) {
    526         this.$matrix = this.$matrix.multiply(mat.$matrix);
    527         return;
    528     }
    529 
    530     var m11 = (mat.$matrix.m11 * this.$matrix.m11 + mat.$matrix.m12 * this.$matrix.m21
    531                + mat.$matrix.m13 * this.$matrix.m31 + mat.$matrix.m14 * this.$matrix.m41);
    532     var m12 = (mat.$matrix.m11 * this.$matrix.m12 + mat.$matrix.m12 * this.$matrix.m22
    533                + mat.$matrix.m13 * this.$matrix.m32 + mat.$matrix.m14 * this.$matrix.m42);
    534     var m13 = (mat.$matrix.m11 * this.$matrix.m13 + mat.$matrix.m12 * this.$matrix.m23
    535                + mat.$matrix.m13 * this.$matrix.m33 + mat.$matrix.m14 * this.$matrix.m43);
    536     var m14 = (mat.$matrix.m11 * this.$matrix.m14 + mat.$matrix.m12 * this.$matrix.m24
    537                + mat.$matrix.m13 * this.$matrix.m34 + mat.$matrix.m14 * this.$matrix.m44);
    538 
    539     var m21 = (mat.$matrix.m21 * this.$matrix.m11 + mat.$matrix.m22 * this.$matrix.m21
    540                + mat.$matrix.m23 * this.$matrix.m31 + mat.$matrix.m24 * this.$matrix.m41);
    541     var m22 = (mat.$matrix.m21 * this.$matrix.m12 + mat.$matrix.m22 * this.$matrix.m22
    542                + mat.$matrix.m23 * this.$matrix.m32 + mat.$matrix.m24 * this.$matrix.m42);
    543     var m23 = (mat.$matrix.m21 * this.$matrix.m13 + mat.$matrix.m22 * this.$matrix.m23
    544                + mat.$matrix.m23 * this.$matrix.m33 + mat.$matrix.m24 * this.$matrix.m43);
    545     var m24 = (mat.$matrix.m21 * this.$matrix.m14 + mat.$matrix.m22 * this.$matrix.m24
    546                + mat.$matrix.m23 * this.$matrix.m34 + mat.$matrix.m24 * this.$matrix.m44);
    547 
    548     var m31 = (mat.$matrix.m31 * this.$matrix.m11 + mat.$matrix.m32 * this.$matrix.m21
    549                + mat.$matrix.m33 * this.$matrix.m31 + mat.$matrix.m34 * this.$matrix.m41);
    550     var m32 = (mat.$matrix.m31 * this.$matrix.m12 + mat.$matrix.m32 * this.$matrix.m22
    551                + mat.$matrix.m33 * this.$matrix.m32 + mat.$matrix.m34 * this.$matrix.m42);
    552     var m33 = (mat.$matrix.m31 * this.$matrix.m13 + mat.$matrix.m32 * this.$matrix.m23
    553                + mat.$matrix.m33 * this.$matrix.m33 + mat.$matrix.m34 * this.$matrix.m43);
    554     var m34 = (mat.$matrix.m31 * this.$matrix.m14 + mat.$matrix.m32 * this.$matrix.m24
    555                + mat.$matrix.m33 * this.$matrix.m34 + mat.$matrix.m34 * this.$matrix.m44);
    556 
    557     var m41 = (mat.$matrix.m41 * this.$matrix.m11 + mat.$matrix.m42 * this.$matrix.m21
    558                + mat.$matrix.m43 * this.$matrix.m31 + mat.$matrix.m44 * this.$matrix.m41);
    559     var m42 = (mat.$matrix.m41 * this.$matrix.m12 + mat.$matrix.m42 * this.$matrix.m22
    560                + mat.$matrix.m43 * this.$matrix.m32 + mat.$matrix.m44 * this.$matrix.m42);
    561     var m43 = (mat.$matrix.m41 * this.$matrix.m13 + mat.$matrix.m42 * this.$matrix.m23
    562                + mat.$matrix.m43 * this.$matrix.m33 + mat.$matrix.m44 * this.$matrix.m43);
    563     var m44 = (mat.$matrix.m41 * this.$matrix.m14 + mat.$matrix.m42 * this.$matrix.m24
    564                + mat.$matrix.m43 * this.$matrix.m34 + mat.$matrix.m44 * this.$matrix.m44);
    565 
    566     this.$matrix.m11 = m11;
    567     this.$matrix.m12 = m12;
    568     this.$matrix.m13 = m13;
    569     this.$matrix.m14 = m14;
    570 
    571     this.$matrix.m21 = m21;
    572     this.$matrix.m22 = m22;
    573     this.$matrix.m23 = m23;
    574     this.$matrix.m24 = m24;
    575 
    576     this.$matrix.m31 = m31;
    577     this.$matrix.m32 = m32;
    578     this.$matrix.m33 = m33;
    579     this.$matrix.m34 = m34;
    580 
    581     this.$matrix.m41 = m41;
    582     this.$matrix.m42 = m42;
    583     this.$matrix.m43 = m43;
    584     this.$matrix.m44 = m44;
    585 }
    586 
    587 J3DIMatrix4.prototype.divide = function(divisor)
    588 {
    589     this.$matrix.m11 /= divisor;
    590     this.$matrix.m12 /= divisor;
    591     this.$matrix.m13 /= divisor;
    592     this.$matrix.m14 /= divisor;
    593 
    594     this.$matrix.m21 /= divisor;
    595     this.$matrix.m22 /= divisor;
    596     this.$matrix.m23 /= divisor;
    597     this.$matrix.m24 /= divisor;
    598 
    599     this.$matrix.m31 /= divisor;
    600     this.$matrix.m32 /= divisor;
    601     this.$matrix.m33 /= divisor;
    602     this.$matrix.m34 /= divisor;
    603 
    604     this.$matrix.m41 /= divisor;
    605     this.$matrix.m42 /= divisor;
    606     this.$matrix.m43 /= divisor;
    607     this.$matrix.m44 /= divisor;
    608 
    609 }
    610 
    611 J3DIMatrix4.prototype.ortho = function(left, right, bottom, top, near, far)
    612 {
    613     var tx = (left + right) / (left - right);
    614     var ty = (top + bottom) / (top - bottom);
    615     var tz = (far + near) / (far - near);
    616 
    617     var matrix = new J3DIMatrix4();
    618     matrix.$matrix.m11 = 2 / (left - right);
    619     matrix.$matrix.m12 = 0;
    620     matrix.$matrix.m13 = 0;
    621     matrix.$matrix.m14 = 0;
    622     matrix.$matrix.m21 = 0;
    623     matrix.$matrix.m22 = 2 / (top - bottom);
    624     matrix.$matrix.m23 = 0;
    625     matrix.$matrix.m24 = 0;
    626     matrix.$matrix.m31 = 0;
    627     matrix.$matrix.m32 = 0;
    628     matrix.$matrix.m33 = -2 / (far - near);
    629     matrix.$matrix.m34 = 0;
    630     matrix.$matrix.m41 = tx;
    631     matrix.$matrix.m42 = ty;
    632     matrix.$matrix.m43 = tz;
    633     matrix.$matrix.m44 = 1;
    634 
    635     this.multiply(matrix);
    636 }
    637 
    638 J3DIMatrix4.prototype.frustum = function(left, right, bottom, top, near, far)
    639 {
    640     var matrix = new J3DIMatrix4();
    641     var A = (right + left) / (right - left);
    642     var B = (top + bottom) / (top - bottom);
    643     var C = -(far + near) / (far - near);
    644     var D = -(2 * far * near) / (far - near);
    645 
    646     matrix.$matrix.m11 = (2 * near) / (right - left);
    647     matrix.$matrix.m12 = 0;
    648     matrix.$matrix.m13 = 0;
    649     matrix.$matrix.m14 = 0;
    650 
    651     matrix.$matrix.m21 = 0;
    652     matrix.$matrix.m22 = 2 * near / (top - bottom);
    653     matrix.$matrix.m23 = 0;
    654     matrix.$matrix.m24 = 0;
    655 
    656     matrix.$matrix.m31 = A;
    657     matrix.$matrix.m32 = B;
    658     matrix.$matrix.m33 = C;
    659     matrix.$matrix.m34 = -1;
    660 
    661     matrix.$matrix.m41 = 0;
    662     matrix.$matrix.m42 = 0;
    663     matrix.$matrix.m43 = D;
    664     matrix.$matrix.m44 = 0;
    665 
    666     this.multiply(matrix);
    667 }
    668 
    669 J3DIMatrix4.prototype.perspective = function(fovy, aspect, zNear, zFar)
    670 {
    671     var top = Math.tan(fovy * Math.PI / 360) * zNear;
    672     var bottom = -top;
    673     var left = aspect * bottom;
    674     var right = aspect * top;
    675     this.frustum(left, right, bottom, top, zNear, zFar);
    676 }
    677 
    678 J3DIMatrix4.prototype.lookat = function(eyex, eyey, eyez, centerx, centery, centerz, upx, upy, upz)
    679 {
    680     if (typeof eyez == 'object' && "length" in eyez) {
    681         var t = eyez;
    682         upx = t[0];
    683         upy = t[1];
    684         upz = t[2];
    685 
    686         t = eyey;
    687         centerx = t[0];
    688         centery = t[1];
    689         centerz = t[2];
    690 
    691         t = eyex;
    692         eyex = t[0];
    693         eyey = t[1];
    694         eyez = t[2];
    695     }
    696 
    697     var matrix = new J3DIMatrix4();
    698 
    699     // Make rotation matrix
    700 
    701     // Z vector
    702     var zx = eyex - centerx;
    703     var zy = eyey - centery;
    704     var zz = eyez - centerz;
    705     var mag = Math.sqrt(zx * zx + zy * zy + zz * zz);
    706     if (mag) {
    707         zx /= mag;
    708         zy /= mag;
    709         zz /= mag;
    710     }
    711 
    712     // Y vector
    713     var yx = upx;
    714     var yy = upy;
    715     var yz = upz;
    716 
    717     // X vector = Y cross Z
    718     xx =  yy * zz - yz * zy;
    719     xy = -yx * zz + yz * zx;
    720     xz =  yx * zy - yy * zx;
    721 
    722     // Recompute Y = Z cross X
    723     yx = zy * xz - zz * xy;
    724     yy = -zx * xz + zz * xx;
    725     yx = zx * xy - zy * xx;
    726 
    727     // cross product gives area of parallelogram, which is < 1.0 for
    728     // non-perpendicular unit-length vectors; so normalize x, y here
    729 
    730     mag = Math.sqrt(xx * xx + xy * xy + xz * xz);
    731     if (mag) {
    732         xx /= mag;
    733         xy /= mag;
    734         xz /= mag;
    735     }
    736 
    737     mag = Math.sqrt(yx * yx + yy * yy + yz * yz);
    738     if (mag) {
    739         yx /= mag;
    740         yy /= mag;
    741         yz /= mag;
    742     }
    743 
    744     matrix.$matrix.m11 = xx;
    745     matrix.$matrix.m12 = xy;
    746     matrix.$matrix.m13 = xz;
    747     matrix.$matrix.m14 = 0;
    748 
    749     matrix.$matrix.m21 = yx;
    750     matrix.$matrix.m22 = yy;
    751     matrix.$matrix.m23 = yz;
    752     matrix.$matrix.m24 = 0;
    753 
    754     matrix.$matrix.m31 = zx;
    755     matrix.$matrix.m32 = zy;
    756     matrix.$matrix.m33 = zz;
    757     matrix.$matrix.m34 = 0;
    758 
    759     matrix.$matrix.m41 = 0;
    760     matrix.$matrix.m42 = 0;
    761     matrix.$matrix.m43 = 0;
    762     matrix.$matrix.m44 = 1;
    763     matrix.translate(-eyex, -eyey, -eyez);
    764 
    765     this.multiply(matrix);
    766 }
    767 
    768 // Returns true on success, false otherwise. All params are Array objects
    769 J3DIMatrix4.prototype.decompose = function(_translate, _rotate, _scale, _skew, _perspective)
    770 {
    771     // Normalize the matrix.
    772     if (this.$matrix.m44 == 0)
    773         return false;
    774 
    775     // Gather the params
    776     var translate, rotate, scale, skew, perspective;
    777 
    778     var translate = (_translate == undefined || !("length" in _translate)) ? new J3DIVector3 : _translate;
    779     var rotate = (_rotate == undefined || !("length" in _rotate)) ? new J3DIVector3 : _rotate;
    780     var scale = (_scale == undefined || !("length" in _scale)) ? new J3DIVector3 : _scale;
    781     var skew = (_skew == undefined || !("length" in _skew)) ? new J3DIVector3 : _skew;
    782     var perspective = (_perspective == undefined || !("length" in _perspective)) ? new Array(4) : _perspective;
    783 
    784     var matrix = new J3DIMatrix4(this);
    785 
    786     matrix.divide(matrix.$matrix.m44);
    787 
    788     // perspectiveMatrix is used to solve for perspective, but it also provides
    789     // an easy way to test for singularity of the upper 3x3 component.
    790     var perspectiveMatrix = new J3DIMatrix4(matrix);
    791 
    792     perspectiveMatrix.$matrix.m14 = 0;
    793     perspectiveMatrix.$matrix.m24 = 0;
    794     perspectiveMatrix.$matrix.m34 = 0;
    795     perspectiveMatrix.$matrix.m44 = 1;
    796 
    797     if (perspectiveMatrix._determinant4x4() == 0)
    798         return false;
    799 
    800     // First, isolate perspective.
    801     if (matrix.$matrix.m14 != 0 || matrix.$matrix.m24 != 0 || matrix.$matrix.m34 != 0) {
    802         // rightHandSide is the right hand side of the equation.
    803         var rightHandSide = [ matrix.$matrix.m14, matrix.$matrix.m24, matrix.$matrix.m34, matrix.$matrix.m44 ];
    804 
    805         // Solve the equation by inverting perspectiveMatrix and multiplying
    806         // rightHandSide by the inverse.
    807         var inversePerspectiveMatrix = new J3DIMatrix4(perspectiveMatrix);
    808         inversePerspectiveMatrix.invert();
    809         var transposedInversePerspectiveMatrix = new J3DIMatrix4(inversePerspectiveMatrix);
    810         transposedInversePerspectiveMatrix.transpose();
    811         transposedInversePerspectiveMatrix.multVecMatrix(perspective, rightHandSide);
    812 
    813         // Clear the perspective partition
    814         matrix.$matrix.m14 = matrix.$matrix.m24 = matrix.$matrix.m34 = 0
    815         matrix.$matrix.m44 = 1;
    816     }
    817     else {
    818         // No perspective.
    819         perspective[0] = perspective[1] = perspective[2] = 0;
    820         perspective[3] = 1;
    821     }
    822 
    823     // Next take care of translation
    824     translate[0] = matrix.$matrix.m41
    825     matrix.$matrix.m41 = 0
    826     translate[1] = matrix.$matrix.m42
    827     matrix.$matrix.m42 = 0
    828     translate[2] = matrix.$matrix.m43
    829     matrix.$matrix.m43 = 0
    830 
    831     // Now get scale and shear. 'row' is a 3 element array of 3 component vectors
    832     var row0 = new J3DIVector3(matrix.$matrix.m11, matrix.$matrix.m12, matrix.$matrix.m13);
    833     var row1 = new J3DIVector3(matrix.$matrix.m21, matrix.$matrix.m22, matrix.$matrix.m23);
    834     var row2 = new J3DIVector3(matrix.$matrix.m31, matrix.$matrix.m32, matrix.$matrix.m33);
    835 
    836     // Compute X scale factor and normalize first row.
    837     scale[0] = row0.vectorLength();
    838     row0.divide(scale[0]);
    839 
    840     // Compute XY shear factor and make 2nd row orthogonal to 1st.
    841     skew[0] = row0.dot(row1);
    842     row1.combine(row0, 1.0, -skew[0]);
    843 
    844     // Now, compute Y scale and normalize 2nd row.
    845     scale[1] = row1.vectorLength();
    846     row1.divide(scale[1]);
    847     skew[0] /= scale[1];
    848 
    849     // Compute XZ and YZ shears, orthogonalize 3rd row
    850     skew[1] = row1.dot(row2);
    851     row2.combine(row0, 1.0, -skew[1]);
    852     skew[2] = row1.dot(row2);
    853     row2.combine(row1, 1.0, -skew[2]);
    854 
    855     // Next, get Z scale and normalize 3rd row.
    856     scale[2] = row2.vectorLength();
    857     row2.divide(scale[2]);
    858     skew[1] /= scale[2];
    859     skew[2] /= scale[2];
    860 
    861     // At this point, the matrix (in rows) is orthonormal.
    862     // Check for a coordinate system flip.  If the determinant
    863     // is -1, then negate the matrix and the scaling factors.
    864     var pdum3 = new J3DIVector3(row1);
    865     pdum3.cross(row2);
    866     if (row0.dot(pdum3) < 0) {
    867         for (i = 0; i < 3; i++) {
    868             scale[i] *= -1;
    869             row[0][i] *= -1;
    870             row[1][i] *= -1;
    871             row[2][i] *= -1;
    872         }
    873     }
    874 
    875     // Now, get the rotations out
    876     rotate[1] = Math.asin(-row0[2]);
    877     if (Math.cos(rotate[1]) != 0) {
    878         rotate[0] = Math.atan2(row1[2], row2[2]);
    879         rotate[2] = Math.atan2(row0[1], row0[0]);
    880     }
    881     else {
    882         rotate[0] = Math.atan2(-row2[0], row1[1]);
    883         rotate[2] = 0;
    884     }
    885 
    886     // Convert rotations to degrees
    887     var rad2deg = 180 / Math.PI;
    888     rotate[0] *= rad2deg;
    889     rotate[1] *= rad2deg;
    890     rotate[2] *= rad2deg;
    891 
    892     return true;
    893 }
    894 
    895 J3DIMatrix4.prototype._determinant2x2 = function(a, b, c, d)
    896 {
    897     return a * d - b * c;
    898 }
    899 
    900 J3DIMatrix4.prototype._determinant3x3 = function(a1, a2, a3, b1, b2, b3, c1, c2, c3)
    901 {
    902     return a1 * this._determinant2x2(b2, b3, c2, c3)
    903          - b1 * this._determinant2x2(a2, a3, c2, c3)
    904          + c1 * this._determinant2x2(a2, a3, b2, b3);
    905 }
    906 
    907 J3DIMatrix4.prototype._determinant4x4 = function()
    908 {
    909     var a1 = this.$matrix.m11;
    910     var b1 = this.$matrix.m12;
    911     var c1 = this.$matrix.m13;
    912     var d1 = this.$matrix.m14;
    913 
    914     var a2 = this.$matrix.m21;
    915     var b2 = this.$matrix.m22;
    916     var c2 = this.$matrix.m23;
    917     var d2 = this.$matrix.m24;
    918 
    919     var a3 = this.$matrix.m31;
    920     var b3 = this.$matrix.m32;
    921     var c3 = this.$matrix.m33;
    922     var d3 = this.$matrix.m34;
    923 
    924     var a4 = this.$matrix.m41;
    925     var b4 = this.$matrix.m42;
    926     var c4 = this.$matrix.m43;
    927     var d4 = this.$matrix.m44;
    928 
    929     return a1 * this._determinant3x3(b2, b3, b4, c2, c3, c4, d2, d3, d4)
    930          - b1 * this._determinant3x3(a2, a3, a4, c2, c3, c4, d2, d3, d4)
    931          + c1 * this._determinant3x3(a2, a3, a4, b2, b3, b4, d2, d3, d4)
    932          - d1 * this._determinant3x3(a2, a3, a4, b2, b3, b4, c2, c3, c4);
    933 }
    934 
    935 J3DIMatrix4.prototype._makeAdjoint = function()
    936 {
    937     var a1 = this.$matrix.m11;
    938     var b1 = this.$matrix.m12;
    939     var c1 = this.$matrix.m13;
    940     var d1 = this.$matrix.m14;
    941 
    942     var a2 = this.$matrix.m21;
    943     var b2 = this.$matrix.m22;
    944     var c2 = this.$matrix.m23;
    945     var d2 = this.$matrix.m24;
    946 
    947     var a3 = this.$matrix.m31;
    948     var b3 = this.$matrix.m32;
    949     var c3 = this.$matrix.m33;
    950     var d3 = this.$matrix.m34;
    951 
    952     var a4 = this.$matrix.m41;
    953     var b4 = this.$matrix.m42;
    954     var c4 = this.$matrix.m43;
    955     var d4 = this.$matrix.m44;
    956 
    957     // Row column labeling reversed since we transpose rows & columns
    958     this.$matrix.m11  =   this._determinant3x3(b2, b3, b4, c2, c3, c4, d2, d3, d4);
    959     this.$matrix.m21  = - this._determinant3x3(a2, a3, a4, c2, c3, c4, d2, d3, d4);
    960     this.$matrix.m31  =   this._determinant3x3(a2, a3, a4, b2, b3, b4, d2, d3, d4);
    961     this.$matrix.m41  = - this._determinant3x3(a2, a3, a4, b2, b3, b4, c2, c3, c4);
    962 
    963     this.$matrix.m12  = - this._determinant3x3(b1, b3, b4, c1, c3, c4, d1, d3, d4);
    964     this.$matrix.m22  =   this._determinant3x3(a1, a3, a4, c1, c3, c4, d1, d3, d4);
    965     this.$matrix.m32  = - this._determinant3x3(a1, a3, a4, b1, b3, b4, d1, d3, d4);
    966     this.$matrix.m42  =   this._determinant3x3(a1, a3, a4, b1, b3, b4, c1, c3, c4);
    967 
    968     this.$matrix.m13  =   this._determinant3x3(b1, b2, b4, c1, c2, c4, d1, d2, d4);
    969     this.$matrix.m23  = - this._determinant3x3(a1, a2, a4, c1, c2, c4, d1, d2, d4);
    970     this.$matrix.m33  =   this._determinant3x3(a1, a2, a4, b1, b2, b4, d1, d2, d4);
    971     this.$matrix.m43  = - this._determinant3x3(a1, a2, a4, b1, b2, b4, c1, c2, c4);
    972 
    973     this.$matrix.m14  = - this._determinant3x3(b1, b2, b3, c1, c2, c3, d1, d2, d3);
    974     this.$matrix.m24  =   this._determinant3x3(a1, a2, a3, c1, c2, c3, d1, d2, d3);
    975     this.$matrix.m34  = - this._determinant3x3(a1, a2, a3, b1, b2, b3, d1, d2, d3);
    976     this.$matrix.m44  =   this._determinant3x3(a1, a2, a3, b1, b2, b3, c1, c2, c3);
    977 }
    978 
    979 //
    980 // J3DIVector3
    981 //
    982 J3DIVector3 = function(x,y,z)
    983 {
    984     this.load(x,y,z);
    985 }
    986 
    987 J3DIVector3.prototype.load = function(x,y,z)
    988 {
    989     if (typeof x == 'object' && "length" in x) {
    990         this[0] = x[0];
    991         this[1] = x[1];
    992         this[2] = x[2];
    993     }
    994     else if (typeof x == 'number') {
    995         this[0] = x;
    996         this[1] = y;
    997         this[2] = z;
    998     }
    999     else {
   1000         this[0] = 0;
   1001         this[1] = 0;
   1002         this[2] = 0;
   1003     }
   1004 }
   1005 
   1006 J3DIVector3.prototype.getAsArray = function()
   1007 {
   1008     return [ this[0], this[1], this[2] ];
   1009 }
   1010 
   1011 J3DIVector3.prototype.getAsFloat32Array = function()
   1012 {
   1013     return new Float32Array(this.getAsArray());
   1014 }
   1015 
   1016 J3DIVector3.prototype.vectorLength = function()
   1017 {
   1018     return Math.sqrt(this[0] * this[0] + this[1] * this[1] + this[2] * this[2]);
   1019 }
   1020 
   1021 J3DIVector3.prototype.divide = function(divisor)
   1022 {
   1023     this[0] /= divisor; this[1] /= divisor; this[2] /= divisor;
   1024 }
   1025 
   1026 J3DIVector3.prototype.cross = function(v)
   1027 {
   1028     this[0] =  this[1] * v[2] - this[2] * v[1];
   1029     this[1] = -this[0] * v[2] + this[2] * v[0];
   1030     this[2] =  this[0] * v[1] - this[1] * v[0];
   1031 }
   1032 
   1033 J3DIVector3.prototype.dot = function(v)
   1034 {
   1035     return this[0] * v[0] + this[1] * v[1] + this[2] * v[2];
   1036 }
   1037 
   1038 J3DIVector3.prototype.combine = function(v, ascl, bscl)
   1039 {
   1040     this[0] = (ascl * this[0]) + (bscl * v[0]);
   1041     this[1] = (ascl * this[1]) + (bscl * v[1]);
   1042     this[2] = (ascl * this[2]) + (bscl * v[2]);
   1043 }
   1044 
   1045 J3DIVector3.prototype.multVecMatrix = function(matrix)
   1046 {
   1047     var x = this[0];
   1048     var y = this[1];
   1049     var z = this[2];
   1050 
   1051     this[0] = matrix.$matrix.m41 + x * matrix.$matrix.m11 + y * matrix.$matrix.m21 + z * matrix.$matrix.m31;
   1052     this[1] = matrix.$matrix.m42 + x * matrix.$matrix.m12 + y * matrix.$matrix.m22 + z * matrix.$matrix.m32;
   1053     this[2] = matrix.$matrix.m43 + x * matrix.$matrix.m13 + y * matrix.$matrix.m23 + z * matrix.$matrix.m33;
   1054     var w = matrix.$matrix.m44 + x * matrix.$matrix.m14 + y * matrix.$matrix.m24 + z * matrix.$matrix.m34;
   1055     if (w != 1 && w != 0) {
   1056         this[0] /= w;
   1057         this[1] /= w;
   1058         this[2] /= w;
   1059     }
   1060 }
   1061 
   1062 J3DIVector3.prototype.toString = function()
   1063 {
   1064     return "["+this[0]+","+this[1]+","+this[2]+"]";
   1065 }
   1066