1/* Copyright (c) 2013, Brandon Jones, Colin MacKenzie IV. All rights reserved.
2
3Redistribution and use in source and binary forms, with or without modification,
4are permitted provided that the following conditions are met:
5
6  * Redistributions of source code must retain the above copyright notice, this
7    list of conditions and the following disclaimer.
8  * Redistributions in binary form must reproduce the above copyright notice,
9    this list of conditions and the following disclaimer in the documentation
10    and/or other materials provided with the distribution.
11
12THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
13ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
14WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
15DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
16ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
17(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
18LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
19ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
20(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
21SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
22
23/**
24 * @class 4x4 Matrix
25 * @name mat4
26 */
27var mat4 = {};
28
29/**
30 * Creates a new identity mat4
31 *
32 * @returns {mat4} a new 4x4 matrix
33 */
34mat4.create = function() {
35    var out = new GLMAT_ARRAY_TYPE(16);
36    out[0] = 1;
37    out[1] = 0;
38    out[2] = 0;
39    out[3] = 0;
40    out[4] = 0;
41    out[5] = 1;
42    out[6] = 0;
43    out[7] = 0;
44    out[8] = 0;
45    out[9] = 0;
46    out[10] = 1;
47    out[11] = 0;
48    out[12] = 0;
49    out[13] = 0;
50    out[14] = 0;
51    out[15] = 1;
52    return out;
53};
54
55/**
56 * Creates a new mat4 initialized with values from an existing matrix
57 *
58 * @param {mat4} a matrix to clone
59 * @returns {mat4} a new 4x4 matrix
60 */
61mat4.clone = function(a) {
62    var out = new GLMAT_ARRAY_TYPE(16);
63    out[0] = a[0];
64    out[1] = a[1];
65    out[2] = a[2];
66    out[3] = a[3];
67    out[4] = a[4];
68    out[5] = a[5];
69    out[6] = a[6];
70    out[7] = a[7];
71    out[8] = a[8];
72    out[9] = a[9];
73    out[10] = a[10];
74    out[11] = a[11];
75    out[12] = a[12];
76    out[13] = a[13];
77    out[14] = a[14];
78    out[15] = a[15];
79    return out;
80};
81
82/**
83 * Copy the values from one mat4 to another
84 *
85 * @param {mat4} out the receiving matrix
86 * @param {mat4} a the source matrix
87 * @returns {mat4} out
88 */
89mat4.copy = function(out, a) {
90    out[0] = a[0];
91    out[1] = a[1];
92    out[2] = a[2];
93    out[3] = a[3];
94    out[4] = a[4];
95    out[5] = a[5];
96    out[6] = a[6];
97    out[7] = a[7];
98    out[8] = a[8];
99    out[9] = a[9];
100    out[10] = a[10];
101    out[11] = a[11];
102    out[12] = a[12];
103    out[13] = a[13];
104    out[14] = a[14];
105    out[15] = a[15];
106    return out;
107};
108
109/**
110 * Set a mat4 to the identity matrix
111 *
112 * @param {mat4} out the receiving matrix
113 * @returns {mat4} out
114 */
115mat4.identity = function(out) {
116    out[0] = 1;
117    out[1] = 0;
118    out[2] = 0;
119    out[3] = 0;
120    out[4] = 0;
121    out[5] = 1;
122    out[6] = 0;
123    out[7] = 0;
124    out[8] = 0;
125    out[9] = 0;
126    out[10] = 1;
127    out[11] = 0;
128    out[12] = 0;
129    out[13] = 0;
130    out[14] = 0;
131    out[15] = 1;
132    return out;
133};
134
135/**
136 * Transpose the values of a mat4
137 *
138 * @param {mat4} out the receiving matrix
139 * @param {mat4} a the source matrix
140 * @returns {mat4} out
141 */
142mat4.transpose = function(out, a) {
143    // If we are transposing ourselves we can skip a few steps but have to cache some values
144    if (out === a) {
145        var a01 = a[1], a02 = a[2], a03 = a[3],
146            a12 = a[6], a13 = a[7],
147            a23 = a[11];
148
149        out[1] = a[4];
150        out[2] = a[8];
151        out[3] = a[12];
152        out[4] = a01;
153        out[6] = a[9];
154        out[7] = a[13];
155        out[8] = a02;
156        out[9] = a12;
157        out[11] = a[14];
158        out[12] = a03;
159        out[13] = a13;
160        out[14] = a23;
161    } else {
162        out[0] = a[0];
163        out[1] = a[4];
164        out[2] = a[8];
165        out[3] = a[12];
166        out[4] = a[1];
167        out[5] = a[5];
168        out[6] = a[9];
169        out[7] = a[13];
170        out[8] = a[2];
171        out[9] = a[6];
172        out[10] = a[10];
173        out[11] = a[14];
174        out[12] = a[3];
175        out[13] = a[7];
176        out[14] = a[11];
177        out[15] = a[15];
178    }
179
180    return out;
181};
182
183/**
184 * Inverts a mat4
185 *
186 * @param {mat4} out the receiving matrix
187 * @param {mat4} a the source matrix
188 * @returns {mat4} out
189 */
190mat4.invert = function(out, a) {
191    var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3],
192        a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7],
193        a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11],
194        a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15],
195
196        b00 = a00 * a11 - a01 * a10,
197        b01 = a00 * a12 - a02 * a10,
198        b02 = a00 * a13 - a03 * a10,
199        b03 = a01 * a12 - a02 * a11,
200        b04 = a01 * a13 - a03 * a11,
201        b05 = a02 * a13 - a03 * a12,
202        b06 = a20 * a31 - a21 * a30,
203        b07 = a20 * a32 - a22 * a30,
204        b08 = a20 * a33 - a23 * a30,
205        b09 = a21 * a32 - a22 * a31,
206        b10 = a21 * a33 - a23 * a31,
207        b11 = a22 * a33 - a23 * a32,
208
209        // Calculate the determinant
210        det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;
211
212    if (!det) {
213        return null;
214    }
215    det = 1.0 / det;
216
217    out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det;
218    out[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det;
219    out[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det;
220    out[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det;
221    out[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det;
222    out[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det;
223    out[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det;
224    out[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det;
225    out[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det;
226    out[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det;
227    out[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det;
228    out[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det;
229    out[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det;
230    out[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det;
231    out[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det;
232    out[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det;
233
234    return out;
235};
236
237/**
238 * Calculates the adjugate of a mat4
239 *
240 * @param {mat4} out the receiving matrix
241 * @param {mat4} a the source matrix
242 * @returns {mat4} out
243 */
244mat4.adjoint = function(out, a) {
245    var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3],
246        a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7],
247        a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11],
248        a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15];
249
250    out[0]  =  (a11 * (a22 * a33 - a23 * a32) - a21 * (a12 * a33 - a13 * a32) + a31 * (a12 * a23 - a13 * a22));
251    out[1]  = -(a01 * (a22 * a33 - a23 * a32) - a21 * (a02 * a33 - a03 * a32) + a31 * (a02 * a23 - a03 * a22));
252    out[2]  =  (a01 * (a12 * a33 - a13 * a32) - a11 * (a02 * a33 - a03 * a32) + a31 * (a02 * a13 - a03 * a12));
253    out[3]  = -(a01 * (a12 * a23 - a13 * a22) - a11 * (a02 * a23 - a03 * a22) + a21 * (a02 * a13 - a03 * a12));
254    out[4]  = -(a10 * (a22 * a33 - a23 * a32) - a20 * (a12 * a33 - a13 * a32) + a30 * (a12 * a23 - a13 * a22));
255    out[5]  =  (a00 * (a22 * a33 - a23 * a32) - a20 * (a02 * a33 - a03 * a32) + a30 * (a02 * a23 - a03 * a22));
256    out[6]  = -(a00 * (a12 * a33 - a13 * a32) - a10 * (a02 * a33 - a03 * a32) + a30 * (a02 * a13 - a03 * a12));
257    out[7]  =  (a00 * (a12 * a23 - a13 * a22) - a10 * (a02 * a23 - a03 * a22) + a20 * (a02 * a13 - a03 * a12));
258    out[8]  =  (a10 * (a21 * a33 - a23 * a31) - a20 * (a11 * a33 - a13 * a31) + a30 * (a11 * a23 - a13 * a21));
259    out[9]  = -(a00 * (a21 * a33 - a23 * a31) - a20 * (a01 * a33 - a03 * a31) + a30 * (a01 * a23 - a03 * a21));
260    out[10] =  (a00 * (a11 * a33 - a13 * a31) - a10 * (a01 * a33 - a03 * a31) + a30 * (a01 * a13 - a03 * a11));
261    out[11] = -(a00 * (a11 * a23 - a13 * a21) - a10 * (a01 * a23 - a03 * a21) + a20 * (a01 * a13 - a03 * a11));
262    out[12] = -(a10 * (a21 * a32 - a22 * a31) - a20 * (a11 * a32 - a12 * a31) + a30 * (a11 * a22 - a12 * a21));
263    out[13] =  (a00 * (a21 * a32 - a22 * a31) - a20 * (a01 * a32 - a02 * a31) + a30 * (a01 * a22 - a02 * a21));
264    out[14] = -(a00 * (a11 * a32 - a12 * a31) - a10 * (a01 * a32 - a02 * a31) + a30 * (a01 * a12 - a02 * a11));
265    out[15] =  (a00 * (a11 * a22 - a12 * a21) - a10 * (a01 * a22 - a02 * a21) + a20 * (a01 * a12 - a02 * a11));
266    return out;
267};
268
269/**
270 * Calculates the determinant of a mat4
271 *
272 * @param {mat4} a the source matrix
273 * @returns {Number} determinant of a
274 */
275mat4.determinant = function (a) {
276    var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3],
277        a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7],
278        a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11],
279        a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15],
280
281        b00 = a00 * a11 - a01 * a10,
282        b01 = a00 * a12 - a02 * a10,
283        b02 = a00 * a13 - a03 * a10,
284        b03 = a01 * a12 - a02 * a11,
285        b04 = a01 * a13 - a03 * a11,
286        b05 = a02 * a13 - a03 * a12,
287        b06 = a20 * a31 - a21 * a30,
288        b07 = a20 * a32 - a22 * a30,
289        b08 = a20 * a33 - a23 * a30,
290        b09 = a21 * a32 - a22 * a31,
291        b10 = a21 * a33 - a23 * a31,
292        b11 = a22 * a33 - a23 * a32;
293
294    // Calculate the determinant
295    return b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;
296};
297
298/**
299 * Multiplies two mat4's
300 *
301 * @param {mat4} out the receiving matrix
302 * @param {mat4} a the first operand
303 * @param {mat4} b the second operand
304 * @returns {mat4} out
305 */
306mat4.multiply = function (out, a, b) {
307    var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3],
308        a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7],
309        a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11],
310        a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15];
311
312    // Cache only the current line of the second matrix
313    var b0  = b[0], b1 = b[1], b2 = b[2], b3 = b[3];
314    out[0] = b0*a00 + b1*a10 + b2*a20 + b3*a30;
315    out[1] = b0*a01 + b1*a11 + b2*a21 + b3*a31;
316    out[2] = b0*a02 + b1*a12 + b2*a22 + b3*a32;
317    out[3] = b0*a03 + b1*a13 + b2*a23 + b3*a33;
318
319    b0 = b[4]; b1 = b[5]; b2 = b[6]; b3 = b[7];
320    out[4] = b0*a00 + b1*a10 + b2*a20 + b3*a30;
321    out[5] = b0*a01 + b1*a11 + b2*a21 + b3*a31;
322    out[6] = b0*a02 + b1*a12 + b2*a22 + b3*a32;
323    out[7] = b0*a03 + b1*a13 + b2*a23 + b3*a33;
324
325    b0 = b[8]; b1 = b[9]; b2 = b[10]; b3 = b[11];
326    out[8] = b0*a00 + b1*a10 + b2*a20 + b3*a30;
327    out[9] = b0*a01 + b1*a11 + b2*a21 + b3*a31;
328    out[10] = b0*a02 + b1*a12 + b2*a22 + b3*a32;
329    out[11] = b0*a03 + b1*a13 + b2*a23 + b3*a33;
330
331    b0 = b[12]; b1 = b[13]; b2 = b[14]; b3 = b[15];
332    out[12] = b0*a00 + b1*a10 + b2*a20 + b3*a30;
333    out[13] = b0*a01 + b1*a11 + b2*a21 + b3*a31;
334    out[14] = b0*a02 + b1*a12 + b2*a22 + b3*a32;
335    out[15] = b0*a03 + b1*a13 + b2*a23 + b3*a33;
336    return out;
337};
338
339/**
340 * Alias for {@link mat4.multiply}
341 * @function
342 */
343mat4.mul = mat4.multiply;
344
345/**
346 * Translate a mat4 by the given vector
347 *
348 * @param {mat4} out the receiving matrix
349 * @param {mat4} a the matrix to translate
350 * @param {vec3} v vector to translate by
351 * @returns {mat4} out
352 */
353mat4.translate = function (out, a, v) {
354    var x = v[0], y = v[1], z = v[2],
355        a00, a01, a02, a03,
356        a10, a11, a12, a13,
357        a20, a21, a22, a23;
358
359    if (a === out) {
360        out[12] = a[0] * x + a[4] * y + a[8] * z + a[12];
361        out[13] = a[1] * x + a[5] * y + a[9] * z + a[13];
362        out[14] = a[2] * x + a[6] * y + a[10] * z + a[14];
363        out[15] = a[3] * x + a[7] * y + a[11] * z + a[15];
364    } else {
365        a00 = a[0]; a01 = a[1]; a02 = a[2]; a03 = a[3];
366        a10 = a[4]; a11 = a[5]; a12 = a[6]; a13 = a[7];
367        a20 = a[8]; a21 = a[9]; a22 = a[10]; a23 = a[11];
368
369        out[0] = a00; out[1] = a01; out[2] = a02; out[3] = a03;
370        out[4] = a10; out[5] = a11; out[6] = a12; out[7] = a13;
371        out[8] = a20; out[9] = a21; out[10] = a22; out[11] = a23;
372
373        out[12] = a00 * x + a10 * y + a20 * z + a[12];
374        out[13] = a01 * x + a11 * y + a21 * z + a[13];
375        out[14] = a02 * x + a12 * y + a22 * z + a[14];
376        out[15] = a03 * x + a13 * y + a23 * z + a[15];
377    }
378
379    return out;
380};
381
382/**
383 * Scales the mat4 by the dimensions in the given vec3
384 *
385 * @param {mat4} out the receiving matrix
386 * @param {mat4} a the matrix to scale
387 * @param {vec3} v the vec3 to scale the matrix by
388 * @returns {mat4} out
389 **/
390mat4.scale = function(out, a, v) {
391    var x = v[0], y = v[1], z = v[2];
392
393    out[0] = a[0] * x;
394    out[1] = a[1] * x;
395    out[2] = a[2] * x;
396    out[3] = a[3] * x;
397    out[4] = a[4] * y;
398    out[5] = a[5] * y;
399    out[6] = a[6] * y;
400    out[7] = a[7] * y;
401    out[8] = a[8] * z;
402    out[9] = a[9] * z;
403    out[10] = a[10] * z;
404    out[11] = a[11] * z;
405    out[12] = a[12];
406    out[13] = a[13];
407    out[14] = a[14];
408    out[15] = a[15];
409    return out;
410};
411
412/**
413 * Rotates a mat4 by the given angle
414 *
415 * @param {mat4} out the receiving matrix
416 * @param {mat4} a the matrix to rotate
417 * @param {Number} rad the angle to rotate the matrix by
418 * @param {vec3} axis the axis to rotate around
419 * @returns {mat4} out
420 */
421mat4.rotate = function (out, a, rad, axis) {
422    var x = axis[0], y = axis[1], z = axis[2],
423        len = Math.sqrt(x * x + y * y + z * z),
424        s, c, t,
425        a00, a01, a02, a03,
426        a10, a11, a12, a13,
427        a20, a21, a22, a23,
428        b00, b01, b02,
429        b10, b11, b12,
430        b20, b21, b22;
431
432    if (Math.abs(len) < GLMAT_EPSILON) { return null; }
433
434    len = 1 / len;
435    x *= len;
436    y *= len;
437    z *= len;
438
439    s = Math.sin(rad);
440    c = Math.cos(rad);
441    t = 1 - c;
442
443    a00 = a[0]; a01 = a[1]; a02 = a[2]; a03 = a[3];
444    a10 = a[4]; a11 = a[5]; a12 = a[6]; a13 = a[7];
445    a20 = a[8]; a21 = a[9]; a22 = a[10]; a23 = a[11];
446
447    // Construct the elements of the rotation matrix
448    b00 = x * x * t + c; b01 = y * x * t + z * s; b02 = z * x * t - y * s;
449    b10 = x * y * t - z * s; b11 = y * y * t + c; b12 = z * y * t + x * s;
450    b20 = x * z * t + y * s; b21 = y * z * t - x * s; b22 = z * z * t + c;
451
452    // Perform rotation-specific matrix multiplication
453    out[0] = a00 * b00 + a10 * b01 + a20 * b02;
454    out[1] = a01 * b00 + a11 * b01 + a21 * b02;
455    out[2] = a02 * b00 + a12 * b01 + a22 * b02;
456    out[3] = a03 * b00 + a13 * b01 + a23 * b02;
457    out[4] = a00 * b10 + a10 * b11 + a20 * b12;
458    out[5] = a01 * b10 + a11 * b11 + a21 * b12;
459    out[6] = a02 * b10 + a12 * b11 + a22 * b12;
460    out[7] = a03 * b10 + a13 * b11 + a23 * b12;
461    out[8] = a00 * b20 + a10 * b21 + a20 * b22;
462    out[9] = a01 * b20 + a11 * b21 + a21 * b22;
463    out[10] = a02 * b20 + a12 * b21 + a22 * b22;
464    out[11] = a03 * b20 + a13 * b21 + a23 * b22;
465
466    if (a !== out) { // If the source and destination differ, copy the unchanged last row
467        out[12] = a[12];
468        out[13] = a[13];
469        out[14] = a[14];
470        out[15] = a[15];
471    }
472    return out;
473};
474
475/**
476 * Rotates a matrix by the given angle around the X axis
477 *
478 * @param {mat4} out the receiving matrix
479 * @param {mat4} a the matrix to rotate
480 * @param {Number} rad the angle to rotate the matrix by
481 * @returns {mat4} out
482 */
483mat4.rotateX = function (out, a, rad) {
484    var s = Math.sin(rad),
485        c = Math.cos(rad),
486        a10 = a[4],
487        a11 = a[5],
488        a12 = a[6],
489        a13 = a[7],
490        a20 = a[8],
491        a21 = a[9],
492        a22 = a[10],
493        a23 = a[11];
494
495    if (a !== out) { // If the source and destination differ, copy the unchanged rows
496        out[0]  = a[0];
497        out[1]  = a[1];
498        out[2]  = a[2];
499        out[3]  = a[3];
500        out[12] = a[12];
501        out[13] = a[13];
502        out[14] = a[14];
503        out[15] = a[15];
504    }
505
506    // Perform axis-specific matrix multiplication
507    out[4] = a10 * c + a20 * s;
508    out[5] = a11 * c + a21 * s;
509    out[6] = a12 * c + a22 * s;
510    out[7] = a13 * c + a23 * s;
511    out[8] = a20 * c - a10 * s;
512    out[9] = a21 * c - a11 * s;
513    out[10] = a22 * c - a12 * s;
514    out[11] = a23 * c - a13 * s;
515    return out;
516};
517
518/**
519 * Rotates a matrix by the given angle around the Y axis
520 *
521 * @param {mat4} out the receiving matrix
522 * @param {mat4} a the matrix to rotate
523 * @param {Number} rad the angle to rotate the matrix by
524 * @returns {mat4} out
525 */
526mat4.rotateY = function (out, a, rad) {
527    var s = Math.sin(rad),
528        c = Math.cos(rad),
529        a00 = a[0],
530        a01 = a[1],
531        a02 = a[2],
532        a03 = a[3],
533        a20 = a[8],
534        a21 = a[9],
535        a22 = a[10],
536        a23 = a[11];
537
538    if (a !== out) { // If the source and destination differ, copy the unchanged rows
539        out[4]  = a[4];
540        out[5]  = a[5];
541        out[6]  = a[6];
542        out[7]  = a[7];
543        out[12] = a[12];
544        out[13] = a[13];
545        out[14] = a[14];
546        out[15] = a[15];
547    }
548
549    // Perform axis-specific matrix multiplication
550    out[0] = a00 * c - a20 * s;
551    out[1] = a01 * c - a21 * s;
552    out[2] = a02 * c - a22 * s;
553    out[3] = a03 * c - a23 * s;
554    out[8] = a00 * s + a20 * c;
555    out[9] = a01 * s + a21 * c;
556    out[10] = a02 * s + a22 * c;
557    out[11] = a03 * s + a23 * c;
558    return out;
559};
560
561/**
562 * Rotates a matrix by the given angle around the Z axis
563 *
564 * @param {mat4} out the receiving matrix
565 * @param {mat4} a the matrix to rotate
566 * @param {Number} rad the angle to rotate the matrix by
567 * @returns {mat4} out
568 */
569mat4.rotateZ = function (out, a, rad) {
570    var s = Math.sin(rad),
571        c = Math.cos(rad),
572        a00 = a[0],
573        a01 = a[1],
574        a02 = a[2],
575        a03 = a[3],
576        a10 = a[4],
577        a11 = a[5],
578        a12 = a[6],
579        a13 = a[7];
580
581    if (a !== out) { // If the source and destination differ, copy the unchanged last row
582        out[8]  = a[8];
583        out[9]  = a[9];
584        out[10] = a[10];
585        out[11] = a[11];
586        out[12] = a[12];
587        out[13] = a[13];
588        out[14] = a[14];
589        out[15] = a[15];
590    }
591
592    // Perform axis-specific matrix multiplication
593    out[0] = a00 * c + a10 * s;
594    out[1] = a01 * c + a11 * s;
595    out[2] = a02 * c + a12 * s;
596    out[3] = a03 * c + a13 * s;
597    out[4] = a10 * c - a00 * s;
598    out[5] = a11 * c - a01 * s;
599    out[6] = a12 * c - a02 * s;
600    out[7] = a13 * c - a03 * s;
601    return out;
602};
603
604/**
605 * Creates a matrix from a quaternion rotation and vector translation
606 * This is equivalent to (but much faster than):
607 *
608 *     mat4.identity(dest);
609 *     mat4.translate(dest, vec);
610 *     var quatMat = mat4.create();
611 *     quat4.toMat4(quat, quatMat);
612 *     mat4.multiply(dest, quatMat);
613 *
614 * @param {mat4} out mat4 receiving operation result
615 * @param {quat4} q Rotation quaternion
616 * @param {vec3} v Translation vector
617 * @returns {mat4} out
618 */
619mat4.fromRotationTranslation = function (out, q, v) {
620    // Quaternion math
621    var x = q[0], y = q[1], z = q[2], w = q[3],
622        x2 = x + x,
623        y2 = y + y,
624        z2 = z + z,
625
626        xx = x * x2,
627        xy = x * y2,
628        xz = x * z2,
629        yy = y * y2,
630        yz = y * z2,
631        zz = z * z2,
632        wx = w * x2,
633        wy = w * y2,
634        wz = w * z2;
635
636    out[0] = 1 - (yy + zz);
637    out[1] = xy + wz;
638    out[2] = xz - wy;
639    out[3] = 0;
640    out[4] = xy - wz;
641    out[5] = 1 - (xx + zz);
642    out[6] = yz + wx;
643    out[7] = 0;
644    out[8] = xz + wy;
645    out[9] = yz - wx;
646    out[10] = 1 - (xx + yy);
647    out[11] = 0;
648    out[12] = v[0];
649    out[13] = v[1];
650    out[14] = v[2];
651    out[15] = 1;
652
653    return out;
654};
655
656/**
657* Calculates a 4x4 matrix from the given quaternion
658*
659* @param {mat4} out mat4 receiving operation result
660* @param {quat} q Quaternion to create matrix from
661*
662* @returns {mat4} out
663*/
664mat4.fromQuat = function (out, q) {
665    var x = q[0], y = q[1], z = q[2], w = q[3],
666        x2 = x + x,
667        y2 = y + y,
668        z2 = z + z,
669
670        xx = x * x2,
671        xy = x * y2,
672        xz = x * z2,
673        yy = y * y2,
674        yz = y * z2,
675        zz = z * z2,
676        wx = w * x2,
677        wy = w * y2,
678        wz = w * z2;
679
680    out[0] = 1 - (yy + zz);
681    out[1] = xy + wz;
682    out[2] = xz - wy;
683    out[3] = 0;
684
685    out[4] = xy - wz;
686    out[5] = 1 - (xx + zz);
687    out[6] = yz + wx;
688    out[7] = 0;
689
690    out[8] = xz + wy;
691    out[9] = yz - wx;
692    out[10] = 1 - (xx + yy);
693    out[11] = 0;
694
695    out[12] = 0;
696    out[13] = 0;
697    out[14] = 0;
698    out[15] = 1;
699
700    return out;
701};
702
703/**
704 * Generates a frustum matrix with the given bounds
705 *
706 * @param {mat4} out mat4 frustum matrix will be written into
707 * @param {Number} left Left bound of the frustum
708 * @param {Number} right Right bound of the frustum
709 * @param {Number} bottom Bottom bound of the frustum
710 * @param {Number} top Top bound of the frustum
711 * @param {Number} near Near bound of the frustum
712 * @param {Number} far Far bound of the frustum
713 * @returns {mat4} out
714 */
715mat4.frustum = function (out, left, right, bottom, top, near, far) {
716    var rl = 1 / (right - left),
717        tb = 1 / (top - bottom),
718        nf = 1 / (near - far);
719    out[0] = (near * 2) * rl;
720    out[1] = 0;
721    out[2] = 0;
722    out[3] = 0;
723    out[4] = 0;
724    out[5] = (near * 2) * tb;
725    out[6] = 0;
726    out[7] = 0;
727    out[8] = (right + left) * rl;
728    out[9] = (top + bottom) * tb;
729    out[10] = (far + near) * nf;
730    out[11] = -1;
731    out[12] = 0;
732    out[13] = 0;
733    out[14] = (far * near * 2) * nf;
734    out[15] = 0;
735    return out;
736};
737
738/**
739 * Generates a perspective projection matrix with the given bounds
740 *
741 * @param {mat4} out mat4 frustum matrix will be written into
742 * @param {number} fovy Vertical field of view in radians
743 * @param {number} aspect Aspect ratio. typically viewport width/height
744 * @param {number} near Near bound of the frustum
745 * @param {number} far Far bound of the frustum
746 * @returns {mat4} out
747 */
748mat4.perspective = function (out, fovy, aspect, near, far) {
749    var f = 1.0 / Math.tan(fovy / 2),
750        nf = 1 / (near - far);
751    out[0] = f / aspect;
752    out[1] = 0;
753    out[2] = 0;
754    out[3] = 0;
755    out[4] = 0;
756    out[5] = f;
757    out[6] = 0;
758    out[7] = 0;
759    out[8] = 0;
760    out[9] = 0;
761    out[10] = (far + near) * nf;
762    out[11] = -1;
763    out[12] = 0;
764    out[13] = 0;
765    out[14] = (2 * far * near) * nf;
766    out[15] = 0;
767    return out;
768};
769
770/**
771 * Generates a orthogonal projection matrix with the given bounds
772 *
773 * @param {mat4} out mat4 frustum matrix will be written into
774 * @param {number} left Left bound of the frustum
775 * @param {number} right Right bound of the frustum
776 * @param {number} bottom Bottom bound of the frustum
777 * @param {number} top Top bound of the frustum
778 * @param {number} near Near bound of the frustum
779 * @param {number} far Far bound of the frustum
780 * @returns {mat4} out
781 */
782mat4.ortho = function (out, left, right, bottom, top, near, far) {
783    var lr = 1 / (left - right),
784        bt = 1 / (bottom - top),
785        nf = 1 / (near - far);
786    out[0] = -2 * lr;
787    out[1] = 0;
788    out[2] = 0;
789    out[3] = 0;
790    out[4] = 0;
791    out[5] = -2 * bt;
792    out[6] = 0;
793    out[7] = 0;
794    out[8] = 0;
795    out[9] = 0;
796    out[10] = 2 * nf;
797    out[11] = 0;
798    out[12] = (left + right) * lr;
799    out[13] = (top + bottom) * bt;
800    out[14] = (far + near) * nf;
801    out[15] = 1;
802    return out;
803};
804
805/**
806 * Generates a look-at matrix with the given eye position, focal point, and up axis
807 *
808 * @param {mat4} out mat4 frustum matrix will be written into
809 * @param {vec3} eye Position of the viewer
810 * @param {vec3} center Point the viewer is looking at
811 * @param {vec3} up vec3 pointing up
812 * @returns {mat4} out
813 */
814mat4.lookAt = function (out, eye, center, up) {
815    var x0, x1, x2, y0, y1, y2, z0, z1, z2, len,
816        eyex = eye[0],
817        eyey = eye[1],
818        eyez = eye[2],
819        upx = up[0],
820        upy = up[1],
821        upz = up[2],
822        centerx = center[0],
823        centery = center[1],
824        centerz = center[2];
825
826    if (Math.abs(eyex - centerx) < GLMAT_EPSILON &&
827        Math.abs(eyey - centery) < GLMAT_EPSILON &&
828        Math.abs(eyez - centerz) < GLMAT_EPSILON) {
829        return mat4.identity(out);
830    }
831
832    z0 = eyex - centerx;
833    z1 = eyey - centery;
834    z2 = eyez - centerz;
835
836    len = 1 / Math.sqrt(z0 * z0 + z1 * z1 + z2 * z2);
837    z0 *= len;
838    z1 *= len;
839    z2 *= len;
840
841    x0 = upy * z2 - upz * z1;
842    x1 = upz * z0 - upx * z2;
843    x2 = upx * z1 - upy * z0;
844    len = Math.sqrt(x0 * x0 + x1 * x1 + x2 * x2);
845    if (!len) {
846        x0 = 0;
847        x1 = 0;
848        x2 = 0;
849    } else {
850        len = 1 / len;
851        x0 *= len;
852        x1 *= len;
853        x2 *= len;
854    }
855
856    y0 = z1 * x2 - z2 * x1;
857    y1 = z2 * x0 - z0 * x2;
858    y2 = z0 * x1 - z1 * x0;
859
860    len = Math.sqrt(y0 * y0 + y1 * y1 + y2 * y2);
861    if (!len) {
862        y0 = 0;
863        y1 = 0;
864        y2 = 0;
865    } else {
866        len = 1 / len;
867        y0 *= len;
868        y1 *= len;
869        y2 *= len;
870    }
871
872    out[0] = x0;
873    out[1] = y0;
874    out[2] = z0;
875    out[3] = 0;
876    out[4] = x1;
877    out[5] = y1;
878    out[6] = z1;
879    out[7] = 0;
880    out[8] = x2;
881    out[9] = y2;
882    out[10] = z2;
883    out[11] = 0;
884    out[12] = -(x0 * eyex + x1 * eyey + x2 * eyez);
885    out[13] = -(y0 * eyex + y1 * eyey + y2 * eyez);
886    out[14] = -(z0 * eyex + z1 * eyey + z2 * eyez);
887    out[15] = 1;
888
889    return out;
890};
891
892/**
893 * Returns a string representation of a mat4
894 *
895 * @param {mat4} mat matrix to represent as a string
896 * @returns {String} string representation of the matrix
897 */
898mat4.str = function (a) {
899    return 'mat4(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + a[3] + ', ' +
900                    a[4] + ', ' + a[5] + ', ' + a[6] + ', ' + a[7] + ', ' +
901                    a[8] + ', ' + a[9] + ', ' + a[10] + ', ' + a[11] + ', ' +
902                    a[12] + ', ' + a[13] + ', ' + a[14] + ', ' + a[15] + ')';
903};
904
905if(typeof(exports) !== 'undefined') {
906    exports.mat4 = mat4;
907}
908