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