Added specular lighting.
4 This class implements a 4x4 matrix. It has functions which
5 duplicate the functionality of the OpenGL matrix stack and
11 Constructor(in CanvasMatrix4 matrix), // copy passed matrix into new CanvasMatrix4
12 Constructor(in sequence<float> array) // create new CanvasMatrix4 with 16 floats (row major)
13 Constructor() // create new CanvasMatrix4 with identity matrix
15 interface CanvasMatrix4 {
33 void load(in CanvasMatrix4 matrix); // copy the values from the passed matrix
34 void load(in sequence<float> array); // copy 16 floats into the matrix
35 sequence<float> getAsArray(); // return the matrix as an array of 16 floats
36 WebGLFloatArray getAsCanvasFloatArray(); // return the matrix as a WebGLFloatArray with 16 values
37 void makeIdentity(); // replace the matrix with identity
38 void transpose(); // replace the matrix with its transpose
39 void invert(); // replace the matrix with its inverse
41 void translate(in float x, in float y, in float z); // multiply the matrix by passed translation values on the right
42 void scale(in float x, in float y, in float z); // multiply the matrix by passed scale values on the right
43 void rotate(in float angle, // multiply the matrix by passed rotation values on the right
44 in float x, in float y, in float z); // (angle is in degrees)
45 void multRight(in CanvasMatrix matrix); // multiply the matrix by the passed matrix on the right
46 void multLeft(in CanvasMatrix matrix); // multiply the matrix by the passed matrix on the left
47 void ortho(in float left, in float right, // multiply the matrix by the passed ortho values on the right
48 in float bottom, in float top,
49 in float near, in float far);
50 void frustum(in float left, in float right, // multiply the matrix by the passed frustum values on the right
51 in float bottom, in float top,
52 in float near, in float far);
53 void perspective(in float fovy, in float aspect, // multiply the matrix by the passed perspective values on the right
54 in float zNear, in float zFar);
55 void lookat(in float eyex, in float eyey, in float eyez, // multiply the matrix by the passed lookat
56 in float ctrx, in float ctry, in float ctrz, // values on the right
57 in float upx, in float upy, in float upz);
61 CanvasMatrix4 = function(m)
63 if (typeof m == 'object') {
64 if ("length" in m && m.length >= 16) {
65 this.load(m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8], m[9], m[10], m[11], m[12], m[13], m[14], m[15]);
68 else if (m instanceof CanvasMatrix4) {
76 CanvasMatrix4.prototype.load = function()
78 if (arguments.length == 1 && typeof arguments[0] == 'object') {
79 var matrix = arguments[0];
81 if ("length" in matrix && matrix.length == 16) {
94 this.m33 = matrix[10];
95 this.m34 = matrix[11];
97 this.m41 = matrix[12];
98 this.m42 = matrix[13];
99 this.m43 = matrix[14];
100 this.m44 = matrix[15];
104 if (arguments[0] instanceof CanvasMatrix4) {
106 this.m11 = matrix.m11;
107 this.m12 = matrix.m12;
108 this.m13 = matrix.m13;
109 this.m14 = matrix.m14;
111 this.m21 = matrix.m21;
112 this.m22 = matrix.m22;
113 this.m23 = matrix.m23;
114 this.m24 = matrix.m24;
116 this.m31 = matrix.m31;
117 this.m32 = matrix.m32;
118 this.m33 = matrix.m33;
119 this.m34 = matrix.m34;
121 this.m41 = matrix.m41;
122 this.m42 = matrix.m42;
123 this.m43 = matrix.m43;
124 this.m44 = matrix.m44;
132 CanvasMatrix4.prototype.getAsArray = function()
135 this.m11, this.m12, this.m13, this.m14,
136 this.m21, this.m22, this.m23, this.m24,
137 this.m31, this.m32, this.m33, this.m34,
138 this.m41, this.m42, this.m43, this.m44
142 CanvasMatrix4.prototype.getAsCanvasFloatArray = function()
144 return new WebGLFloatArray(this.getAsArray());
147 CanvasMatrix4.prototype.makeIdentity = function()
170 CanvasMatrix4.prototype.transpose = function()
197 CanvasMatrix4.prototype.invert = function()
199 // Calculate the 4x4 determinant
200 // If the determinant is zero,
201 // then the inverse matrix is not unique.
202 var det = this._determinant4x4();
204 if (Math.abs(det) < 1e-8)
209 // Scale the adjoint matrix to get the inverse
231 CanvasMatrix4.prototype.translate = function(x,y,z)
240 var matrix = new CanvasMatrix4();
245 this.multRight(matrix);
248 CanvasMatrix4.prototype.scale = function(x,y,z)
252 if (z == undefined) {
253 if (y == undefined) {
260 else if (y == undefined)
263 var matrix = new CanvasMatrix4();
268 this.multRight(matrix);
271 CanvasMatrix4.prototype.rotate = function(angle,x,y,z)
273 // angles are in degrees. Switch to radians
274 angle = angle / 180 * Math.PI;
277 var sinA = Math.sin(angle);
278 var cosA = Math.cos(angle);
279 var sinA2 = sinA * sinA;
282 var length = Math.sqrt(x * x + y * y + z * z);
284 // bad vector, just use something reasonable
288 } else if (length != 1) {
294 var mat = new CanvasMatrix4();
296 // optimize case where axis is along major axis
297 if (x == 1 && y == 0 && z == 0) {
302 mat.m22 = 1 - 2 * sinA2;
303 mat.m23 = 2 * sinA * cosA;
305 mat.m32 = -2 * sinA * cosA;
306 mat.m33 = 1 - 2 * sinA2;
307 mat.m14 = mat.m24 = mat.m34 = 0;
308 mat.m41 = mat.m42 = mat.m43 = 0;
310 } else if (x == 0 && y == 1 && z == 0) {
311 mat.m11 = 1 - 2 * sinA2;
313 mat.m13 = -2 * sinA * cosA;
317 mat.m31 = 2 * sinA * cosA;
319 mat.m33 = 1 - 2 * sinA2;
320 mat.m14 = mat.m24 = mat.m34 = 0;
321 mat.m41 = mat.m42 = mat.m43 = 0;
323 } else if (x == 0 && y == 0 && z == 1) {
324 mat.m11 = 1 - 2 * sinA2;
325 mat.m12 = 2 * sinA * cosA;
327 mat.m21 = -2 * sinA * cosA;
328 mat.m22 = 1 - 2 * sinA2;
333 mat.m14 = mat.m24 = mat.m34 = 0;
334 mat.m41 = mat.m42 = mat.m43 = 0;
341 mat.m11 = 1 - 2 * (y2 + z2) * sinA2;
342 mat.m12 = 2 * (x * y * sinA2 + z * sinA * cosA);
343 mat.m13 = 2 * (x * z * sinA2 - y * sinA * cosA);
344 mat.m21 = 2 * (y * x * sinA2 - z * sinA * cosA);
345 mat.m22 = 1 - 2 * (z2 + x2) * sinA2;
346 mat.m23 = 2 * (y * z * sinA2 + x * sinA * cosA);
347 mat.m31 = 2 * (z * x * sinA2 + y * sinA * cosA);
348 mat.m32 = 2 * (z * y * sinA2 - x * sinA * cosA);
349 mat.m33 = 1 - 2 * (x2 + y2) * sinA2;
350 mat.m14 = mat.m24 = mat.m34 = 0;
351 mat.m41 = mat.m42 = mat.m43 = 0;
357 CanvasMatrix4.prototype.multRight = function(mat)
359 var m11 = (this.m11 * mat.m11 + this.m12 * mat.m21
360 + this.m13 * mat.m31 + this.m14 * mat.m41);
361 var m12 = (this.m11 * mat.m12 + this.m12 * mat.m22
362 + this.m13 * mat.m32 + this.m14 * mat.m42);
363 var m13 = (this.m11 * mat.m13 + this.m12 * mat.m23
364 + this.m13 * mat.m33 + this.m14 * mat.m43);
365 var m14 = (this.m11 * mat.m14 + this.m12 * mat.m24
366 + this.m13 * mat.m34 + this.m14 * mat.m44);
368 var m21 = (this.m21 * mat.m11 + this.m22 * mat.m21
369 + this.m23 * mat.m31 + this.m24 * mat.m41);
370 var m22 = (this.m21 * mat.m12 + this.m22 * mat.m22
371 + this.m23 * mat.m32 + this.m24 * mat.m42);
372 var m23 = (this.m21 * mat.m13 + this.m22 * mat.m23
373 + this.m23 * mat.m33 + this.m24 * mat.m43);
374 var m24 = (this.m21 * mat.m14 + this.m22 * mat.m24
375 + this.m23 * mat.m34 + this.m24 * mat.m44);
377 var m31 = (this.m31 * mat.m11 + this.m32 * mat.m21
378 + this.m33 * mat.m31 + this.m34 * mat.m41);
379 var m32 = (this.m31 * mat.m12 + this.m32 * mat.m22
380 + this.m33 * mat.m32 + this.m34 * mat.m42);
381 var m33 = (this.m31 * mat.m13 + this.m32 * mat.m23
382 + this.m33 * mat.m33 + this.m34 * mat.m43);
383 var m34 = (this.m31 * mat.m14 + this.m32 * mat.m24
384 + this.m33 * mat.m34 + this.m34 * mat.m44);
386 var m41 = (this.m41 * mat.m11 + this.m42 * mat.m21
387 + this.m43 * mat.m31 + this.m44 * mat.m41);
388 var m42 = (this.m41 * mat.m12 + this.m42 * mat.m22
389 + this.m43 * mat.m32 + this.m44 * mat.m42);
390 var m43 = (this.m41 * mat.m13 + this.m42 * mat.m23
391 + this.m43 * mat.m33 + this.m44 * mat.m43);
392 var m44 = (this.m41 * mat.m14 + this.m42 * mat.m24
393 + this.m43 * mat.m34 + this.m44 * mat.m44);
416 CanvasMatrix4.prototype.multLeft = function(mat)
418 var m11 = (mat.m11 * this.m11 + mat.m12 * this.m21
419 + mat.m13 * this.m31 + mat.m14 * this.m41);
420 var m12 = (mat.m11 * this.m12 + mat.m12 * this.m22
421 + mat.m13 * this.m32 + mat.m14 * this.m42);
422 var m13 = (mat.m11 * this.m13 + mat.m12 * this.m23
423 + mat.m13 * this.m33 + mat.m14 * this.m43);
424 var m14 = (mat.m11 * this.m14 + mat.m12 * this.m24
425 + mat.m13 * this.m34 + mat.m14 * this.m44);
427 var m21 = (mat.m21 * this.m11 + mat.m22 * this.m21
428 + mat.m23 * this.m31 + mat.m24 * this.m41);
429 var m22 = (mat.m21 * this.m12 + mat.m22 * this.m22
430 + mat.m23 * this.m32 + mat.m24 * this.m42);
431 var m23 = (mat.m21 * this.m13 + mat.m22 * this.m23
432 + mat.m23 * this.m33 + mat.m24 * this.m43);
433 var m24 = (mat.m21 * this.m14 + mat.m22 * this.m24
434 + mat.m23 * this.m34 + mat.m24 * this.m44);
436 var m31 = (mat.m31 * this.m11 + mat.m32 * this.m21
437 + mat.m33 * this.m31 + mat.m34 * this.m41);
438 var m32 = (mat.m31 * this.m12 + mat.m32 * this.m22
439 + mat.m33 * this.m32 + mat.m34 * this.m42);
440 var m33 = (mat.m31 * this.m13 + mat.m32 * this.m23
441 + mat.m33 * this.m33 + mat.m34 * this.m43);
442 var m34 = (mat.m31 * this.m14 + mat.m32 * this.m24
443 + mat.m33 * this.m34 + mat.m34 * this.m44);
445 var m41 = (mat.m41 * this.m11 + mat.m42 * this.m21
446 + mat.m43 * this.m31 + mat.m44 * this.m41);
447 var m42 = (mat.m41 * this.m12 + mat.m42 * this.m22
448 + mat.m43 * this.m32 + mat.m44 * this.m42);
449 var m43 = (mat.m41 * this.m13 + mat.m42 * this.m23
450 + mat.m43 * this.m33 + mat.m44 * this.m43);
451 var m44 = (mat.m41 * this.m14 + mat.m42 * this.m24
452 + mat.m43 * this.m34 + mat.m44 * this.m44);
475 CanvasMatrix4.prototype.ortho = function(left, right, bottom, top, near, far)
477 var tx = (left + right) / (left - right);
478 var ty = (top + bottom) / (top - bottom);
479 var tz = (far + near) / (far - near);
481 var matrix = new CanvasMatrix4();
482 matrix.m11 = 2 / (left - right);
487 matrix.m22 = 2 / (top - bottom);
492 matrix.m33 = -2 / (far - near);
499 this.multRight(matrix);
502 CanvasMatrix4.prototype.frustum = function(left, right, bottom, top, near, far)
504 var matrix = new CanvasMatrix4();
505 var A = (right + left) / (right - left);
506 var B = (top + bottom) / (top - bottom);
507 var C = -(far + near) / (far - near);
508 var D = -(2 * far * near) / (far - near);
510 matrix.m11 = (2 * near) / (right - left);
516 matrix.m22 = 2 * near / (top - bottom);
530 this.multRight(matrix);
533 CanvasMatrix4.prototype.perspective = function(fovy, aspect, zNear, zFar)
535 var top = Math.tan(fovy * Math.PI / 360) * zNear;
537 var left = aspect * bottom;
538 var right = aspect * top;
539 this.frustum(left, right, bottom, top, zNear, zFar);
542 CanvasMatrix4.prototype.lookat = function(eyex, eyey, eyez, centerx, centery, centerz, upx, upy, upz)
544 var matrix = new CanvasMatrix4();
546 // Make rotation matrix
549 var zx = eyex - centerx;
550 var zy = eyey - centery;
551 var zz = eyez - centerz;
552 var mag = Math.sqrt(zx * zx + zy * zy + zz * zz);
564 // X vector = Y cross Z
565 xx = yy * zz - yz * zy;
566 xy = -yx * zz + yz * zx;
567 xz = yx * zy - yy * zx;
569 // Recompute Y = Z cross X
570 yx = zy * xz - zz * xy;
571 yy = -zx * xz + zz * xx;
572 yx = zx * xy - zy * xx;
574 // cross product gives area of parallelogram, which is < 1.0 for
575 // non-perpendicular unit-length vectors; so normalize x, y here
577 mag = Math.sqrt(xx * xx + xy * xy + xz * xz);
584 mag = Math.sqrt(yx * yx + yy * yy + yz * yz);
610 matrix.translate(-eyex, -eyey, -eyez);
612 this.multRight(matrix);
616 CanvasMatrix4.prototype._determinant2x2 = function(a, b, c, d)
618 return a * d - b * c;
621 CanvasMatrix4.prototype._determinant3x3 = function(a1, a2, a3, b1, b2, b3, c1, c2, c3)
623 return a1 * this._determinant2x2(b2, b3, c2, c3)
624 - b1 * this._determinant2x2(a2, a3, c2, c3)
625 + c1 * this._determinant2x2(a2, a3, b2, b3);
628 CanvasMatrix4.prototype._determinant4x4 = function()
650 return a1 * this._determinant3x3(b2, b3, b4, c2, c3, c4, d2, d3, d4)
651 - b1 * this._determinant3x3(a2, a3, a4, c2, c3, c4, d2, d3, d4)
652 + c1 * this._determinant3x3(a2, a3, a4, b2, b3, b4, d2, d3, d4)
653 - d1 * this._determinant3x3(a2, a3, a4, b2, b3, b4, c2, c3, c4);
656 CanvasMatrix4.prototype._makeAdjoint = function()
678 // Row column labeling reversed since we transpose rows & columns
679 this.m11 = this._determinant3x3(b2, b3, b4, c2, c3, c4, d2, d3, d4);
680 this.m21 = - this._determinant3x3(a2, a3, a4, c2, c3, c4, d2, d3, d4);
681 this.m31 = this._determinant3x3(a2, a3, a4, b2, b3, b4, d2, d3, d4);
682 this.m41 = - this._determinant3x3(a2, a3, a4, b2, b3, b4, c2, c3, c4);
684 this.m12 = - this._determinant3x3(b1, b3, b4, c1, c3, c4, d1, d3, d4);
685 this.m22 = this._determinant3x3(a1, a3, a4, c1, c3, c4, d1, d3, d4);
686 this.m32 = - this._determinant3x3(a1, a3, a4, b1, b3, b4, d1, d3, d4);
687 this.m42 = this._determinant3x3(a1, a3, a4, b1, b3, b4, c1, c3, c4);
689 this.m13 = this._determinant3x3(b1, b2, b4, c1, c2, c4, d1, d2, d4);
690 this.m23 = - this._determinant3x3(a1, a2, a4, c1, c2, c4, d1, d2, d4);
691 this.m33 = this._determinant3x3(a1, a2, a4, b1, b2, b4, d1, d2, d4);
692 this.m43 = - this._determinant3x3(a1, a2, a4, b1, b2, b4, c1, c2, c4);
694 this.m14 = - this._determinant3x3(b1, b2, b3, c1, c2, c3, d1, d2, d3);
695 this.m24 = this._determinant3x3(a1, a2, a3, c1, c2, c3, d1, d2, d3);
696 this.m34 = - this._determinant3x3(a1, a2, a3, b1, b2, b3, d1, d2, d3);
697 this.m44 = this._determinant3x3(a1, a2, a3, b1, b2, b3, c1, c2, c3);