/* * Copyright (C) 2009 Apple Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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. */ // J3DI (Jedi) - A support library for WebGL. /* J3DI Math Classes. Currently includes: J3DIMatrix4 - A 4x4 Matrix */ /* J3DIMatrix4 class This class implements a 4x4 matrix. It has functions which duplicate the functionality of the OpenGL matrix stack and glut functions. On browsers that support it, CSSMatrix is used to accelerate operations. IDL: [ Constructor(in J3DIMatrix4 matrix), // copy passed matrix into new J3DIMatrix4 Constructor(in sequence array) // create new J3DIMatrix4 with 16 floats (row major) Constructor() // create new J3DIMatrix4 with identity matrix ] interface J3DIMatrix4 { void load(in J3DIMatrix4 matrix); // copy the values from the passed matrix void load(in sequence array); // copy 16 floats into the matrix sequence getAsArray(); // return the matrix as an array of 16 floats Float32Array getAsFloat32Array(); // return the matrix as a Float32Array with 16 values void setUniform(in WebGLRenderingContext ctx, // Send the matrix to the passed uniform location in the passed context in WebGLUniformLocation loc, in boolean transpose); void makeIdentity(); // replace the matrix with identity void transpose(); // replace the matrix with its transpose void invert(); // replace the matrix with its inverse void translate(in float x, in float y, in float z); // multiply the matrix by passed translation values on the right void translate(in J3DVector3 v); // multiply the matrix by passed translation values on the right void scale(in float x, in float y, in float z); // multiply the matrix by passed scale values on the right void scale(in J3DVector3 v); // multiply the matrix by passed scale values on the right void rotate(in float angle, // multiply the matrix by passed rotation values on the right in float x, in float y, in float z); // (angle is in degrees) void rotate(in float angle, in J3DVector3 v); // multiply the matrix by passed rotation values on the right // (angle is in degrees) void multiply(in CanvasMatrix matrix); // multiply the matrix by the passed matrix on the right void divide(in float divisor); // divide the matrix by the passed divisor void ortho(in float left, in float right, // multiply the matrix by the passed ortho values on the right in float bottom, in float top, in float near, in float far); void frustum(in float left, in float right, // multiply the matrix by the passed frustum values on the right in float bottom, in float top, in float near, in float far); void perspective(in float fovy, in float aspect, // multiply the matrix by the passed perspective values on the right in float zNear, in float zFar); void lookat(in J3DVector3 eye, // multiply the matrix by the passed lookat in J3DVector3 center, in J3DVector3 up); // values on the right bool decompose(in J3DVector3 translate, // decompose the matrix into the passed vector in J3DVector3 rotate, in J3DVector3 scale, in J3DVector3 skew, in sequence perspective); } [ Constructor(in J3DVector3 vector), // copy passed vector into new J3DVector3 Constructor(in sequence array) // create new J3DVector3 with 3 floats from array Constructor(in float x, in float y, in float z) // create new J3DVector3 with 3 floats Constructor() // create new J3DVector3 with (0,0,0) ] interface J3DVector3 { void load(in J3DVector3 vector); // copy the values from the passed vector void load(in sequence array); // copy 3 floats into the vector from array void load(in float x, in float y, in float z); // copy 3 floats into the vector sequence getAsArray(); // return the vector as an array of 3 floats Float32Array getAsFloat32Array(); // return the matrix as a Float32Array with 16 values void multMatrix(in J3DIMatrix4 matrix); // multiply the vector by the passed matrix (on the right) float vectorLength(); // return the length of the vector float dot(); // return the dot product of the vector void cross(in J3DVector3 v); // replace the vector with vector x v void divide(in float divisor); // divide the vector by the passed divisor } */ J3DIHasCSSMatrix = false; J3DIHasCSSMatrixCopy = false; /* if ("WebKitCSSMatrix" in window && ("media" in window && window.media.matchMedium("(-webkit-transform-3d)")) || ("styleMedia" in window && window.styleMedia.matchMedium("(-webkit-transform-3d)"))) { J3DIHasCSSMatrix = true; if ("copy" in WebKitCSSMatrix.prototype) J3DIHasCSSMatrixCopy = true; } */ // console.log("J3DIHasCSSMatrix="+J3DIHasCSSMatrix); // console.log("J3DIHasCSSMatrixCopy="+J3DIHasCSSMatrixCopy); // // J3DIMatrix4 // J3DIMatrix4 = function(m) { if (J3DIHasCSSMatrix) this.$matrix = new WebKitCSSMatrix; else this.$matrix = new Object; if (typeof m == 'object') { if ("length" in m && m.length >= 16) { this.load(m); return; } else if (m instanceof J3DIMatrix4) { this.load(m); return; } } this.makeIdentity(); } J3DIMatrix4.prototype.load = function() { if (arguments.length == 1 && typeof arguments[0] == 'object') { var matrix; if (arguments[0] instanceof J3DIMatrix4) { matrix = arguments[0].$matrix; this.$matrix.m11 = matrix.m11; this.$matrix.m12 = matrix.m12; this.$matrix.m13 = matrix.m13; this.$matrix.m14 = matrix.m14; this.$matrix.m21 = matrix.m21; this.$matrix.m22 = matrix.m22; this.$matrix.m23 = matrix.m23; this.$matrix.m24 = matrix.m24; this.$matrix.m31 = matrix.m31; this.$matrix.m32 = matrix.m32; this.$matrix.m33 = matrix.m33; this.$matrix.m34 = matrix.m34; this.$matrix.m41 = matrix.m41; this.$matrix.m42 = matrix.m42; this.$matrix.m43 = matrix.m43; this.$matrix.m44 = matrix.m44; return; } else matrix = arguments[0]; if ("length" in matrix && matrix.length >= 16) { this.$matrix.m11 = matrix[0]; this.$matrix.m12 = matrix[1]; this.$matrix.m13 = matrix[2]; this.$matrix.m14 = matrix[3]; this.$matrix.m21 = matrix[4]; this.$matrix.m22 = matrix[5]; this.$matrix.m23 = matrix[6]; this.$matrix.m24 = matrix[7]; this.$matrix.m31 = matrix[8]; this.$matrix.m32 = matrix[9]; this.$matrix.m33 = matrix[10]; this.$matrix.m34 = matrix[11]; this.$matrix.m41 = matrix[12]; this.$matrix.m42 = matrix[13]; this.$matrix.m43 = matrix[14]; this.$matrix.m44 = matrix[15]; return; } } this.makeIdentity(); } J3DIMatrix4.prototype.getAsArray = function() { return [ this.$matrix.m11, this.$matrix.m12, this.$matrix.m13, this.$matrix.m14, this.$matrix.m21, this.$matrix.m22, this.$matrix.m23, this.$matrix.m24, this.$matrix.m31, this.$matrix.m32, this.$matrix.m33, this.$matrix.m34, this.$matrix.m41, this.$matrix.m42, this.$matrix.m43, this.$matrix.m44 ]; } J3DIMatrix4.prototype.getAsFloat32Array = function() { if (J3DIHasCSSMatrixCopy) { var array = new Float32Array(16); this.$matrix.copy(array); return array; } return new Float32Array(this.getAsArray()); } J3DIMatrix4.prototype.setUniform = function(ctx, loc, transpose) { if (J3DIMatrix4.setUniformArray == undefined) { J3DIMatrix4.setUniformWebGLArray = new Float32Array(16); J3DIMatrix4.setUniformArray = new Array(16); } if (J3DIHasCSSMatrixCopy) this.$matrix.copy(J3DIMatrix4.setUniformWebGLArray); else { J3DIMatrix4.setUniformArray[0] = this.$matrix.m11; J3DIMatrix4.setUniformArray[1] = this.$matrix.m12; J3DIMatrix4.setUniformArray[2] = this.$matrix.m13; J3DIMatrix4.setUniformArray[3] = this.$matrix.m14; J3DIMatrix4.setUniformArray[4] = this.$matrix.m21; J3DIMatrix4.setUniformArray[5] = this.$matrix.m22; J3DIMatrix4.setUniformArray[6] = this.$matrix.m23; J3DIMatrix4.setUniformArray[7] = this.$matrix.m24; J3DIMatrix4.setUniformArray[8] = this.$matrix.m31; J3DIMatrix4.setUniformArray[9] = this.$matrix.m32; J3DIMatrix4.setUniformArray[10] = this.$matrix.m33; J3DIMatrix4.setUniformArray[11] = this.$matrix.m34; J3DIMatrix4.setUniformArray[12] = this.$matrix.m41; J3DIMatrix4.setUniformArray[13] = this.$matrix.m42; J3DIMatrix4.setUniformArray[14] = this.$matrix.m43; J3DIMatrix4.setUniformArray[15] = this.$matrix.m44; J3DIMatrix4.setUniformWebGLArray.set(J3DIMatrix4.setUniformArray); } ctx.uniformMatrix4fv(loc, transpose, J3DIMatrix4.setUniformWebGLArray); } J3DIMatrix4.prototype.makeIdentity = function() { this.$matrix.m11 = 1; this.$matrix.m12 = 0; this.$matrix.m13 = 0; this.$matrix.m14 = 0; this.$matrix.m21 = 0; this.$matrix.m22 = 1; this.$matrix.m23 = 0; this.$matrix.m24 = 0; this.$matrix.m31 = 0; this.$matrix.m32 = 0; this.$matrix.m33 = 1; this.$matrix.m34 = 0; this.$matrix.m41 = 0; this.$matrix.m42 = 0; this.$matrix.m43 = 0; this.$matrix.m44 = 1; } J3DIMatrix4.prototype.transpose = function() { var tmp = this.$matrix.m12; this.$matrix.m12 = this.$matrix.m21; this.$matrix.m21 = tmp; tmp = this.$matrix.m13; this.$matrix.m13 = this.$matrix.m31; this.$matrix.m31 = tmp; tmp = this.$matrix.m14; this.$matrix.m14 = this.$matrix.m41; this.$matrix.m41 = tmp; tmp = this.$matrix.m23; this.$matrix.m23 = this.$matrix.m32; this.$matrix.m32 = tmp; tmp = this.$matrix.m24; this.$matrix.m24 = this.$matrix.m42; this.$matrix.m42 = tmp; tmp = this.$matrix.m34; this.$matrix.m34 = this.$matrix.m43; this.$matrix.m43 = tmp; } J3DIMatrix4.prototype.invert = function() { if (J3DIHasCSSMatrix) { this.$matrix = this.$matrix.inverse(); return; } // Calculate the 4x4 determinant // If the determinant is zero, // then the inverse matrix is not unique. var det = this._determinant4x4(); if (Math.abs(det) < 1e-8) return null; this._makeAdjoint(); // Scale the adjoint matrix to get the inverse this.$matrix.m11 /= det; this.$matrix.m12 /= det; this.$matrix.m13 /= det; this.$matrix.m14 /= det; this.$matrix.m21 /= det; this.$matrix.m22 /= det; this.$matrix.m23 /= det; this.$matrix.m24 /= det; this.$matrix.m31 /= det; this.$matrix.m32 /= det; this.$matrix.m33 /= det; this.$matrix.m34 /= det; this.$matrix.m41 /= det; this.$matrix.m42 /= det; this.$matrix.m43 /= det; this.$matrix.m44 /= det; } J3DIMatrix4.prototype.translate = function(x,y,z) { if (typeof x == 'object' && "length" in x) { var t = x; x = t[0]; y = t[1]; z = t[2]; } else { if (x == undefined) x = 0; if (y == undefined) y = 0; if (z == undefined) z = 0; } if (J3DIHasCSSMatrix) { this.$matrix = this.$matrix.translate(x, y, z); return; } var matrix = new J3DIMatrix4(); matrix.$matrix.m41 = x; matrix.$matrix.m42 = y; matrix.$matrix.m43 = z; this.multiply(matrix); } J3DIMatrix4.prototype.scale = function(x,y,z) { if (typeof x == 'object' && "length" in x) { var t = x; x = t[0]; y = t[1]; z = t[2]; } else { if (x == undefined) x = 1; if (z == undefined) { if (y == undefined) { y = x; z = x; } else z = 1; } else if (y == undefined) y = x; } if (J3DIHasCSSMatrix) { this.$matrix = this.$matrix.scale(x, y, z); return; } var matrix = new J3DIMatrix4(); matrix.$matrix.m11 = x; matrix.$matrix.m22 = y; matrix.$matrix.m33 = z; this.multiply(matrix); } J3DIMatrix4.prototype.rotate = function(angle,x,y,z) { // Forms are (angle, x,y,z), (angle,vector), (angleX, angleY, angleZ), (angle) if (typeof x == 'object' && "length" in x) { var t = x; x = t[0]; y = t[1]; z = t[2]; } else { if (arguments.length == 1) { x = 0; y = 0; z = 1; } else if (arguments.length == 3) { this.rotate(angle, 1,0,0); // about X axis this.rotate(x, 0,1,0); // about Y axis this.rotate(y, 0,0,1); // about Z axis return; } } if (J3DIHasCSSMatrix) { this.$matrix = this.$matrix.rotateAxisAngle(x, y, z, angle); return; } // angles are in degrees. Switch to radians angle = angle / 180 * Math.PI; angle /= 2; var sinA = Math.sin(angle); var cosA = Math.cos(angle); var sinA2 = sinA * sinA; // normalize var len = Math.sqrt(x * x + y * y + z * z); if (len == 0) { // bad vector, just use something reasonable x = 0; y = 0; z = 1; } else if (len != 1) { x /= len; y /= len; z /= len; } var mat = new J3DIMatrix4(); // optimize case where axis is along major axis if (x == 1 && y == 0 && z == 0) { mat.$matrix.m11 = 1; mat.$matrix.m12 = 0; mat.$matrix.m13 = 0; mat.$matrix.m21 = 0; mat.$matrix.m22 = 1 - 2 * sinA2; mat.$matrix.m23 = 2 * sinA * cosA; mat.$matrix.m31 = 0; mat.$matrix.m32 = -2 * sinA * cosA; mat.$matrix.m33 = 1 - 2 * sinA2; mat.$matrix.m14 = mat.$matrix.m24 = mat.$matrix.m34 = 0; mat.$matrix.m41 = mat.$matrix.m42 = mat.$matrix.m43 = 0; mat.$matrix.m44 = 1; } else if (x == 0 && y == 1 && z == 0) { mat.$matrix.m11 = 1 - 2 * sinA2; mat.$matrix.m12 = 0; mat.$matrix.m13 = -2 * sinA * cosA; mat.$matrix.m21 = 0; mat.$matrix.m22 = 1; mat.$matrix.m23 = 0; mat.$matrix.m31 = 2 * sinA * cosA; mat.$matrix.m32 = 0; mat.$matrix.m33 = 1 - 2 * sinA2; mat.$matrix.m14 = mat.$matrix.m24 = mat.$matrix.m34 = 0; mat.$matrix.m41 = mat.$matrix.m42 = mat.$matrix.m43 = 0; mat.$matrix.m44 = 1; } else if (x == 0 && y == 0 && z == 1) { mat.$matrix.m11 = 1 - 2 * sinA2; mat.$matrix.m12 = 2 * sinA * cosA; mat.$matrix.m13 = 0; mat.$matrix.m21 = -2 * sinA * cosA; mat.$matrix.m22 = 1 - 2 * sinA2; mat.$matrix.m23 = 0; mat.$matrix.m31 = 0; mat.$matrix.m32 = 0; mat.$matrix.m33 = 1; mat.$matrix.m14 = mat.$matrix.m24 = mat.$matrix.m34 = 0; mat.$matrix.m41 = mat.$matrix.m42 = mat.$matrix.m43 = 0; mat.$matrix.m44 = 1; } else { var x2 = x*x; var y2 = y*y; var z2 = z*z; mat.$matrix.m11 = 1 - 2 * (y2 + z2) * sinA2; mat.$matrix.m12 = 2 * (x * y * sinA2 + z * sinA * cosA); mat.$matrix.m13 = 2 * (x * z * sinA2 - y * sinA * cosA); mat.$matrix.m21 = 2 * (y * x * sinA2 - z * sinA * cosA); mat.$matrix.m22 = 1 - 2 * (z2 + x2) * sinA2; mat.$matrix.m23 = 2 * (y * z * sinA2 + x * sinA * cosA); mat.$matrix.m31 = 2 * (z * x * sinA2 + y * sinA * cosA); mat.$matrix.m32 = 2 * (z * y * sinA2 - x * sinA * cosA); mat.$matrix.m33 = 1 - 2 * (x2 + y2) * sinA2; mat.$matrix.m14 = mat.$matrix.m24 = mat.$matrix.m34 = 0; mat.$matrix.m41 = mat.$matrix.m42 = mat.$matrix.m43 = 0; mat.$matrix.m44 = 1; } this.multiply(mat); } J3DIMatrix4.prototype.multiply = function(mat) { if (J3DIHasCSSMatrix) { this.$matrix = this.$matrix.multiply(mat.$matrix); return; } var m11 = (mat.$matrix.m11 * this.$matrix.m11 + mat.$matrix.m12 * this.$matrix.m21 + mat.$matrix.m13 * this.$matrix.m31 + mat.$matrix.m14 * this.$matrix.m41); var m12 = (mat.$matrix.m11 * this.$matrix.m12 + mat.$matrix.m12 * this.$matrix.m22 + mat.$matrix.m13 * this.$matrix.m32 + mat.$matrix.m14 * this.$matrix.m42); var m13 = (mat.$matrix.m11 * this.$matrix.m13 + mat.$matrix.m12 * this.$matrix.m23 + mat.$matrix.m13 * this.$matrix.m33 + mat.$matrix.m14 * this.$matrix.m43); var m14 = (mat.$matrix.m11 * this.$matrix.m14 + mat.$matrix.m12 * this.$matrix.m24 + mat.$matrix.m13 * this.$matrix.m34 + mat.$matrix.m14 * this.$matrix.m44); var m21 = (mat.$matrix.m21 * this.$matrix.m11 + mat.$matrix.m22 * this.$matrix.m21 + mat.$matrix.m23 * this.$matrix.m31 + mat.$matrix.m24 * this.$matrix.m41); var m22 = (mat.$matrix.m21 * this.$matrix.m12 + mat.$matrix.m22 * this.$matrix.m22 + mat.$matrix.m23 * this.$matrix.m32 + mat.$matrix.m24 * this.$matrix.m42); var m23 = (mat.$matrix.m21 * this.$matrix.m13 + mat.$matrix.m22 * this.$matrix.m23 + mat.$matrix.m23 * this.$matrix.m33 + mat.$matrix.m24 * this.$matrix.m43); var m24 = (mat.$matrix.m21 * this.$matrix.m14 + mat.$matrix.m22 * this.$matrix.m24 + mat.$matrix.m23 * this.$matrix.m34 + mat.$matrix.m24 * this.$matrix.m44); var m31 = (mat.$matrix.m31 * this.$matrix.m11 + mat.$matrix.m32 * this.$matrix.m21 + mat.$matrix.m33 * this.$matrix.m31 + mat.$matrix.m34 * this.$matrix.m41); var m32 = (mat.$matrix.m31 * this.$matrix.m12 + mat.$matrix.m32 * this.$matrix.m22 + mat.$matrix.m33 * this.$matrix.m32 + mat.$matrix.m34 * this.$matrix.m42); var m33 = (mat.$matrix.m31 * this.$matrix.m13 + mat.$matrix.m32 * this.$matrix.m23 + mat.$matrix.m33 * this.$matrix.m33 + mat.$matrix.m34 * this.$matrix.m43); var m34 = (mat.$matrix.m31 * this.$matrix.m14 + mat.$matrix.m32 * this.$matrix.m24 + mat.$matrix.m33 * this.$matrix.m34 + mat.$matrix.m34 * this.$matrix.m44); var m41 = (mat.$matrix.m41 * this.$matrix.m11 + mat.$matrix.m42 * this.$matrix.m21 + mat.$matrix.m43 * this.$matrix.m31 + mat.$matrix.m44 * this.$matrix.m41); var m42 = (mat.$matrix.m41 * this.$matrix.m12 + mat.$matrix.m42 * this.$matrix.m22 + mat.$matrix.m43 * this.$matrix.m32 + mat.$matrix.m44 * this.$matrix.m42); var m43 = (mat.$matrix.m41 * this.$matrix.m13 + mat.$matrix.m42 * this.$matrix.m23 + mat.$matrix.m43 * this.$matrix.m33 + mat.$matrix.m44 * this.$matrix.m43); var m44 = (mat.$matrix.m41 * this.$matrix.m14 + mat.$matrix.m42 * this.$matrix.m24 + mat.$matrix.m43 * this.$matrix.m34 + mat.$matrix.m44 * this.$matrix.m44); this.$matrix.m11 = m11; this.$matrix.m12 = m12; this.$matrix.m13 = m13; this.$matrix.m14 = m14; this.$matrix.m21 = m21; this.$matrix.m22 = m22; this.$matrix.m23 = m23; this.$matrix.m24 = m24; this.$matrix.m31 = m31; this.$matrix.m32 = m32; this.$matrix.m33 = m33; this.$matrix.m34 = m34; this.$matrix.m41 = m41; this.$matrix.m42 = m42; this.$matrix.m43 = m43; this.$matrix.m44 = m44; } J3DIMatrix4.prototype.divide = function(divisor) { this.$matrix.m11 /= divisor; this.$matrix.m12 /= divisor; this.$matrix.m13 /= divisor; this.$matrix.m14 /= divisor; this.$matrix.m21 /= divisor; this.$matrix.m22 /= divisor; this.$matrix.m23 /= divisor; this.$matrix.m24 /= divisor; this.$matrix.m31 /= divisor; this.$matrix.m32 /= divisor; this.$matrix.m33 /= divisor; this.$matrix.m34 /= divisor; this.$matrix.m41 /= divisor; this.$matrix.m42 /= divisor; this.$matrix.m43 /= divisor; this.$matrix.m44 /= divisor; } J3DIMatrix4.prototype.ortho = function(left, right, bottom, top, near, far) { var tx = (left + right) / (left - right); var ty = (top + bottom) / (top - bottom); var tz = (far + near) / (far - near); var matrix = new J3DIMatrix4(); matrix.$matrix.m11 = 2 / (left - right); matrix.$matrix.m12 = 0; matrix.$matrix.m13 = 0; matrix.$matrix.m14 = 0; matrix.$matrix.m21 = 0; matrix.$matrix.m22 = 2 / (top - bottom); matrix.$matrix.m23 = 0; matrix.$matrix.m24 = 0; matrix.$matrix.m31 = 0; matrix.$matrix.m32 = 0; matrix.$matrix.m33 = -2 / (far - near); matrix.$matrix.m34 = 0; matrix.$matrix.m41 = tx; matrix.$matrix.m42 = ty; matrix.$matrix.m43 = tz; matrix.$matrix.m44 = 1; this.multiply(matrix); } J3DIMatrix4.prototype.frustum = function(left, right, bottom, top, near, far) { var matrix = new J3DIMatrix4(); var A = (right + left) / (right - left); var B = (top + bottom) / (top - bottom); var C = -(far + near) / (far - near); var D = -(2 * far * near) / (far - near); matrix.$matrix.m11 = (2 * near) / (right - left); matrix.$matrix.m12 = 0; matrix.$matrix.m13 = 0; matrix.$matrix.m14 = 0; matrix.$matrix.m21 = 0; matrix.$matrix.m22 = 2 * near / (top - bottom); matrix.$matrix.m23 = 0; matrix.$matrix.m24 = 0; matrix.$matrix.m31 = A; matrix.$matrix.m32 = B; matrix.$matrix.m33 = C; matrix.$matrix.m34 = -1; matrix.$matrix.m41 = 0; matrix.$matrix.m42 = 0; matrix.$matrix.m43 = D; matrix.$matrix.m44 = 0; this.multiply(matrix); } J3DIMatrix4.prototype.perspective = function(fovy, aspect, zNear, zFar) { var top = Math.tan(fovy * Math.PI / 360) * zNear; var bottom = -top; var left = aspect * bottom; var right = aspect * top; this.frustum(left, right, bottom, top, zNear, zFar); } J3DIMatrix4.prototype.lookat = function(eyex, eyey, eyez, centerx, centery, centerz, upx, upy, upz) { if (typeof eyez == 'object' && "length" in eyez) { var t = eyez; upx = t[0]; upy = t[1]; upz = t[2]; t = eyey; centerx = t[0]; centery = t[1]; centerz = t[2]; t = eyex; eyex = t[0]; eyey = t[1]; eyez = t[2]; } var matrix = new J3DIMatrix4(); // Make rotation matrix // Z vector var zx = eyex - centerx; var zy = eyey - centery; var zz = eyez - centerz; var mag = Math.sqrt(zx * zx + zy * zy + zz * zz); if (mag) { zx /= mag; zy /= mag; zz /= mag; } // Y vector var yx = upx; var yy = upy; var yz = upz; // X vector = Y cross Z xx = yy * zz - yz * zy; xy = -yx * zz + yz * zx; xz = yx * zy - yy * zx; // Recompute Y = Z cross X yx = zy * xz - zz * xy; yy = -zx * xz + zz * xx; yx = zx * xy - zy * xx; // cross product gives area of parallelogram, which is < 1.0 for // non-perpendicular unit-length vectors; so normalize x, y here mag = Math.sqrt(xx * xx + xy * xy + xz * xz); if (mag) { xx /= mag; xy /= mag; xz /= mag; } mag = Math.sqrt(yx * yx + yy * yy + yz * yz); if (mag) { yx /= mag; yy /= mag; yz /= mag; } matrix.$matrix.m11 = xx; matrix.$matrix.m12 = xy; matrix.$matrix.m13 = xz; matrix.$matrix.m14 = 0; matrix.$matrix.m21 = yx; matrix.$matrix.m22 = yy; matrix.$matrix.m23 = yz; matrix.$matrix.m24 = 0; matrix.$matrix.m31 = zx; matrix.$matrix.m32 = zy; matrix.$matrix.m33 = zz; matrix.$matrix.m34 = 0; matrix.$matrix.m41 = 0; matrix.$matrix.m42 = 0; matrix.$matrix.m43 = 0; matrix.$matrix.m44 = 1; matrix.translate(-eyex, -eyey, -eyez); this.multiply(matrix); } // Returns true on success, false otherwise. All params are Array objects J3DIMatrix4.prototype.decompose = function(_translate, _rotate, _scale, _skew, _perspective) { // Normalize the matrix. if (this.$matrix.m44 == 0) return false; // Gather the params var translate, rotate, scale, skew, perspective; var translate = (_translate == undefined || !("length" in _translate)) ? new J3DIVector3 : _translate; var rotate = (_rotate == undefined || !("length" in _rotate)) ? new J3DIVector3 : _rotate; var scale = (_scale == undefined || !("length" in _scale)) ? new J3DIVector3 : _scale; var skew = (_skew == undefined || !("length" in _skew)) ? new J3DIVector3 : _skew; var perspective = (_perspective == undefined || !("length" in _perspective)) ? new Array(4) : _perspective; var matrix = new J3DIMatrix4(this); matrix.divide(matrix.$matrix.m44); // perspectiveMatrix is used to solve for perspective, but it also provides // an easy way to test for singularity of the upper 3x3 component. var perspectiveMatrix = new J3DIMatrix4(matrix); perspectiveMatrix.$matrix.m14 = 0; perspectiveMatrix.$matrix.m24 = 0; perspectiveMatrix.$matrix.m34 = 0; perspectiveMatrix.$matrix.m44 = 1; if (perspectiveMatrix._determinant4x4() == 0) return false; // First, isolate perspective. if (matrix.$matrix.m14 != 0 || matrix.$matrix.m24 != 0 || matrix.$matrix.m34 != 0) { // rightHandSide is the right hand side of the equation. var rightHandSide = [ matrix.$matrix.m14, matrix.$matrix.m24, matrix.$matrix.m34, matrix.$matrix.m44 ]; // Solve the equation by inverting perspectiveMatrix and multiplying // rightHandSide by the inverse. var inversePerspectiveMatrix = new J3DIMatrix4(perspectiveMatrix); inversePerspectiveMatrix.invert(); var transposedInversePerspectiveMatrix = new J3DIMatrix4(inversePerspectiveMatrix); transposedInversePerspectiveMatrix.transpose(); transposedInversePerspectiveMatrix.multVecMatrix(perspective, rightHandSide); // Clear the perspective partition matrix.$matrix.m14 = matrix.$matrix.m24 = matrix.$matrix.m34 = 0 matrix.$matrix.m44 = 1; } else { // No perspective. perspective[0] = perspective[1] = perspective[2] = 0; perspective[3] = 1; } // Next take care of translation translate[0] = matrix.$matrix.m41 matrix.$matrix.m41 = 0 translate[1] = matrix.$matrix.m42 matrix.$matrix.m42 = 0 translate[2] = matrix.$matrix.m43 matrix.$matrix.m43 = 0 // Now get scale and shear. 'row' is a 3 element array of 3 component vectors var row0 = new J3DIVector3(matrix.$matrix.m11, matrix.$matrix.m12, matrix.$matrix.m13); var row1 = new J3DIVector3(matrix.$matrix.m21, matrix.$matrix.m22, matrix.$matrix.m23); var row2 = new J3DIVector3(matrix.$matrix.m31, matrix.$matrix.m32, matrix.$matrix.m33); // Compute X scale factor and normalize first row. scale[0] = row0.vectorLength(); row0.divide(scale[0]); // Compute XY shear factor and make 2nd row orthogonal to 1st. skew[0] = row0.dot(row1); row1.combine(row0, 1.0, -skew[0]); // Now, compute Y scale and normalize 2nd row. scale[1] = row1.vectorLength(); row1.divide(scale[1]); skew[0] /= scale[1]; // Compute XZ and YZ shears, orthogonalize 3rd row skew[1] = row1.dot(row2); row2.combine(row0, 1.0, -skew[1]); skew[2] = row1.dot(row2); row2.combine(row1, 1.0, -skew[2]); // Next, get Z scale and normalize 3rd row. scale[2] = row2.vectorLength(); row2.divide(scale[2]); skew[1] /= scale[2]; skew[2] /= scale[2]; // At this point, the matrix (in rows) is orthonormal. // Check for a coordinate system flip. If the determinant // is -1, then negate the matrix and the scaling factors. var pdum3 = new J3DIVector3(row1); pdum3.cross(row2); if (row0.dot(pdum3) < 0) { for (i = 0; i < 3; i++) { scale[i] *= -1; row[0][i] *= -1; row[1][i] *= -1; row[2][i] *= -1; } } // Now, get the rotations out rotate[1] = Math.asin(-row0[2]); if (Math.cos(rotate[1]) != 0) { rotate[0] = Math.atan2(row1[2], row2[2]); rotate[2] = Math.atan2(row0[1], row0[0]); } else { rotate[0] = Math.atan2(-row2[0], row1[1]); rotate[2] = 0; } // Convert rotations to degrees var rad2deg = 180 / Math.PI; rotate[0] *= rad2deg; rotate[1] *= rad2deg; rotate[2] *= rad2deg; return true; } J3DIMatrix4.prototype._determinant2x2 = function(a, b, c, d) { return a * d - b * c; } J3DIMatrix4.prototype._determinant3x3 = function(a1, a2, a3, b1, b2, b3, c1, c2, c3) { return a1 * this._determinant2x2(b2, b3, c2, c3) - b1 * this._determinant2x2(a2, a3, c2, c3) + c1 * this._determinant2x2(a2, a3, b2, b3); } J3DIMatrix4.prototype._determinant4x4 = function() { var a1 = this.$matrix.m11; var b1 = this.$matrix.m12; var c1 = this.$matrix.m13; var d1 = this.$matrix.m14; var a2 = this.$matrix.m21; var b2 = this.$matrix.m22; var c2 = this.$matrix.m23; var d2 = this.$matrix.m24; var a3 = this.$matrix.m31; var b3 = this.$matrix.m32; var c3 = this.$matrix.m33; var d3 = this.$matrix.m34; var a4 = this.$matrix.m41; var b4 = this.$matrix.m42; var c4 = this.$matrix.m43; var d4 = this.$matrix.m44; return a1 * this._determinant3x3(b2, b3, b4, c2, c3, c4, d2, d3, d4) - b1 * this._determinant3x3(a2, a3, a4, c2, c3, c4, d2, d3, d4) + c1 * this._determinant3x3(a2, a3, a4, b2, b3, b4, d2, d3, d4) - d1 * this._determinant3x3(a2, a3, a4, b2, b3, b4, c2, c3, c4); } J3DIMatrix4.prototype._makeAdjoint = function() { var a1 = this.$matrix.m11; var b1 = this.$matrix.m12; var c1 = this.$matrix.m13; var d1 = this.$matrix.m14; var a2 = this.$matrix.m21; var b2 = this.$matrix.m22; var c2 = this.$matrix.m23; var d2 = this.$matrix.m24; var a3 = this.$matrix.m31; var b3 = this.$matrix.m32; var c3 = this.$matrix.m33; var d3 = this.$matrix.m34; var a4 = this.$matrix.m41; var b4 = this.$matrix.m42; var c4 = this.$matrix.m43; var d4 = this.$matrix.m44; // Row column labeling reversed since we transpose rows & columns this.$matrix.m11 = this._determinant3x3(b2, b3, b4, c2, c3, c4, d2, d3, d4); this.$matrix.m21 = - this._determinant3x3(a2, a3, a4, c2, c3, c4, d2, d3, d4); this.$matrix.m31 = this._determinant3x3(a2, a3, a4, b2, b3, b4, d2, d3, d4); this.$matrix.m41 = - this._determinant3x3(a2, a3, a4, b2, b3, b4, c2, c3, c4); this.$matrix.m12 = - this._determinant3x3(b1, b3, b4, c1, c3, c4, d1, d3, d4); this.$matrix.m22 = this._determinant3x3(a1, a3, a4, c1, c3, c4, d1, d3, d4); this.$matrix.m32 = - this._determinant3x3(a1, a3, a4, b1, b3, b4, d1, d3, d4); this.$matrix.m42 = this._determinant3x3(a1, a3, a4, b1, b3, b4, c1, c3, c4); this.$matrix.m13 = this._determinant3x3(b1, b2, b4, c1, c2, c4, d1, d2, d4); this.$matrix.m23 = - this._determinant3x3(a1, a2, a4, c1, c2, c4, d1, d2, d4); this.$matrix.m33 = this._determinant3x3(a1, a2, a4, b1, b2, b4, d1, d2, d4); this.$matrix.m43 = - this._determinant3x3(a1, a2, a4, b1, b2, b4, c1, c2, c4); this.$matrix.m14 = - this._determinant3x3(b1, b2, b3, c1, c2, c3, d1, d2, d3); this.$matrix.m24 = this._determinant3x3(a1, a2, a3, c1, c2, c3, d1, d2, d3); this.$matrix.m34 = - this._determinant3x3(a1, a2, a3, b1, b2, b3, d1, d2, d3); this.$matrix.m44 = this._determinant3x3(a1, a2, a3, b1, b2, b3, c1, c2, c3); } // // J3DIVector3 // J3DIVector3 = function(x,y,z) { this.load(x,y,z); } J3DIVector3.prototype.load = function(x,y,z) { if (typeof x == 'object' && "length" in x) { this[0] = x[0]; this[1] = x[1]; this[2] = x[2]; } else if (typeof x == 'number') { this[0] = x; this[1] = y; this[2] = z; } else { this[0] = 0; this[1] = 0; this[2] = 0; } } J3DIVector3.prototype.getAsArray = function() { return [ this[0], this[1], this[2] ]; } J3DIVector3.prototype.getAsFloat32Array = function() { return new Float32Array(this.getAsArray()); } J3DIVector3.prototype.vectorLength = function() { return Math.sqrt(this[0] * this[0] + this[1] * this[1] + this[2] * this[2]); } J3DIVector3.prototype.divide = function(divisor) { this[0] /= divisor; this[1] /= divisor; this[2] /= divisor; } J3DIVector3.prototype.cross = function(v) { this[0] = this[1] * v[2] - this[2] * v[1]; this[1] = -this[0] * v[2] + this[2] * v[0]; this[2] = this[0] * v[1] - this[1] * v[0]; } J3DIVector3.prototype.dot = function(v) { return this[0] * v[0] + this[1] * v[1] + this[2] * v[2]; } J3DIVector3.prototype.combine = function(v, ascl, bscl) { this[0] = (ascl * this[0]) + (bscl * v[0]); this[1] = (ascl * this[1]) + (bscl * v[1]); this[2] = (ascl * this[2]) + (bscl * v[2]); } J3DIVector3.prototype.multVecMatrix = function(matrix) { var x = this[0]; var y = this[1]; var z = this[2]; this[0] = matrix.$matrix.m41 + x * matrix.$matrix.m11 + y * matrix.$matrix.m21 + z * matrix.$matrix.m31; this[1] = matrix.$matrix.m42 + x * matrix.$matrix.m12 + y * matrix.$matrix.m22 + z * matrix.$matrix.m32; this[2] = matrix.$matrix.m43 + x * matrix.$matrix.m13 + y * matrix.$matrix.m23 + z * matrix.$matrix.m33; var w = matrix.$matrix.m44 + x * matrix.$matrix.m14 + y * matrix.$matrix.m24 + z * matrix.$matrix.m34; if (w != 1 && w != 0) { this[0] /= w; this[1] /= w; this[2] /= w; } } J3DIVector3.prototype.toString = function() { return "["+this[0]+","+this[1]+","+this[2]+"]"; }