├── .gitignore ├── README.md ├── assets └── Item │ └── Objectcomponents │ └── weapon │ ├── axe_1h_blacksmithing_d_01.blp │ ├── axe_1h_blacksmithing_d_01.m2 │ ├── axe_1h_blacksmithing_d_01.png │ └── axe_1h_blacksmithing_d_0100.skin ├── font ├── buttons.eot ├── buttons.svg ├── buttons.ttf └── buttons.woff ├── images └── modelviewer.png ├── index.html └── scripts ├── gl-matrix.js ├── launcher.js ├── m2.js └── modelviewer.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | jsWoWModelViewer 2 | ================ 3 | 4 | Display World of Warcraft Models (M2) in browser. 5 | 6 | It uses [jBinary](https://github.com/jDataView/jBinary) library for loading & parsing binary data and WebGL for rendering. 7 | 8 | Demo: http://vjeux.github.io/jsWoWModelViewer/ 9 | 10 | [![](images/modelviewer.png)](http://vjeux.github.io/jsWoWModelViewer/) 11 | -------------------------------------------------------------------------------- /assets/Item/Objectcomponents/weapon/axe_1h_blacksmithing_d_01.blp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vjeux/jsWoWModelViewer/fd4f055c60f1c29b11b051727b4a902d32df003e/assets/Item/Objectcomponents/weapon/axe_1h_blacksmithing_d_01.blp -------------------------------------------------------------------------------- /assets/Item/Objectcomponents/weapon/axe_1h_blacksmithing_d_01.m2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vjeux/jsWoWModelViewer/fd4f055c60f1c29b11b051727b4a902d32df003e/assets/Item/Objectcomponents/weapon/axe_1h_blacksmithing_d_01.m2 -------------------------------------------------------------------------------- /assets/Item/Objectcomponents/weapon/axe_1h_blacksmithing_d_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vjeux/jsWoWModelViewer/fd4f055c60f1c29b11b051727b4a902d32df003e/assets/Item/Objectcomponents/weapon/axe_1h_blacksmithing_d_01.png -------------------------------------------------------------------------------- /assets/Item/Objectcomponents/weapon/axe_1h_blacksmithing_d_0100.skin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vjeux/jsWoWModelViewer/fd4f055c60f1c29b11b051727b4a902d32df003e/assets/Item/Objectcomponents/weapon/axe_1h_blacksmithing_d_0100.skin -------------------------------------------------------------------------------- /font/buttons.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vjeux/jsWoWModelViewer/fd4f055c60f1c29b11b051727b4a902d32df003e/font/buttons.eot -------------------------------------------------------------------------------- /font/buttons.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Created by FontForge 20100429 at Wed Nov 7 16:15:14 2012 6 | By root 7 | Copyright (C) 2012 by original authors @ fontello.com 8 | 9 | 10 | 11 | 24 | 26 | 28 | 30 | 32 | 34 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /font/buttons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vjeux/jsWoWModelViewer/fd4f055c60f1c29b11b051727b4a902d32df003e/font/buttons.ttf -------------------------------------------------------------------------------- /font/buttons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vjeux/jsWoWModelViewer/fd4f055c60f1c29b11b051727b4a902d32df003e/font/buttons.woff -------------------------------------------------------------------------------- /images/modelviewer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vjeux/jsWoWModelViewer/fd4f055c60f1c29b11b051727b4a902d32df003e/images/modelviewer.png -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | JS WoW Model Viewer 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 36 | 37 | 51 | 52 | 124 | 125 | 126 | 127 | 128 |
Use your arrow keys and mouse for changing angles.
129 | Fork me on GitHub 130 | 131 | 132 | 133 | -------------------------------------------------------------------------------- /scripts/gl-matrix.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview gl-matrix - High performance matrix and vector operations for WebGL 3 | * @author Brandon Jones 4 | * @author Colin MacKenzie IV 5 | * @version 1.3.7 6 | */ 7 | 8 | /* 9 | * Copyright (c) 2012 Brandon Jones, Colin MacKenzie IV 10 | * 11 | * This software is provided 'as-is', without any express or implied 12 | * warranty. In no event will the authors be held liable for any damages 13 | * arising from the use of this software. 14 | * 15 | * Permission is granted to anyone to use this software for any purpose, 16 | * including commercial applications, and to alter it and redistribute it 17 | * freely, subject to the following restrictions: 18 | * 19 | * 1. The origin of this software must not be misrepresented; you must not 20 | * claim that you wrote the original software. If you use this software 21 | * in a product, an acknowledgment in the product documentation would be 22 | * appreciated but is not required. 23 | * 24 | * 2. Altered source versions must be plainly marked as such, and must not 25 | * be misrepresented as being the original software. 26 | * 27 | * 3. This notice may not be removed or altered from any source 28 | * distribution. 29 | */ 30 | 31 | // Updated to use a modification of the "returnExportsGlobal" pattern from https://github.com/umdjs/umd 32 | 33 | (function (root, factory) { 34 | if (typeof exports === 'object') { 35 | // Node. Does not work with strict CommonJS, but 36 | // only CommonJS-like enviroments that support module.exports, 37 | // like Node. 38 | module.exports = factory(global); 39 | } else if (typeof define === 'function' && define.amd) { 40 | // AMD. Register as an anonymous module. 41 | define([], function () { 42 | return factory(root); 43 | }); 44 | } else { 45 | // Browser globals 46 | factory(root); 47 | } 48 | }(this, function (root) { 49 | "use strict"; 50 | 51 | // Tweak to your liking 52 | var FLOAT_EPSILON = 0.000001; 53 | 54 | var glMath = {}; 55 | (function() { 56 | if (typeof(Float32Array) != 'undefined') { 57 | var y = new Float32Array(1); 58 | var i = new Int32Array(y.buffer); 59 | 60 | /** 61 | * Fast way to calculate the inverse square root, 62 | * see http://jsperf.com/inverse-square-root/5 63 | * 64 | * If typed arrays are not available, a slower 65 | * implementation will be used. 66 | * 67 | * @param {Number} number the number 68 | * @returns {Number} Inverse square root 69 | */ 70 | glMath.invsqrt = function(number) { 71 | var x2 = number * 0.5; 72 | y[0] = number; 73 | var threehalfs = 1.5; 74 | 75 | i[0] = 0x5f3759df - (i[0] >> 1); 76 | 77 | var number2 = y[0]; 78 | 79 | return number2 * (threehalfs - (x2 * number2 * number2)); 80 | }; 81 | } else { 82 | glMath.invsqrt = function(number) { return 1.0 / Math.sqrt(number); }; 83 | } 84 | })(); 85 | 86 | /** 87 | * @class System-specific optimal array type 88 | * @name MatrixArray 89 | */ 90 | var MatrixArray = null; 91 | 92 | // explicitly sets and returns the type of array to use within glMatrix 93 | function setMatrixArrayType(type) { 94 | MatrixArray = type; 95 | return MatrixArray; 96 | } 97 | 98 | // auto-detects and returns the best type of array to use within glMatrix, falling 99 | // back to Array if typed arrays are unsupported 100 | function determineMatrixArrayType() { 101 | MatrixArray = (typeof Float32Array !== 'undefined') ? Float32Array : Array; 102 | return MatrixArray; 103 | } 104 | 105 | determineMatrixArrayType(); 106 | 107 | /** 108 | * @class 3 Dimensional Vector 109 | * @name vec3 110 | */ 111 | var vec3 = {}; 112 | 113 | /** 114 | * Creates a new instance of a vec3 using the default array type 115 | * Any javascript array-like objects containing at least 3 numeric elements can serve as a vec3 116 | * 117 | * @param {vec3} [vec] vec3 containing values to initialize with 118 | * 119 | * @returns {vec3} New vec3 120 | */ 121 | vec3.create = function (vec) { 122 | var dest = new MatrixArray(3); 123 | 124 | if (vec) { 125 | dest[0] = vec[0]; 126 | dest[1] = vec[1]; 127 | dest[2] = vec[2]; 128 | } else { 129 | dest[0] = dest[1] = dest[2] = 0; 130 | } 131 | 132 | return dest; 133 | }; 134 | 135 | /** 136 | * Creates a new instance of a vec3, initializing it with the given arguments 137 | * 138 | * @param {number} x X value 139 | * @param {number} y Y value 140 | * @param {number} z Z value 141 | 142 | * @returns {vec3} New vec3 143 | */ 144 | vec3.createFrom = function (x, y, z) { 145 | var dest = new MatrixArray(3); 146 | 147 | dest[0] = x; 148 | dest[1] = y; 149 | dest[2] = z; 150 | 151 | return dest; 152 | }; 153 | 154 | /** 155 | * Copies the values of one vec3 to another 156 | * 157 | * @param {vec3} vec vec3 containing values to copy 158 | * @param {vec3} dest vec3 receiving copied values 159 | * 160 | * @returns {vec3} dest 161 | */ 162 | vec3.set = function (vec, dest) { 163 | dest[0] = vec[0]; 164 | dest[1] = vec[1]; 165 | dest[2] = vec[2]; 166 | 167 | return dest; 168 | }; 169 | 170 | /** 171 | * Compares two vectors for equality within a certain margin of error 172 | * 173 | * @param {vec3} a First vector 174 | * @param {vec3} b Second vector 175 | * 176 | * @returns {Boolean} True if a is equivalent to b 177 | */ 178 | vec3.equal = function (a, b) { 179 | return a === b || ( 180 | Math.abs(a[0] - b[0]) < FLOAT_EPSILON && 181 | Math.abs(a[1] - b[1]) < FLOAT_EPSILON && 182 | Math.abs(a[2] - b[2]) < FLOAT_EPSILON 183 | ); 184 | }; 185 | 186 | /** 187 | * Performs a vector addition 188 | * 189 | * @param {vec3} vec First operand 190 | * @param {vec3} vec2 Second operand 191 | * @param {vec3} [dest] vec3 receiving operation result. If not specified result is written to vec 192 | * 193 | * @returns {vec3} dest if specified, vec otherwise 194 | */ 195 | vec3.add = function (vec, vec2, dest) { 196 | if (!dest || vec === dest) { 197 | vec[0] += vec2[0]; 198 | vec[1] += vec2[1]; 199 | vec[2] += vec2[2]; 200 | return vec; 201 | } 202 | 203 | dest[0] = vec[0] + vec2[0]; 204 | dest[1] = vec[1] + vec2[1]; 205 | dest[2] = vec[2] + vec2[2]; 206 | return dest; 207 | }; 208 | 209 | /** 210 | * Performs a vector subtraction 211 | * 212 | * @param {vec3} vec First operand 213 | * @param {vec3} vec2 Second operand 214 | * @param {vec3} [dest] vec3 receiving operation result. If not specified result is written to vec 215 | * 216 | * @returns {vec3} dest if specified, vec otherwise 217 | */ 218 | vec3.subtract = function (vec, vec2, dest) { 219 | if (!dest || vec === dest) { 220 | vec[0] -= vec2[0]; 221 | vec[1] -= vec2[1]; 222 | vec[2] -= vec2[2]; 223 | return vec; 224 | } 225 | 226 | dest[0] = vec[0] - vec2[0]; 227 | dest[1] = vec[1] - vec2[1]; 228 | dest[2] = vec[2] - vec2[2]; 229 | return dest; 230 | }; 231 | 232 | /** 233 | * Performs a vector multiplication 234 | * 235 | * @param {vec3} vec First operand 236 | * @param {vec3} vec2 Second operand 237 | * @param {vec3} [dest] vec3 receiving operation result. If not specified result is written to vec 238 | * 239 | * @returns {vec3} dest if specified, vec otherwise 240 | */ 241 | vec3.multiply = function (vec, vec2, dest) { 242 | if (!dest || vec === dest) { 243 | vec[0] *= vec2[0]; 244 | vec[1] *= vec2[1]; 245 | vec[2] *= vec2[2]; 246 | return vec; 247 | } 248 | 249 | dest[0] = vec[0] * vec2[0]; 250 | dest[1] = vec[1] * vec2[1]; 251 | dest[2] = vec[2] * vec2[2]; 252 | return dest; 253 | }; 254 | 255 | /** 256 | * Negates the components of a vec3 257 | * 258 | * @param {vec3} vec vec3 to negate 259 | * @param {vec3} [dest] vec3 receiving operation result. If not specified result is written to vec 260 | * 261 | * @returns {vec3} dest if specified, vec otherwise 262 | */ 263 | vec3.negate = function (vec, dest) { 264 | if (!dest) { dest = vec; } 265 | 266 | dest[0] = -vec[0]; 267 | dest[1] = -vec[1]; 268 | dest[2] = -vec[2]; 269 | return dest; 270 | }; 271 | 272 | /** 273 | * Multiplies the components of a vec3 by a scalar value 274 | * 275 | * @param {vec3} vec vec3 to scale 276 | * @param {number} val Value to scale by 277 | * @param {vec3} [dest] vec3 receiving operation result. If not specified result is written to vec 278 | * 279 | * @returns {vec3} dest if specified, vec otherwise 280 | */ 281 | vec3.scale = function (vec, val, dest) { 282 | if (!dest || vec === dest) { 283 | vec[0] *= val; 284 | vec[1] *= val; 285 | vec[2] *= val; 286 | return vec; 287 | } 288 | 289 | dest[0] = vec[0] * val; 290 | dest[1] = vec[1] * val; 291 | dest[2] = vec[2] * val; 292 | return dest; 293 | }; 294 | 295 | /** 296 | * Generates a unit vector of the same direction as the provided vec3 297 | * If vector length is 0, returns [0, 0, 0] 298 | * 299 | * @param {vec3} vec vec3 to normalize 300 | * @param {vec3} [dest] vec3 receiving operation result. If not specified result is written to vec 301 | * 302 | * @returns {vec3} dest if specified, vec otherwise 303 | */ 304 | vec3.normalize = function (vec, dest) { 305 | if (!dest) { dest = vec; } 306 | 307 | var x = vec[0], y = vec[1], z = vec[2], 308 | len = Math.sqrt(x * x + y * y + z * z); 309 | 310 | if (!len) { 311 | dest[0] = 0; 312 | dest[1] = 0; 313 | dest[2] = 0; 314 | return dest; 315 | } else if (len === 1) { 316 | dest[0] = x; 317 | dest[1] = y; 318 | dest[2] = z; 319 | return dest; 320 | } 321 | 322 | len = 1 / len; 323 | dest[0] = x * len; 324 | dest[1] = y * len; 325 | dest[2] = z * len; 326 | return dest; 327 | }; 328 | 329 | /** 330 | * Generates the cross product of two vec3s 331 | * 332 | * @param {vec3} vec First operand 333 | * @param {vec3} vec2 Second operand 334 | * @param {vec3} [dest] vec3 receiving operation result. If not specified result is written to vec 335 | * 336 | * @returns {vec3} dest if specified, vec otherwise 337 | */ 338 | vec3.cross = function (vec, vec2, dest) { 339 | if (!dest) { dest = vec; } 340 | 341 | var x = vec[0], y = vec[1], z = vec[2], 342 | x2 = vec2[0], y2 = vec2[1], z2 = vec2[2]; 343 | 344 | dest[0] = y * z2 - z * y2; 345 | dest[1] = z * x2 - x * z2; 346 | dest[2] = x * y2 - y * x2; 347 | return dest; 348 | }; 349 | 350 | /** 351 | * Caclulates the length of a vec3 352 | * 353 | * @param {vec3} vec vec3 to calculate length of 354 | * 355 | * @returns {number} Length of vec 356 | */ 357 | vec3.length = function (vec) { 358 | var x = vec[0], y = vec[1], z = vec[2]; 359 | return Math.sqrt(x * x + y * y + z * z); 360 | }; 361 | 362 | /** 363 | * Caclulates the squared length of a vec3 364 | * 365 | * @param {vec3} vec vec3 to calculate squared length of 366 | * 367 | * @returns {number} Squared Length of vec 368 | */ 369 | vec3.squaredLength = function (vec) { 370 | var x = vec[0], y = vec[1], z = vec[2]; 371 | return x * x + y * y + z * z; 372 | }; 373 | 374 | /** 375 | * Caclulates the dot product of two vec3s 376 | * 377 | * @param {vec3} vec First operand 378 | * @param {vec3} vec2 Second operand 379 | * 380 | * @returns {number} Dot product of vec and vec2 381 | */ 382 | vec3.dot = function (vec, vec2) { 383 | return vec[0] * vec2[0] + vec[1] * vec2[1] + vec[2] * vec2[2]; 384 | }; 385 | 386 | /** 387 | * Generates a unit vector pointing from one vector to another 388 | * 389 | * @param {vec3} vec Origin vec3 390 | * @param {vec3} vec2 vec3 to point to 391 | * @param {vec3} [dest] vec3 receiving operation result. If not specified result is written to vec 392 | * 393 | * @returns {vec3} dest if specified, vec otherwise 394 | */ 395 | vec3.direction = function (vec, vec2, dest) { 396 | if (!dest) { dest = vec; } 397 | 398 | var x = vec[0] - vec2[0], 399 | y = vec[1] - vec2[1], 400 | z = vec[2] - vec2[2], 401 | len = Math.sqrt(x * x + y * y + z * z); 402 | 403 | if (!len) { 404 | dest[0] = 0; 405 | dest[1] = 0; 406 | dest[2] = 0; 407 | return dest; 408 | } 409 | 410 | len = 1 / len; 411 | dest[0] = x * len; 412 | dest[1] = y * len; 413 | dest[2] = z * len; 414 | return dest; 415 | }; 416 | 417 | /** 418 | * Performs a linear interpolation between two vec3 419 | * 420 | * @param {vec3} vec First vector 421 | * @param {vec3} vec2 Second vector 422 | * @param {number} lerp Interpolation amount between the two inputs 423 | * @param {vec3} [dest] vec3 receiving operation result. If not specified result is written to vec 424 | * 425 | * @returns {vec3} dest if specified, vec otherwise 426 | */ 427 | vec3.lerp = function (vec, vec2, lerp, dest) { 428 | if (!dest) { dest = vec; } 429 | 430 | dest[0] = vec[0] + lerp * (vec2[0] - vec[0]); 431 | dest[1] = vec[1] + lerp * (vec2[1] - vec[1]); 432 | dest[2] = vec[2] + lerp * (vec2[2] - vec[2]); 433 | 434 | return dest; 435 | }; 436 | 437 | /** 438 | * Calculates the euclidian distance between two vec3 439 | * 440 | * Params: 441 | * @param {vec3} vec First vector 442 | * @param {vec3} vec2 Second vector 443 | * 444 | * @returns {number} Distance between vec and vec2 445 | */ 446 | vec3.dist = function (vec, vec2) { 447 | var x = vec2[0] - vec[0], 448 | y = vec2[1] - vec[1], 449 | z = vec2[2] - vec[2]; 450 | 451 | return Math.sqrt(x*x + y*y + z*z); 452 | }; 453 | 454 | // Pre-allocated to prevent unecessary garbage collection 455 | var unprojectMat = null; 456 | var unprojectVec = new MatrixArray(4); 457 | /** 458 | * Projects the specified vec3 from screen space into object space 459 | * Based on the Mesa gluUnProject implementation 460 | * 461 | * @param {vec3} vec Screen-space vector to project 462 | * @param {mat4} view View matrix 463 | * @param {mat4} proj Projection matrix 464 | * @param {vec4} viewport Viewport as given to gl.viewport [x, y, width, height] 465 | * @param {vec3} [dest] vec3 receiving unprojected result. If not specified result is written to vec 466 | * 467 | * @returns {vec3} dest if specified, vec otherwise 468 | */ 469 | vec3.unproject = function (vec, view, proj, viewport, dest) { 470 | if (!dest) { dest = vec; } 471 | 472 | if(!unprojectMat) { 473 | unprojectMat = mat4.create(); 474 | } 475 | 476 | var m = unprojectMat; 477 | var v = unprojectVec; 478 | 479 | v[0] = (vec[0] - viewport[0]) * 2.0 / viewport[2] - 1.0; 480 | v[1] = (vec[1] - viewport[1]) * 2.0 / viewport[3] - 1.0; 481 | v[2] = 2.0 * vec[2] - 1.0; 482 | v[3] = 1.0; 483 | 484 | mat4.multiply(proj, view, m); 485 | if(!mat4.inverse(m)) { return null; } 486 | 487 | mat4.multiplyVec4(m, v); 488 | if(v[3] === 0.0) { return null; } 489 | 490 | dest[0] = v[0] / v[3]; 491 | dest[1] = v[1] / v[3]; 492 | dest[2] = v[2] / v[3]; 493 | 494 | return dest; 495 | }; 496 | 497 | var xUnitVec3 = vec3.createFrom(1,0,0); 498 | var yUnitVec3 = vec3.createFrom(0,1,0); 499 | var zUnitVec3 = vec3.createFrom(0,0,1); 500 | 501 | var tmpvec3 = vec3.create(); 502 | /** 503 | * Generates a quaternion of rotation between two given normalized vectors 504 | * 505 | * @param {vec3} a Normalized source vector 506 | * @param {vec3} b Normalized target vector 507 | * @param {quat4} [dest] quat4 receiving operation result. 508 | * 509 | * @returns {quat4} dest if specified, a new quat4 otherwise 510 | */ 511 | vec3.rotationTo = function (a, b, dest) { 512 | if (!dest) { dest = quat4.create(); } 513 | 514 | var d = vec3.dot(a, b); 515 | var axis = tmpvec3; 516 | if (d >= 1.0) { 517 | quat4.set(identityQuat4, dest); 518 | } else if (d < (0.000001 - 1.0)) { 519 | vec3.cross(xUnitVec3, a, axis); 520 | if (vec3.length(axis) < 0.000001) 521 | vec3.cross(yUnitVec3, a, axis); 522 | if (vec3.length(axis) < 0.000001) 523 | vec3.cross(zUnitVec3, a, axis); 524 | vec3.normalize(axis); 525 | quat4.fromAngleAxis(Math.PI, axis, dest); 526 | } else { 527 | var s = Math.sqrt((1.0 + d) * 2.0); 528 | var sInv = 1.0 / s; 529 | vec3.cross(a, b, axis); 530 | dest[0] = axis[0] * sInv; 531 | dest[1] = axis[1] * sInv; 532 | dest[2] = axis[2] * sInv; 533 | dest[3] = s * 0.5; 534 | quat4.normalize(dest); 535 | } 536 | if (dest[3] > 1.0) dest[3] = 1.0; 537 | else if (dest[3] < -1.0) dest[3] = -1.0; 538 | return dest; 539 | }; 540 | 541 | /** 542 | * Returns a string representation of a vector 543 | * 544 | * @param {vec3} vec Vector to represent as a string 545 | * 546 | * @returns {string} String representation of vec 547 | */ 548 | vec3.str = function (vec) { 549 | return '[' + vec[0] + ', ' + vec[1] + ', ' + vec[2] + ']'; 550 | }; 551 | 552 | /** 553 | * @class 3x3 Matrix 554 | * @name mat3 555 | */ 556 | var mat3 = {}; 557 | 558 | /** 559 | * Creates a new instance of a mat3 using the default array type 560 | * Any javascript array-like object containing at least 9 numeric elements can serve as a mat3 561 | * 562 | * @param {mat3} [mat] mat3 containing values to initialize with 563 | * 564 | * @returns {mat3} New mat3 565 | */ 566 | mat3.create = function (mat) { 567 | var dest = new MatrixArray(9); 568 | 569 | if (mat) { 570 | dest[0] = mat[0]; 571 | dest[1] = mat[1]; 572 | dest[2] = mat[2]; 573 | dest[3] = mat[3]; 574 | dest[4] = mat[4]; 575 | dest[5] = mat[5]; 576 | dest[6] = mat[6]; 577 | dest[7] = mat[7]; 578 | dest[8] = mat[8]; 579 | } else { 580 | dest[0] = dest[1] = 581 | dest[2] = dest[3] = 582 | dest[4] = dest[5] = 583 | dest[6] = dest[7] = 584 | dest[8] = 0; 585 | } 586 | 587 | return dest; 588 | }; 589 | 590 | /** 591 | * Creates a new instance of a mat3, initializing it with the given arguments 592 | * 593 | * @param {number} m00 594 | * @param {number} m01 595 | * @param {number} m02 596 | * @param {number} m10 597 | * @param {number} m11 598 | * @param {number} m12 599 | * @param {number} m20 600 | * @param {number} m21 601 | * @param {number} m22 602 | 603 | * @returns {mat3} New mat3 604 | */ 605 | mat3.createFrom = function (m00, m01, m02, m10, m11, m12, m20, m21, m22) { 606 | var dest = new MatrixArray(9); 607 | 608 | dest[0] = m00; 609 | dest[1] = m01; 610 | dest[2] = m02; 611 | dest[3] = m10; 612 | dest[4] = m11; 613 | dest[5] = m12; 614 | dest[6] = m20; 615 | dest[7] = m21; 616 | dest[8] = m22; 617 | 618 | return dest; 619 | }; 620 | 621 | /** 622 | * Calculates the determinant of a mat3 623 | * 624 | * @param {mat3} mat mat3 to calculate determinant of 625 | * 626 | * @returns {Number} determinant of mat 627 | */ 628 | mat3.determinant = function (mat) { 629 | var a00 = mat[0], a01 = mat[1], a02 = mat[2], 630 | a10 = mat[3], a11 = mat[4], a12 = mat[5], 631 | a20 = mat[6], a21 = mat[7], a22 = mat[8]; 632 | 633 | return a00 * (a22 * a11 - a12 * a21) + a01 * (-a22 * a10 + a12 * a20) + a02 * (a21 * a10 - a11 * a20); 634 | }; 635 | 636 | /** 637 | * Calculates the inverse matrix of a mat3 638 | * 639 | * @param {mat3} mat mat3 to calculate inverse of 640 | * @param {mat3} [dest] mat3 receiving inverse matrix. If not specified result is written to mat 641 | * 642 | * @param {mat3} dest is specified, mat otherwise, null if matrix cannot be inverted 643 | */ 644 | mat3.inverse = function (mat, dest) { 645 | var a00 = mat[0], a01 = mat[1], a02 = mat[2], 646 | a10 = mat[3], a11 = mat[4], a12 = mat[5], 647 | a20 = mat[6], a21 = mat[7], a22 = mat[8], 648 | 649 | b01 = a22 * a11 - a12 * a21, 650 | b11 = -a22 * a10 + a12 * a20, 651 | b21 = a21 * a10 - a11 * a20, 652 | 653 | d = a00 * b01 + a01 * b11 + a02 * b21, 654 | id; 655 | 656 | if (!d) { return null; } 657 | id = 1 / d; 658 | 659 | if (!dest) { dest = mat3.create(); } 660 | 661 | dest[0] = b01 * id; 662 | dest[1] = (-a22 * a01 + a02 * a21) * id; 663 | dest[2] = (a12 * a01 - a02 * a11) * id; 664 | dest[3] = b11 * id; 665 | dest[4] = (a22 * a00 - a02 * a20) * id; 666 | dest[5] = (-a12 * a00 + a02 * a10) * id; 667 | dest[6] = b21 * id; 668 | dest[7] = (-a21 * a00 + a01 * a20) * id; 669 | dest[8] = (a11 * a00 - a01 * a10) * id; 670 | return dest; 671 | }; 672 | 673 | /** 674 | * Performs a matrix multiplication 675 | * 676 | * @param {mat3} mat First operand 677 | * @param {mat3} mat2 Second operand 678 | * @param {mat3} [dest] mat3 receiving operation result. If not specified result is written to mat 679 | * 680 | * @returns {mat3} dest if specified, mat otherwise 681 | */ 682 | mat3.multiply = function (mat, mat2, dest) { 683 | if (!dest) { dest = mat; } 684 | 685 | 686 | // Cache the matrix values (makes for huge speed increases!) 687 | var a00 = mat[0], a01 = mat[1], a02 = mat[2], 688 | a10 = mat[3], a11 = mat[4], a12 = mat[5], 689 | a20 = mat[6], a21 = mat[7], a22 = mat[8], 690 | 691 | b00 = mat2[0], b01 = mat2[1], b02 = mat2[2], 692 | b10 = mat2[3], b11 = mat2[4], b12 = mat2[5], 693 | b20 = mat2[6], b21 = mat2[7], b22 = mat2[8]; 694 | 695 | dest[0] = b00 * a00 + b01 * a10 + b02 * a20; 696 | dest[1] = b00 * a01 + b01 * a11 + b02 * a21; 697 | dest[2] = b00 * a02 + b01 * a12 + b02 * a22; 698 | 699 | dest[3] = b10 * a00 + b11 * a10 + b12 * a20; 700 | dest[4] = b10 * a01 + b11 * a11 + b12 * a21; 701 | dest[5] = b10 * a02 + b11 * a12 + b12 * a22; 702 | 703 | dest[6] = b20 * a00 + b21 * a10 + b22 * a20; 704 | dest[7] = b20 * a01 + b21 * a11 + b22 * a21; 705 | dest[8] = b20 * a02 + b21 * a12 + b22 * a22; 706 | 707 | return dest; 708 | }; 709 | 710 | /** 711 | * Transforms the vec2 according to the given mat3. 712 | * 713 | * @param {mat3} matrix mat3 to multiply against 714 | * @param {vec2} vec the vector to multiply 715 | * @param {vec2} [dest] an optional receiving vector. If not given, vec is used. 716 | * 717 | * @returns {vec2} The multiplication result 718 | **/ 719 | mat3.multiplyVec2 = function(matrix, vec, dest) { 720 | if (!dest) dest = vec; 721 | var x = vec[0], y = vec[1]; 722 | dest[0] = x * matrix[0] + y * matrix[3] + matrix[6]; 723 | dest[1] = x * matrix[1] + y * matrix[4] + matrix[7]; 724 | return dest; 725 | }; 726 | 727 | /** 728 | * Transforms the vec3 according to the given mat3 729 | * 730 | * @param {mat3} matrix mat3 to multiply against 731 | * @param {vec3} vec the vector to multiply 732 | * @param {vec3} [dest] an optional receiving vector. If not given, vec is used. 733 | * 734 | * @returns {vec3} The multiplication result 735 | **/ 736 | mat3.multiplyVec3 = function(matrix, vec, dest) { 737 | if (!dest) dest = vec; 738 | var x = vec[0], y = vec[1], z = vec[2]; 739 | dest[0] = x * matrix[0] + y * matrix[3] + z * matrix[6]; 740 | dest[1] = x * matrix[1] + y * matrix[4] + z * matrix[7]; 741 | dest[2] = x * matrix[2] + y * matrix[5] + z * matrix[8]; 742 | 743 | return dest; 744 | }; 745 | 746 | /** 747 | * Copies the values of one mat3 to another 748 | * 749 | * @param {mat3} mat mat3 containing values to copy 750 | * @param {mat3} dest mat3 receiving copied values 751 | * 752 | * @returns {mat3} dest 753 | */ 754 | mat3.set = function (mat, dest) { 755 | dest[0] = mat[0]; 756 | dest[1] = mat[1]; 757 | dest[2] = mat[2]; 758 | dest[3] = mat[3]; 759 | dest[4] = mat[4]; 760 | dest[5] = mat[5]; 761 | dest[6] = mat[6]; 762 | dest[7] = mat[7]; 763 | dest[8] = mat[8]; 764 | return dest; 765 | }; 766 | 767 | /** 768 | * Compares two matrices for equality within a certain margin of error 769 | * 770 | * @param {mat3} a First matrix 771 | * @param {mat3} b Second matrix 772 | * 773 | * @returns {Boolean} True if a is equivalent to b 774 | */ 775 | mat3.equal = function (a, b) { 776 | return a === b || ( 777 | Math.abs(a[0] - b[0]) < FLOAT_EPSILON && 778 | Math.abs(a[1] - b[1]) < FLOAT_EPSILON && 779 | Math.abs(a[2] - b[2]) < FLOAT_EPSILON && 780 | Math.abs(a[3] - b[3]) < FLOAT_EPSILON && 781 | Math.abs(a[4] - b[4]) < FLOAT_EPSILON && 782 | Math.abs(a[5] - b[5]) < FLOAT_EPSILON && 783 | Math.abs(a[6] - b[6]) < FLOAT_EPSILON && 784 | Math.abs(a[7] - b[7]) < FLOAT_EPSILON && 785 | Math.abs(a[8] - b[8]) < FLOAT_EPSILON 786 | ); 787 | }; 788 | 789 | /** 790 | * Sets a mat3 to an identity matrix 791 | * 792 | * @param {mat3} dest mat3 to set 793 | * 794 | * @returns dest if specified, otherwise a new mat3 795 | */ 796 | mat3.identity = function (dest) { 797 | if (!dest) { dest = mat3.create(); } 798 | dest[0] = 1; 799 | dest[1] = 0; 800 | dest[2] = 0; 801 | dest[3] = 0; 802 | dest[4] = 1; 803 | dest[5] = 0; 804 | dest[6] = 0; 805 | dest[7] = 0; 806 | dest[8] = 1; 807 | return dest; 808 | }; 809 | 810 | /** 811 | * Transposes a mat3 (flips the values over the diagonal) 812 | * 813 | * Params: 814 | * @param {mat3} mat mat3 to transpose 815 | * @param {mat3} [dest] mat3 receiving transposed values. If not specified result is written to mat 816 | * 817 | * @returns {mat3} dest is specified, mat otherwise 818 | */ 819 | mat3.transpose = function (mat, dest) { 820 | // If we are transposing ourselves we can skip a few steps but have to cache some values 821 | if (!dest || mat === dest) { 822 | var a01 = mat[1], a02 = mat[2], 823 | a12 = mat[5]; 824 | 825 | mat[1] = mat[3]; 826 | mat[2] = mat[6]; 827 | mat[3] = a01; 828 | mat[5] = mat[7]; 829 | mat[6] = a02; 830 | mat[7] = a12; 831 | return mat; 832 | } 833 | 834 | dest[0] = mat[0]; 835 | dest[1] = mat[3]; 836 | dest[2] = mat[6]; 837 | dest[3] = mat[1]; 838 | dest[4] = mat[4]; 839 | dest[5] = mat[7]; 840 | dest[6] = mat[2]; 841 | dest[7] = mat[5]; 842 | dest[8] = mat[8]; 843 | return dest; 844 | }; 845 | 846 | /** 847 | * Copies the elements of a mat3 into the upper 3x3 elements of a mat4 848 | * 849 | * @param {mat3} mat mat3 containing values to copy 850 | * @param {mat4} [dest] mat4 receiving copied values 851 | * 852 | * @returns {mat4} dest if specified, a new mat4 otherwise 853 | */ 854 | mat3.toMat4 = function (mat, dest) { 855 | if (!dest) { dest = mat4.create(); } 856 | 857 | dest[15] = 1; 858 | dest[14] = 0; 859 | dest[13] = 0; 860 | dest[12] = 0; 861 | 862 | dest[11] = 0; 863 | dest[10] = mat[8]; 864 | dest[9] = mat[7]; 865 | dest[8] = mat[6]; 866 | 867 | dest[7] = 0; 868 | dest[6] = mat[5]; 869 | dest[5] = mat[4]; 870 | dest[4] = mat[3]; 871 | 872 | dest[3] = 0; 873 | dest[2] = mat[2]; 874 | dest[1] = mat[1]; 875 | dest[0] = mat[0]; 876 | 877 | return dest; 878 | }; 879 | 880 | /** 881 | * Returns a string representation of a mat3 882 | * 883 | * @param {mat3} mat mat3 to represent as a string 884 | * 885 | * @param {string} String representation of mat 886 | */ 887 | mat3.str = function (mat) { 888 | return '[' + mat[0] + ', ' + mat[1] + ', ' + mat[2] + 889 | ', ' + mat[3] + ', ' + mat[4] + ', ' + mat[5] + 890 | ', ' + mat[6] + ', ' + mat[7] + ', ' + mat[8] + ']'; 891 | }; 892 | 893 | /** 894 | * @class 4x4 Matrix 895 | * @name mat4 896 | */ 897 | var mat4 = {}; 898 | 899 | /** 900 | * Creates a new instance of a mat4 using the default array type 901 | * Any javascript array-like object containing at least 16 numeric elements can serve as a mat4 902 | * 903 | * @param {mat4} [mat] mat4 containing values to initialize with 904 | * 905 | * @returns {mat4} New mat4 906 | */ 907 | mat4.create = function (mat) { 908 | var dest = new MatrixArray(16); 909 | 910 | if (mat) { 911 | dest[0] = mat[0]; 912 | dest[1] = mat[1]; 913 | dest[2] = mat[2]; 914 | dest[3] = mat[3]; 915 | dest[4] = mat[4]; 916 | dest[5] = mat[5]; 917 | dest[6] = mat[6]; 918 | dest[7] = mat[7]; 919 | dest[8] = mat[8]; 920 | dest[9] = mat[9]; 921 | dest[10] = mat[10]; 922 | dest[11] = mat[11]; 923 | dest[12] = mat[12]; 924 | dest[13] = mat[13]; 925 | dest[14] = mat[14]; 926 | dest[15] = mat[15]; 927 | } 928 | 929 | return dest; 930 | }; 931 | 932 | /** 933 | * Creates a new instance of a mat4, initializing it with the given arguments 934 | * 935 | * @param {number} m00 936 | * @param {number} m01 937 | * @param {number} m02 938 | * @param {number} m03 939 | * @param {number} m10 940 | * @param {number} m11 941 | * @param {number} m12 942 | * @param {number} m13 943 | * @param {number} m20 944 | * @param {number} m21 945 | * @param {number} m22 946 | * @param {number} m23 947 | * @param {number} m30 948 | * @param {number} m31 949 | * @param {number} m32 950 | * @param {number} m33 951 | 952 | * @returns {mat4} New mat4 953 | */ 954 | mat4.createFrom = function (m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23, m30, m31, m32, m33) { 955 | var dest = new MatrixArray(16); 956 | 957 | dest[0] = m00; 958 | dest[1] = m01; 959 | dest[2] = m02; 960 | dest[3] = m03; 961 | dest[4] = m10; 962 | dest[5] = m11; 963 | dest[6] = m12; 964 | dest[7] = m13; 965 | dest[8] = m20; 966 | dest[9] = m21; 967 | dest[10] = m22; 968 | dest[11] = m23; 969 | dest[12] = m30; 970 | dest[13] = m31; 971 | dest[14] = m32; 972 | dest[15] = m33; 973 | 974 | return dest; 975 | }; 976 | 977 | /** 978 | * Copies the values of one mat4 to another 979 | * 980 | * @param {mat4} mat mat4 containing values to copy 981 | * @param {mat4} dest mat4 receiving copied values 982 | * 983 | * @returns {mat4} dest 984 | */ 985 | mat4.set = function (mat, dest) { 986 | dest[0] = mat[0]; 987 | dest[1] = mat[1]; 988 | dest[2] = mat[2]; 989 | dest[3] = mat[3]; 990 | dest[4] = mat[4]; 991 | dest[5] = mat[5]; 992 | dest[6] = mat[6]; 993 | dest[7] = mat[7]; 994 | dest[8] = mat[8]; 995 | dest[9] = mat[9]; 996 | dest[10] = mat[10]; 997 | dest[11] = mat[11]; 998 | dest[12] = mat[12]; 999 | dest[13] = mat[13]; 1000 | dest[14] = mat[14]; 1001 | dest[15] = mat[15]; 1002 | return dest; 1003 | }; 1004 | 1005 | /** 1006 | * Compares two matrices for equality within a certain margin of error 1007 | * 1008 | * @param {mat4} a First matrix 1009 | * @param {mat4} b Second matrix 1010 | * 1011 | * @returns {Boolean} True if a is equivalent to b 1012 | */ 1013 | mat4.equal = function (a, b) { 1014 | return a === b || ( 1015 | Math.abs(a[0] - b[0]) < FLOAT_EPSILON && 1016 | Math.abs(a[1] - b[1]) < FLOAT_EPSILON && 1017 | Math.abs(a[2] - b[2]) < FLOAT_EPSILON && 1018 | Math.abs(a[3] - b[3]) < FLOAT_EPSILON && 1019 | Math.abs(a[4] - b[4]) < FLOAT_EPSILON && 1020 | Math.abs(a[5] - b[5]) < FLOAT_EPSILON && 1021 | Math.abs(a[6] - b[6]) < FLOAT_EPSILON && 1022 | Math.abs(a[7] - b[7]) < FLOAT_EPSILON && 1023 | Math.abs(a[8] - b[8]) < FLOAT_EPSILON && 1024 | Math.abs(a[9] - b[9]) < FLOAT_EPSILON && 1025 | Math.abs(a[10] - b[10]) < FLOAT_EPSILON && 1026 | Math.abs(a[11] - b[11]) < FLOAT_EPSILON && 1027 | Math.abs(a[12] - b[12]) < FLOAT_EPSILON && 1028 | Math.abs(a[13] - b[13]) < FLOAT_EPSILON && 1029 | Math.abs(a[14] - b[14]) < FLOAT_EPSILON && 1030 | Math.abs(a[15] - b[15]) < FLOAT_EPSILON 1031 | ); 1032 | }; 1033 | 1034 | /** 1035 | * Sets a mat4 to an identity matrix 1036 | * 1037 | * @param {mat4} dest mat4 to set 1038 | * 1039 | * @returns {mat4} dest 1040 | */ 1041 | mat4.identity = function (dest) { 1042 | if (!dest) { dest = mat4.create(); } 1043 | dest[0] = 1; 1044 | dest[1] = 0; 1045 | dest[2] = 0; 1046 | dest[3] = 0; 1047 | dest[4] = 0; 1048 | dest[5] = 1; 1049 | dest[6] = 0; 1050 | dest[7] = 0; 1051 | dest[8] = 0; 1052 | dest[9] = 0; 1053 | dest[10] = 1; 1054 | dest[11] = 0; 1055 | dest[12] = 0; 1056 | dest[13] = 0; 1057 | dest[14] = 0; 1058 | dest[15] = 1; 1059 | return dest; 1060 | }; 1061 | 1062 | /** 1063 | * Transposes a mat4 (flips the values over the diagonal) 1064 | * 1065 | * @param {mat4} mat mat4 to transpose 1066 | * @param {mat4} [dest] mat4 receiving transposed values. If not specified result is written to mat 1067 | * 1068 | * @param {mat4} dest is specified, mat otherwise 1069 | */ 1070 | mat4.transpose = function (mat, dest) { 1071 | // If we are transposing ourselves we can skip a few steps but have to cache some values 1072 | if (!dest || mat === dest) { 1073 | var a01 = mat[1], a02 = mat[2], a03 = mat[3], 1074 | a12 = mat[6], a13 = mat[7], 1075 | a23 = mat[11]; 1076 | 1077 | mat[1] = mat[4]; 1078 | mat[2] = mat[8]; 1079 | mat[3] = mat[12]; 1080 | mat[4] = a01; 1081 | mat[6] = mat[9]; 1082 | mat[7] = mat[13]; 1083 | mat[8] = a02; 1084 | mat[9] = a12; 1085 | mat[11] = mat[14]; 1086 | mat[12] = a03; 1087 | mat[13] = a13; 1088 | mat[14] = a23; 1089 | return mat; 1090 | } 1091 | 1092 | dest[0] = mat[0]; 1093 | dest[1] = mat[4]; 1094 | dest[2] = mat[8]; 1095 | dest[3] = mat[12]; 1096 | dest[4] = mat[1]; 1097 | dest[5] = mat[5]; 1098 | dest[6] = mat[9]; 1099 | dest[7] = mat[13]; 1100 | dest[8] = mat[2]; 1101 | dest[9] = mat[6]; 1102 | dest[10] = mat[10]; 1103 | dest[11] = mat[14]; 1104 | dest[12] = mat[3]; 1105 | dest[13] = mat[7]; 1106 | dest[14] = mat[11]; 1107 | dest[15] = mat[15]; 1108 | return dest; 1109 | }; 1110 | 1111 | /** 1112 | * Calculates the determinant of a mat4 1113 | * 1114 | * @param {mat4} mat mat4 to calculate determinant of 1115 | * 1116 | * @returns {number} determinant of mat 1117 | */ 1118 | mat4.determinant = function (mat) { 1119 | // Cache the matrix values (makes for huge speed increases!) 1120 | var a00 = mat[0], a01 = mat[1], a02 = mat[2], a03 = mat[3], 1121 | a10 = mat[4], a11 = mat[5], a12 = mat[6], a13 = mat[7], 1122 | a20 = mat[8], a21 = mat[9], a22 = mat[10], a23 = mat[11], 1123 | a30 = mat[12], a31 = mat[13], a32 = mat[14], a33 = mat[15]; 1124 | 1125 | return (a30 * a21 * a12 * a03 - a20 * a31 * a12 * a03 - a30 * a11 * a22 * a03 + a10 * a31 * a22 * a03 + 1126 | a20 * a11 * a32 * a03 - a10 * a21 * a32 * a03 - a30 * a21 * a02 * a13 + a20 * a31 * a02 * a13 + 1127 | a30 * a01 * a22 * a13 - a00 * a31 * a22 * a13 - a20 * a01 * a32 * a13 + a00 * a21 * a32 * a13 + 1128 | a30 * a11 * a02 * a23 - a10 * a31 * a02 * a23 - a30 * a01 * a12 * a23 + a00 * a31 * a12 * a23 + 1129 | a10 * a01 * a32 * a23 - a00 * a11 * a32 * a23 - a20 * a11 * a02 * a33 + a10 * a21 * a02 * a33 + 1130 | a20 * a01 * a12 * a33 - a00 * a21 * a12 * a33 - a10 * a01 * a22 * a33 + a00 * a11 * a22 * a33); 1131 | }; 1132 | 1133 | /** 1134 | * Calculates the inverse matrix of a mat4 1135 | * 1136 | * @param {mat4} mat mat4 to calculate inverse of 1137 | * @param {mat4} [dest] mat4 receiving inverse matrix. If not specified result is written to mat 1138 | * 1139 | * @param {mat4} dest is specified, mat otherwise, null if matrix cannot be inverted 1140 | */ 1141 | mat4.inverse = function (mat, dest) { 1142 | if (!dest) { dest = mat; } 1143 | 1144 | // Cache the matrix values (makes for huge speed increases!) 1145 | var a00 = mat[0], a01 = mat[1], a02 = mat[2], a03 = mat[3], 1146 | a10 = mat[4], a11 = mat[5], a12 = mat[6], a13 = mat[7], 1147 | a20 = mat[8], a21 = mat[9], a22 = mat[10], a23 = mat[11], 1148 | a30 = mat[12], a31 = mat[13], a32 = mat[14], a33 = mat[15], 1149 | 1150 | b00 = a00 * a11 - a01 * a10, 1151 | b01 = a00 * a12 - a02 * a10, 1152 | b02 = a00 * a13 - a03 * a10, 1153 | b03 = a01 * a12 - a02 * a11, 1154 | b04 = a01 * a13 - a03 * a11, 1155 | b05 = a02 * a13 - a03 * a12, 1156 | b06 = a20 * a31 - a21 * a30, 1157 | b07 = a20 * a32 - a22 * a30, 1158 | b08 = a20 * a33 - a23 * a30, 1159 | b09 = a21 * a32 - a22 * a31, 1160 | b10 = a21 * a33 - a23 * a31, 1161 | b11 = a22 * a33 - a23 * a32, 1162 | 1163 | d = (b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06), 1164 | invDet; 1165 | 1166 | // Calculate the determinant 1167 | if (!d) { return null; } 1168 | invDet = 1 / d; 1169 | 1170 | dest[0] = (a11 * b11 - a12 * b10 + a13 * b09) * invDet; 1171 | dest[1] = (-a01 * b11 + a02 * b10 - a03 * b09) * invDet; 1172 | dest[2] = (a31 * b05 - a32 * b04 + a33 * b03) * invDet; 1173 | dest[3] = (-a21 * b05 + a22 * b04 - a23 * b03) * invDet; 1174 | dest[4] = (-a10 * b11 + a12 * b08 - a13 * b07) * invDet; 1175 | dest[5] = (a00 * b11 - a02 * b08 + a03 * b07) * invDet; 1176 | dest[6] = (-a30 * b05 + a32 * b02 - a33 * b01) * invDet; 1177 | dest[7] = (a20 * b05 - a22 * b02 + a23 * b01) * invDet; 1178 | dest[8] = (a10 * b10 - a11 * b08 + a13 * b06) * invDet; 1179 | dest[9] = (-a00 * b10 + a01 * b08 - a03 * b06) * invDet; 1180 | dest[10] = (a30 * b04 - a31 * b02 + a33 * b00) * invDet; 1181 | dest[11] = (-a20 * b04 + a21 * b02 - a23 * b00) * invDet; 1182 | dest[12] = (-a10 * b09 + a11 * b07 - a12 * b06) * invDet; 1183 | dest[13] = (a00 * b09 - a01 * b07 + a02 * b06) * invDet; 1184 | dest[14] = (-a30 * b03 + a31 * b01 - a32 * b00) * invDet; 1185 | dest[15] = (a20 * b03 - a21 * b01 + a22 * b00) * invDet; 1186 | 1187 | return dest; 1188 | }; 1189 | 1190 | /** 1191 | * Copies the upper 3x3 elements of a mat4 into another mat4 1192 | * 1193 | * @param {mat4} mat mat4 containing values to copy 1194 | * @param {mat4} [dest] mat4 receiving copied values 1195 | * 1196 | * @returns {mat4} dest is specified, a new mat4 otherwise 1197 | */ 1198 | mat4.toRotationMat = function (mat, dest) { 1199 | if (!dest) { dest = mat4.create(); } 1200 | 1201 | dest[0] = mat[0]; 1202 | dest[1] = mat[1]; 1203 | dest[2] = mat[2]; 1204 | dest[3] = mat[3]; 1205 | dest[4] = mat[4]; 1206 | dest[5] = mat[5]; 1207 | dest[6] = mat[6]; 1208 | dest[7] = mat[7]; 1209 | dest[8] = mat[8]; 1210 | dest[9] = mat[9]; 1211 | dest[10] = mat[10]; 1212 | dest[11] = mat[11]; 1213 | dest[12] = 0; 1214 | dest[13] = 0; 1215 | dest[14] = 0; 1216 | dest[15] = 1; 1217 | 1218 | return dest; 1219 | }; 1220 | 1221 | /** 1222 | * Copies the upper 3x3 elements of a mat4 into a mat3 1223 | * 1224 | * @param {mat4} mat mat4 containing values to copy 1225 | * @param {mat3} [dest] mat3 receiving copied values 1226 | * 1227 | * @returns {mat3} dest is specified, a new mat3 otherwise 1228 | */ 1229 | mat4.toMat3 = function (mat, dest) { 1230 | if (!dest) { dest = mat3.create(); } 1231 | 1232 | dest[0] = mat[0]; 1233 | dest[1] = mat[1]; 1234 | dest[2] = mat[2]; 1235 | dest[3] = mat[4]; 1236 | dest[4] = mat[5]; 1237 | dest[5] = mat[6]; 1238 | dest[6] = mat[8]; 1239 | dest[7] = mat[9]; 1240 | dest[8] = mat[10]; 1241 | 1242 | return dest; 1243 | }; 1244 | 1245 | /** 1246 | * Calculates the inverse of the upper 3x3 elements of a mat4 and copies the result into a mat3 1247 | * The resulting matrix is useful for calculating transformed normals 1248 | * 1249 | * Params: 1250 | * @param {mat4} mat mat4 containing values to invert and copy 1251 | * @param {mat3} [dest] mat3 receiving values 1252 | * 1253 | * @returns {mat3} dest is specified, a new mat3 otherwise, null if the matrix cannot be inverted 1254 | */ 1255 | mat4.toInverseMat3 = function (mat, dest) { 1256 | // Cache the matrix values (makes for huge speed increases!) 1257 | var a00 = mat[0], a01 = mat[1], a02 = mat[2], 1258 | a10 = mat[4], a11 = mat[5], a12 = mat[6], 1259 | a20 = mat[8], a21 = mat[9], a22 = mat[10], 1260 | 1261 | b01 = a22 * a11 - a12 * a21, 1262 | b11 = -a22 * a10 + a12 * a20, 1263 | b21 = a21 * a10 - a11 * a20, 1264 | 1265 | d = a00 * b01 + a01 * b11 + a02 * b21, 1266 | id; 1267 | 1268 | if (!d) { return null; } 1269 | id = 1 / d; 1270 | 1271 | if (!dest) { dest = mat3.create(); } 1272 | 1273 | dest[0] = b01 * id; 1274 | dest[1] = (-a22 * a01 + a02 * a21) * id; 1275 | dest[2] = (a12 * a01 - a02 * a11) * id; 1276 | dest[3] = b11 * id; 1277 | dest[4] = (a22 * a00 - a02 * a20) * id; 1278 | dest[5] = (-a12 * a00 + a02 * a10) * id; 1279 | dest[6] = b21 * id; 1280 | dest[7] = (-a21 * a00 + a01 * a20) * id; 1281 | dest[8] = (a11 * a00 - a01 * a10) * id; 1282 | 1283 | return dest; 1284 | }; 1285 | 1286 | /** 1287 | * Performs a matrix multiplication 1288 | * 1289 | * @param {mat4} mat First operand 1290 | * @param {mat4} mat2 Second operand 1291 | * @param {mat4} [dest] mat4 receiving operation result. If not specified result is written to mat 1292 | * 1293 | * @returns {mat4} dest if specified, mat otherwise 1294 | */ 1295 | mat4.multiply = function (mat, mat2, dest) { 1296 | if (!dest) { dest = mat; } 1297 | 1298 | // Cache the matrix values (makes for huge speed increases!) 1299 | var a00 = mat[ 0], a01 = mat[ 1], a02 = mat[ 2], a03 = mat[3]; 1300 | var a10 = mat[ 4], a11 = mat[ 5], a12 = mat[ 6], a13 = mat[7]; 1301 | var a20 = mat[ 8], a21 = mat[ 9], a22 = mat[10], a23 = mat[11]; 1302 | var a30 = mat[12], a31 = mat[13], a32 = mat[14], a33 = mat[15]; 1303 | 1304 | // Cache only the current line of the second matrix 1305 | var b0 = mat2[0], b1 = mat2[1], b2 = mat2[2], b3 = mat2[3]; 1306 | dest[0] = b0*a00 + b1*a10 + b2*a20 + b3*a30; 1307 | dest[1] = b0*a01 + b1*a11 + b2*a21 + b3*a31; 1308 | dest[2] = b0*a02 + b1*a12 + b2*a22 + b3*a32; 1309 | dest[3] = b0*a03 + b1*a13 + b2*a23 + b3*a33; 1310 | 1311 | b0 = mat2[4]; 1312 | b1 = mat2[5]; 1313 | b2 = mat2[6]; 1314 | b3 = mat2[7]; 1315 | dest[4] = b0*a00 + b1*a10 + b2*a20 + b3*a30; 1316 | dest[5] = b0*a01 + b1*a11 + b2*a21 + b3*a31; 1317 | dest[6] = b0*a02 + b1*a12 + b2*a22 + b3*a32; 1318 | dest[7] = b0*a03 + b1*a13 + b2*a23 + b3*a33; 1319 | 1320 | b0 = mat2[8]; 1321 | b1 = mat2[9]; 1322 | b2 = mat2[10]; 1323 | b3 = mat2[11]; 1324 | dest[8] = b0*a00 + b1*a10 + b2*a20 + b3*a30; 1325 | dest[9] = b0*a01 + b1*a11 + b2*a21 + b3*a31; 1326 | dest[10] = b0*a02 + b1*a12 + b2*a22 + b3*a32; 1327 | dest[11] = b0*a03 + b1*a13 + b2*a23 + b3*a33; 1328 | 1329 | b0 = mat2[12]; 1330 | b1 = mat2[13]; 1331 | b2 = mat2[14]; 1332 | b3 = mat2[15]; 1333 | dest[12] = b0*a00 + b1*a10 + b2*a20 + b3*a30; 1334 | dest[13] = b0*a01 + b1*a11 + b2*a21 + b3*a31; 1335 | dest[14] = b0*a02 + b1*a12 + b2*a22 + b3*a32; 1336 | dest[15] = b0*a03 + b1*a13 + b2*a23 + b3*a33; 1337 | 1338 | return dest; 1339 | }; 1340 | 1341 | /** 1342 | * Transforms a vec3 with the given matrix 1343 | * 4th vector component is implicitly '1' 1344 | * 1345 | * @param {mat4} mat mat4 to transform the vector with 1346 | * @param {vec3} vec vec3 to transform 1347 | * @param {vec3} [dest] vec3 receiving operation result. If not specified result is written to vec 1348 | * 1349 | * @returns {vec3} dest if specified, vec otherwise 1350 | */ 1351 | mat4.multiplyVec3 = function (mat, vec, dest) { 1352 | if (!dest) { dest = vec; } 1353 | 1354 | var x = vec[0], y = vec[1], z = vec[2]; 1355 | 1356 | dest[0] = mat[0] * x + mat[4] * y + mat[8] * z + mat[12]; 1357 | dest[1] = mat[1] * x + mat[5] * y + mat[9] * z + mat[13]; 1358 | dest[2] = mat[2] * x + mat[6] * y + mat[10] * z + mat[14]; 1359 | 1360 | return dest; 1361 | }; 1362 | 1363 | /** 1364 | * Transforms a vec4 with the given matrix 1365 | * 1366 | * @param {mat4} mat mat4 to transform the vector with 1367 | * @param {vec4} vec vec4 to transform 1368 | * @param {vec4} [dest] vec4 receiving operation result. If not specified result is written to vec 1369 | * 1370 | * @returns {vec4} dest if specified, vec otherwise 1371 | */ 1372 | mat4.multiplyVec4 = function (mat, vec, dest) { 1373 | if (!dest) { dest = vec; } 1374 | 1375 | var x = vec[0], y = vec[1], z = vec[2], w = vec[3]; 1376 | 1377 | dest[0] = mat[0] * x + mat[4] * y + mat[8] * z + mat[12] * w; 1378 | dest[1] = mat[1] * x + mat[5] * y + mat[9] * z + mat[13] * w; 1379 | dest[2] = mat[2] * x + mat[6] * y + mat[10] * z + mat[14] * w; 1380 | dest[3] = mat[3] * x + mat[7] * y + mat[11] * z + mat[15] * w; 1381 | 1382 | return dest; 1383 | }; 1384 | 1385 | /** 1386 | * Translates a matrix by the given vector 1387 | * 1388 | * @param {mat4} mat mat4 to translate 1389 | * @param {vec3} vec vec3 specifying the translation 1390 | * @param {mat4} [dest] mat4 receiving operation result. If not specified result is written to mat 1391 | * 1392 | * @returns {mat4} dest if specified, mat otherwise 1393 | */ 1394 | mat4.translate = function (mat, vec, dest) { 1395 | var x = vec[0], y = vec[1], z = vec[2], 1396 | a00, a01, a02, a03, 1397 | a10, a11, a12, a13, 1398 | a20, a21, a22, a23; 1399 | 1400 | if (!dest || mat === dest) { 1401 | mat[12] = mat[0] * x + mat[4] * y + mat[8] * z + mat[12]; 1402 | mat[13] = mat[1] * x + mat[5] * y + mat[9] * z + mat[13]; 1403 | mat[14] = mat[2] * x + mat[6] * y + mat[10] * z + mat[14]; 1404 | mat[15] = mat[3] * x + mat[7] * y + mat[11] * z + mat[15]; 1405 | return mat; 1406 | } 1407 | 1408 | a00 = mat[0]; a01 = mat[1]; a02 = mat[2]; a03 = mat[3]; 1409 | a10 = mat[4]; a11 = mat[5]; a12 = mat[6]; a13 = mat[7]; 1410 | a20 = mat[8]; a21 = mat[9]; a22 = mat[10]; a23 = mat[11]; 1411 | 1412 | dest[0] = a00; dest[1] = a01; dest[2] = a02; dest[3] = a03; 1413 | dest[4] = a10; dest[5] = a11; dest[6] = a12; dest[7] = a13; 1414 | dest[8] = a20; dest[9] = a21; dest[10] = a22; dest[11] = a23; 1415 | 1416 | dest[12] = a00 * x + a10 * y + a20 * z + mat[12]; 1417 | dest[13] = a01 * x + a11 * y + a21 * z + mat[13]; 1418 | dest[14] = a02 * x + a12 * y + a22 * z + mat[14]; 1419 | dest[15] = a03 * x + a13 * y + a23 * z + mat[15]; 1420 | return dest; 1421 | }; 1422 | 1423 | /** 1424 | * Scales a matrix by the given vector 1425 | * 1426 | * @param {mat4} mat mat4 to scale 1427 | * @param {vec3} vec vec3 specifying the scale for each axis 1428 | * @param {mat4} [dest] mat4 receiving operation result. If not specified result is written to mat 1429 | * 1430 | * @param {mat4} dest if specified, mat otherwise 1431 | */ 1432 | mat4.scale = function (mat, vec, dest) { 1433 | var x = vec[0], y = vec[1], z = vec[2]; 1434 | 1435 | if (!dest || mat === dest) { 1436 | mat[0] *= x; 1437 | mat[1] *= x; 1438 | mat[2] *= x; 1439 | mat[3] *= x; 1440 | mat[4] *= y; 1441 | mat[5] *= y; 1442 | mat[6] *= y; 1443 | mat[7] *= y; 1444 | mat[8] *= z; 1445 | mat[9] *= z; 1446 | mat[10] *= z; 1447 | mat[11] *= z; 1448 | return mat; 1449 | } 1450 | 1451 | dest[0] = mat[0] * x; 1452 | dest[1] = mat[1] * x; 1453 | dest[2] = mat[2] * x; 1454 | dest[3] = mat[3] * x; 1455 | dest[4] = mat[4] * y; 1456 | dest[5] = mat[5] * y; 1457 | dest[6] = mat[6] * y; 1458 | dest[7] = mat[7] * y; 1459 | dest[8] = mat[8] * z; 1460 | dest[9] = mat[9] * z; 1461 | dest[10] = mat[10] * z; 1462 | dest[11] = mat[11] * z; 1463 | dest[12] = mat[12]; 1464 | dest[13] = mat[13]; 1465 | dest[14] = mat[14]; 1466 | dest[15] = mat[15]; 1467 | return dest; 1468 | }; 1469 | 1470 | /** 1471 | * Rotates a matrix by the given angle around the specified axis 1472 | * If rotating around a primary axis (X,Y,Z) one of the specialized rotation functions should be used instead for performance 1473 | * 1474 | * @param {mat4} mat mat4 to rotate 1475 | * @param {number} angle Angle (in radians) to rotate 1476 | * @param {vec3} axis vec3 representing the axis to rotate around 1477 | * @param {mat4} [dest] mat4 receiving operation result. If not specified result is written to mat 1478 | * 1479 | * @returns {mat4} dest if specified, mat otherwise 1480 | */ 1481 | mat4.rotate = function (mat, angle, axis, dest) { 1482 | var x = axis[0], y = axis[1], z = axis[2], 1483 | len = Math.sqrt(x * x + y * y + z * z), 1484 | s, c, t, 1485 | a00, a01, a02, a03, 1486 | a10, a11, a12, a13, 1487 | a20, a21, a22, a23, 1488 | b00, b01, b02, 1489 | b10, b11, b12, 1490 | b20, b21, b22; 1491 | 1492 | if (!len) { return null; } 1493 | if (len !== 1) { 1494 | len = 1 / len; 1495 | x *= len; 1496 | y *= len; 1497 | z *= len; 1498 | } 1499 | 1500 | s = Math.sin(angle); 1501 | c = Math.cos(angle); 1502 | t = 1 - c; 1503 | 1504 | a00 = mat[0]; a01 = mat[1]; a02 = mat[2]; a03 = mat[3]; 1505 | a10 = mat[4]; a11 = mat[5]; a12 = mat[6]; a13 = mat[7]; 1506 | a20 = mat[8]; a21 = mat[9]; a22 = mat[10]; a23 = mat[11]; 1507 | 1508 | // Construct the elements of the rotation matrix 1509 | b00 = x * x * t + c; b01 = y * x * t + z * s; b02 = z * x * t - y * s; 1510 | b10 = x * y * t - z * s; b11 = y * y * t + c; b12 = z * y * t + x * s; 1511 | b20 = x * z * t + y * s; b21 = y * z * t - x * s; b22 = z * z * t + c; 1512 | 1513 | if (!dest) { 1514 | dest = mat; 1515 | } else if (mat !== dest) { // If the source and destination differ, copy the unchanged last row 1516 | dest[12] = mat[12]; 1517 | dest[13] = mat[13]; 1518 | dest[14] = mat[14]; 1519 | dest[15] = mat[15]; 1520 | } 1521 | 1522 | // Perform rotation-specific matrix multiplication 1523 | dest[0] = a00 * b00 + a10 * b01 + a20 * b02; 1524 | dest[1] = a01 * b00 + a11 * b01 + a21 * b02; 1525 | dest[2] = a02 * b00 + a12 * b01 + a22 * b02; 1526 | dest[3] = a03 * b00 + a13 * b01 + a23 * b02; 1527 | 1528 | dest[4] = a00 * b10 + a10 * b11 + a20 * b12; 1529 | dest[5] = a01 * b10 + a11 * b11 + a21 * b12; 1530 | dest[6] = a02 * b10 + a12 * b11 + a22 * b12; 1531 | dest[7] = a03 * b10 + a13 * b11 + a23 * b12; 1532 | 1533 | dest[8] = a00 * b20 + a10 * b21 + a20 * b22; 1534 | dest[9] = a01 * b20 + a11 * b21 + a21 * b22; 1535 | dest[10] = a02 * b20 + a12 * b21 + a22 * b22; 1536 | dest[11] = a03 * b20 + a13 * b21 + a23 * b22; 1537 | return dest; 1538 | }; 1539 | 1540 | /** 1541 | * Rotates a matrix by the given angle around the X axis 1542 | * 1543 | * @param {mat4} mat mat4 to rotate 1544 | * @param {number} angle Angle (in radians) to rotate 1545 | * @param {mat4} [dest] mat4 receiving operation result. If not specified result is written to mat 1546 | * 1547 | * @returns {mat4} dest if specified, mat otherwise 1548 | */ 1549 | mat4.rotateX = function (mat, angle, dest) { 1550 | var s = Math.sin(angle), 1551 | c = Math.cos(angle), 1552 | a10 = mat[4], 1553 | a11 = mat[5], 1554 | a12 = mat[6], 1555 | a13 = mat[7], 1556 | a20 = mat[8], 1557 | a21 = mat[9], 1558 | a22 = mat[10], 1559 | a23 = mat[11]; 1560 | 1561 | if (!dest) { 1562 | dest = mat; 1563 | } else if (mat !== dest) { // If the source and destination differ, copy the unchanged rows 1564 | dest[0] = mat[0]; 1565 | dest[1] = mat[1]; 1566 | dest[2] = mat[2]; 1567 | dest[3] = mat[3]; 1568 | 1569 | dest[12] = mat[12]; 1570 | dest[13] = mat[13]; 1571 | dest[14] = mat[14]; 1572 | dest[15] = mat[15]; 1573 | } 1574 | 1575 | // Perform axis-specific matrix multiplication 1576 | dest[4] = a10 * c + a20 * s; 1577 | dest[5] = a11 * c + a21 * s; 1578 | dest[6] = a12 * c + a22 * s; 1579 | dest[7] = a13 * c + a23 * s; 1580 | 1581 | dest[8] = a10 * -s + a20 * c; 1582 | dest[9] = a11 * -s + a21 * c; 1583 | dest[10] = a12 * -s + a22 * c; 1584 | dest[11] = a13 * -s + a23 * c; 1585 | return dest; 1586 | }; 1587 | 1588 | /** 1589 | * Rotates a matrix by the given angle around the Y axis 1590 | * 1591 | * @param {mat4} mat mat4 to rotate 1592 | * @param {number} angle Angle (in radians) to rotate 1593 | * @param {mat4} [dest] mat4 receiving operation result. If not specified result is written to mat 1594 | * 1595 | * @returns {mat4} dest if specified, mat otherwise 1596 | */ 1597 | mat4.rotateY = function (mat, angle, dest) { 1598 | var s = Math.sin(angle), 1599 | c = Math.cos(angle), 1600 | a00 = mat[0], 1601 | a01 = mat[1], 1602 | a02 = mat[2], 1603 | a03 = mat[3], 1604 | a20 = mat[8], 1605 | a21 = mat[9], 1606 | a22 = mat[10], 1607 | a23 = mat[11]; 1608 | 1609 | if (!dest) { 1610 | dest = mat; 1611 | } else if (mat !== dest) { // If the source and destination differ, copy the unchanged rows 1612 | dest[4] = mat[4]; 1613 | dest[5] = mat[5]; 1614 | dest[6] = mat[6]; 1615 | dest[7] = mat[7]; 1616 | 1617 | dest[12] = mat[12]; 1618 | dest[13] = mat[13]; 1619 | dest[14] = mat[14]; 1620 | dest[15] = mat[15]; 1621 | } 1622 | 1623 | // Perform axis-specific matrix multiplication 1624 | dest[0] = a00 * c + a20 * -s; 1625 | dest[1] = a01 * c + a21 * -s; 1626 | dest[2] = a02 * c + a22 * -s; 1627 | dest[3] = a03 * c + a23 * -s; 1628 | 1629 | dest[8] = a00 * s + a20 * c; 1630 | dest[9] = a01 * s + a21 * c; 1631 | dest[10] = a02 * s + a22 * c; 1632 | dest[11] = a03 * s + a23 * c; 1633 | return dest; 1634 | }; 1635 | 1636 | /** 1637 | * Rotates a matrix by the given angle around the Z axis 1638 | * 1639 | * @param {mat4} mat mat4 to rotate 1640 | * @param {number} angle Angle (in radians) to rotate 1641 | * @param {mat4} [dest] mat4 receiving operation result. If not specified result is written to mat 1642 | * 1643 | * @returns {mat4} dest if specified, mat otherwise 1644 | */ 1645 | mat4.rotateZ = function (mat, angle, dest) { 1646 | var s = Math.sin(angle), 1647 | c = Math.cos(angle), 1648 | a00 = mat[0], 1649 | a01 = mat[1], 1650 | a02 = mat[2], 1651 | a03 = mat[3], 1652 | a10 = mat[4], 1653 | a11 = mat[5], 1654 | a12 = mat[6], 1655 | a13 = mat[7]; 1656 | 1657 | if (!dest) { 1658 | dest = mat; 1659 | } else if (mat !== dest) { // If the source and destination differ, copy the unchanged last row 1660 | dest[8] = mat[8]; 1661 | dest[9] = mat[9]; 1662 | dest[10] = mat[10]; 1663 | dest[11] = mat[11]; 1664 | 1665 | dest[12] = mat[12]; 1666 | dest[13] = mat[13]; 1667 | dest[14] = mat[14]; 1668 | dest[15] = mat[15]; 1669 | } 1670 | 1671 | // Perform axis-specific matrix multiplication 1672 | dest[0] = a00 * c + a10 * s; 1673 | dest[1] = a01 * c + a11 * s; 1674 | dest[2] = a02 * c + a12 * s; 1675 | dest[3] = a03 * c + a13 * s; 1676 | 1677 | dest[4] = a00 * -s + a10 * c; 1678 | dest[5] = a01 * -s + a11 * c; 1679 | dest[6] = a02 * -s + a12 * c; 1680 | dest[7] = a03 * -s + a13 * c; 1681 | 1682 | return dest; 1683 | }; 1684 | 1685 | /** 1686 | * Generates a frustum matrix with the given bounds 1687 | * 1688 | * @param {number} left Left bound of the frustum 1689 | * @param {number} right Right bound of the frustum 1690 | * @param {number} bottom Bottom bound of the frustum 1691 | * @param {number} top Top bound of the frustum 1692 | * @param {number} near Near bound of the frustum 1693 | * @param {number} far Far bound of the frustum 1694 | * @param {mat4} [dest] mat4 frustum matrix will be written into 1695 | * 1696 | * @returns {mat4} dest if specified, a new mat4 otherwise 1697 | */ 1698 | mat4.frustum = function (left, right, bottom, top, near, far, dest) { 1699 | if (!dest) { dest = mat4.create(); } 1700 | var rl = (right - left), 1701 | tb = (top - bottom), 1702 | fn = (far - near); 1703 | dest[0] = (near * 2) / rl; 1704 | dest[1] = 0; 1705 | dest[2] = 0; 1706 | dest[3] = 0; 1707 | dest[4] = 0; 1708 | dest[5] = (near * 2) / tb; 1709 | dest[6] = 0; 1710 | dest[7] = 0; 1711 | dest[8] = (right + left) / rl; 1712 | dest[9] = (top + bottom) / tb; 1713 | dest[10] = -(far + near) / fn; 1714 | dest[11] = -1; 1715 | dest[12] = 0; 1716 | dest[13] = 0; 1717 | dest[14] = -(far * near * 2) / fn; 1718 | dest[15] = 0; 1719 | return dest; 1720 | }; 1721 | 1722 | /** 1723 | * Generates a perspective projection matrix with the given bounds 1724 | * 1725 | * @param {number} fovy Vertical field of view 1726 | * @param {number} aspect Aspect ratio. typically viewport width/height 1727 | * @param {number} near Near bound of the frustum 1728 | * @param {number} far Far bound of the frustum 1729 | * @param {mat4} [dest] mat4 frustum matrix will be written into 1730 | * 1731 | * @returns {mat4} dest if specified, a new mat4 otherwise 1732 | */ 1733 | mat4.perspective = function (fovy, aspect, near, far, dest) { 1734 | var top = near * Math.tan(fovy * Math.PI / 360.0), 1735 | right = top * aspect; 1736 | return mat4.frustum(-right, right, -top, top, near, far, dest); 1737 | }; 1738 | 1739 | /** 1740 | * Generates a orthogonal projection matrix with the given bounds 1741 | * 1742 | * @param {number} left Left bound of the frustum 1743 | * @param {number} right Right bound of the frustum 1744 | * @param {number} bottom Bottom bound of the frustum 1745 | * @param {number} top Top bound of the frustum 1746 | * @param {number} near Near bound of the frustum 1747 | * @param {number} far Far bound of the frustum 1748 | * @param {mat4} [dest] mat4 frustum matrix will be written into 1749 | * 1750 | * @returns {mat4} dest if specified, a new mat4 otherwise 1751 | */ 1752 | mat4.ortho = function (left, right, bottom, top, near, far, dest) { 1753 | if (!dest) { dest = mat4.create(); } 1754 | var rl = (right - left), 1755 | tb = (top - bottom), 1756 | fn = (far - near); 1757 | dest[0] = 2 / rl; 1758 | dest[1] = 0; 1759 | dest[2] = 0; 1760 | dest[3] = 0; 1761 | dest[4] = 0; 1762 | dest[5] = 2 / tb; 1763 | dest[6] = 0; 1764 | dest[7] = 0; 1765 | dest[8] = 0; 1766 | dest[9] = 0; 1767 | dest[10] = -2 / fn; 1768 | dest[11] = 0; 1769 | dest[12] = -(left + right) / rl; 1770 | dest[13] = -(top + bottom) / tb; 1771 | dest[14] = -(far + near) / fn; 1772 | dest[15] = 1; 1773 | return dest; 1774 | }; 1775 | 1776 | /** 1777 | * Generates a look-at matrix with the given eye position, focal point, and up axis 1778 | * 1779 | * @param {vec3} eye Position of the viewer 1780 | * @param {vec3} center Point the viewer is looking at 1781 | * @param {vec3} up vec3 pointing "up" 1782 | * @param {mat4} [dest] mat4 frustum matrix will be written into 1783 | * 1784 | * @returns {mat4} dest if specified, a new mat4 otherwise 1785 | */ 1786 | mat4.lookAt = function (eye, center, up, dest) { 1787 | if (!dest) { dest = mat4.create(); } 1788 | 1789 | var x0, x1, x2, y0, y1, y2, z0, z1, z2, len, 1790 | eyex = eye[0], 1791 | eyey = eye[1], 1792 | eyez = eye[2], 1793 | upx = up[0], 1794 | upy = up[1], 1795 | upz = up[2], 1796 | centerx = center[0], 1797 | centery = center[1], 1798 | centerz = center[2]; 1799 | 1800 | if (eyex === centerx && eyey === centery && eyez === centerz) { 1801 | return mat4.identity(dest); 1802 | } 1803 | 1804 | //vec3.direction(eye, center, z); 1805 | z0 = eyex - centerx; 1806 | z1 = eyey - centery; 1807 | z2 = eyez - centerz; 1808 | 1809 | // normalize (no check needed for 0 because of early return) 1810 | len = 1 / Math.sqrt(z0 * z0 + z1 * z1 + z2 * z2); 1811 | z0 *= len; 1812 | z1 *= len; 1813 | z2 *= len; 1814 | 1815 | //vec3.normalize(vec3.cross(up, z, x)); 1816 | x0 = upy * z2 - upz * z1; 1817 | x1 = upz * z0 - upx * z2; 1818 | x2 = upx * z1 - upy * z0; 1819 | len = Math.sqrt(x0 * x0 + x1 * x1 + x2 * x2); 1820 | if (!len) { 1821 | x0 = 0; 1822 | x1 = 0; 1823 | x2 = 0; 1824 | } else { 1825 | len = 1 / len; 1826 | x0 *= len; 1827 | x1 *= len; 1828 | x2 *= len; 1829 | } 1830 | 1831 | //vec3.normalize(vec3.cross(z, x, y)); 1832 | y0 = z1 * x2 - z2 * x1; 1833 | y1 = z2 * x0 - z0 * x2; 1834 | y2 = z0 * x1 - z1 * x0; 1835 | 1836 | len = Math.sqrt(y0 * y0 + y1 * y1 + y2 * y2); 1837 | if (!len) { 1838 | y0 = 0; 1839 | y1 = 0; 1840 | y2 = 0; 1841 | } else { 1842 | len = 1 / len; 1843 | y0 *= len; 1844 | y1 *= len; 1845 | y2 *= len; 1846 | } 1847 | 1848 | dest[0] = x0; 1849 | dest[1] = y0; 1850 | dest[2] = z0; 1851 | dest[3] = 0; 1852 | dest[4] = x1; 1853 | dest[5] = y1; 1854 | dest[6] = z1; 1855 | dest[7] = 0; 1856 | dest[8] = x2; 1857 | dest[9] = y2; 1858 | dest[10] = z2; 1859 | dest[11] = 0; 1860 | dest[12] = -(x0 * eyex + x1 * eyey + x2 * eyez); 1861 | dest[13] = -(y0 * eyex + y1 * eyey + y2 * eyez); 1862 | dest[14] = -(z0 * eyex + z1 * eyey + z2 * eyez); 1863 | dest[15] = 1; 1864 | 1865 | return dest; 1866 | }; 1867 | 1868 | /** 1869 | * Creates a matrix from a quaternion rotation and vector translation 1870 | * This is equivalent to (but much faster than): 1871 | * 1872 | * mat4.identity(dest); 1873 | * mat4.translate(dest, vec); 1874 | * var quatMat = mat4.create(); 1875 | * quat4.toMat4(quat, quatMat); 1876 | * mat4.multiply(dest, quatMat); 1877 | * 1878 | * @param {quat4} quat Rotation quaternion 1879 | * @param {vec3} vec Translation vector 1880 | * @param {mat4} [dest] mat4 receiving operation result. If not specified result is written to a new mat4 1881 | * 1882 | * @returns {mat4} dest if specified, a new mat4 otherwise 1883 | */ 1884 | mat4.fromRotationTranslation = function (quat, vec, dest) { 1885 | if (!dest) { dest = mat4.create(); } 1886 | 1887 | // Quaternion math 1888 | var x = quat[0], y = quat[1], z = quat[2], w = quat[3], 1889 | x2 = x + x, 1890 | y2 = y + y, 1891 | z2 = z + z, 1892 | 1893 | xx = x * x2, 1894 | xy = x * y2, 1895 | xz = x * z2, 1896 | yy = y * y2, 1897 | yz = y * z2, 1898 | zz = z * z2, 1899 | wx = w * x2, 1900 | wy = w * y2, 1901 | wz = w * z2; 1902 | 1903 | dest[0] = 1 - (yy + zz); 1904 | dest[1] = xy + wz; 1905 | dest[2] = xz - wy; 1906 | dest[3] = 0; 1907 | dest[4] = xy - wz; 1908 | dest[5] = 1 - (xx + zz); 1909 | dest[6] = yz + wx; 1910 | dest[7] = 0; 1911 | dest[8] = xz + wy; 1912 | dest[9] = yz - wx; 1913 | dest[10] = 1 - (xx + yy); 1914 | dest[11] = 0; 1915 | dest[12] = vec[0]; 1916 | dest[13] = vec[1]; 1917 | dest[14] = vec[2]; 1918 | dest[15] = 1; 1919 | 1920 | return dest; 1921 | }; 1922 | 1923 | /** 1924 | * Returns a string representation of a mat4 1925 | * 1926 | * @param {mat4} mat mat4 to represent as a string 1927 | * 1928 | * @returns {string} String representation of mat 1929 | */ 1930 | mat4.str = function (mat) { 1931 | return '[' + mat[0] + ', ' + mat[1] + ', ' + mat[2] + ', ' + mat[3] + 1932 | ', ' + mat[4] + ', ' + mat[5] + ', ' + mat[6] + ', ' + mat[7] + 1933 | ', ' + mat[8] + ', ' + mat[9] + ', ' + mat[10] + ', ' + mat[11] + 1934 | ', ' + mat[12] + ', ' + mat[13] + ', ' + mat[14] + ', ' + mat[15] + ']'; 1935 | }; 1936 | 1937 | /** 1938 | * @class Quaternion 1939 | * @name quat4 1940 | */ 1941 | var quat4 = {}; 1942 | 1943 | /** 1944 | * Creates a new instance of a quat4 using the default array type 1945 | * Any javascript array containing at least 4 numeric elements can serve as a quat4 1946 | * 1947 | * @param {quat4} [quat] quat4 containing values to initialize with 1948 | * 1949 | * @returns {quat4} New quat4 1950 | */ 1951 | quat4.create = function (quat) { 1952 | var dest = new MatrixArray(4); 1953 | 1954 | if (quat) { 1955 | dest[0] = quat[0]; 1956 | dest[1] = quat[1]; 1957 | dest[2] = quat[2]; 1958 | dest[3] = quat[3]; 1959 | } else { 1960 | dest[0] = dest[1] = dest[2] = dest[3] = 0; 1961 | } 1962 | 1963 | return dest; 1964 | }; 1965 | 1966 | /** 1967 | * Creates a new instance of a quat4, initializing it with the given arguments 1968 | * 1969 | * @param {number} x X value 1970 | * @param {number} y Y value 1971 | * @param {number} z Z value 1972 | * @param {number} w W value 1973 | 1974 | * @returns {quat4} New quat4 1975 | */ 1976 | quat4.createFrom = function (x, y, z, w) { 1977 | var dest = new MatrixArray(4); 1978 | 1979 | dest[0] = x; 1980 | dest[1] = y; 1981 | dest[2] = z; 1982 | dest[3] = w; 1983 | 1984 | return dest; 1985 | }; 1986 | 1987 | /** 1988 | * Copies the values of one quat4 to another 1989 | * 1990 | * @param {quat4} quat quat4 containing values to copy 1991 | * @param {quat4} dest quat4 receiving copied values 1992 | * 1993 | * @returns {quat4} dest 1994 | */ 1995 | quat4.set = function (quat, dest) { 1996 | dest[0] = quat[0]; 1997 | dest[1] = quat[1]; 1998 | dest[2] = quat[2]; 1999 | dest[3] = quat[3]; 2000 | 2001 | return dest; 2002 | }; 2003 | 2004 | /** 2005 | * Compares two quaternions for equality within a certain margin of error 2006 | * 2007 | * @param {quat4} a First vector 2008 | * @param {quat4} b Second vector 2009 | * 2010 | * @returns {Boolean} True if a is equivalent to b 2011 | */ 2012 | quat4.equal = function (a, b) { 2013 | return a === b || ( 2014 | Math.abs(a[0] - b[0]) < FLOAT_EPSILON && 2015 | Math.abs(a[1] - b[1]) < FLOAT_EPSILON && 2016 | Math.abs(a[2] - b[2]) < FLOAT_EPSILON && 2017 | Math.abs(a[3] - b[3]) < FLOAT_EPSILON 2018 | ); 2019 | }; 2020 | 2021 | /** 2022 | * Creates a new identity Quat4 2023 | * 2024 | * @param {quat4} [dest] quat4 receiving copied values 2025 | * 2026 | * @returns {quat4} dest is specified, new quat4 otherwise 2027 | */ 2028 | quat4.identity = function (dest) { 2029 | if (!dest) { dest = quat4.create(); } 2030 | dest[0] = 0; 2031 | dest[1] = 0; 2032 | dest[2] = 0; 2033 | dest[3] = 1; 2034 | return dest; 2035 | }; 2036 | 2037 | var identityQuat4 = quat4.identity(); 2038 | 2039 | /** 2040 | * Calculates the W component of a quat4 from the X, Y, and Z components. 2041 | * Assumes that quaternion is 1 unit in length. 2042 | * Any existing W component will be ignored. 2043 | * 2044 | * @param {quat4} quat quat4 to calculate W component of 2045 | * @param {quat4} [dest] quat4 receiving calculated values. If not specified result is written to quat 2046 | * 2047 | * @returns {quat4} dest if specified, quat otherwise 2048 | */ 2049 | quat4.calculateW = function (quat, dest) { 2050 | var x = quat[0], y = quat[1], z = quat[2]; 2051 | 2052 | if (!dest || quat === dest) { 2053 | quat[3] = -Math.sqrt(Math.abs(1.0 - x * x - y * y - z * z)); 2054 | return quat; 2055 | } 2056 | dest[0] = x; 2057 | dest[1] = y; 2058 | dest[2] = z; 2059 | dest[3] = -Math.sqrt(Math.abs(1.0 - x * x - y * y - z * z)); 2060 | return dest; 2061 | }; 2062 | 2063 | /** 2064 | * Calculates the dot product of two quaternions 2065 | * 2066 | * @param {quat4} quat First operand 2067 | * @param {quat4} quat2 Second operand 2068 | * 2069 | * @return {number} Dot product of quat and quat2 2070 | */ 2071 | quat4.dot = function(quat, quat2){ 2072 | return quat[0]*quat2[0] + quat[1]*quat2[1] + quat[2]*quat2[2] + quat[3]*quat2[3]; 2073 | }; 2074 | 2075 | /** 2076 | * Calculates the inverse of a quat4 2077 | * 2078 | * @param {quat4} quat quat4 to calculate inverse of 2079 | * @param {quat4} [dest] quat4 receiving inverse values. If not specified result is written to quat 2080 | * 2081 | * @returns {quat4} dest if specified, quat otherwise 2082 | */ 2083 | quat4.inverse = function(quat, dest) { 2084 | var q0 = quat[0], q1 = quat[1], q2 = quat[2], q3 = quat[3], 2085 | dot = q0*q0 + q1*q1 + q2*q2 + q3*q3, 2086 | invDot = dot ? 1.0/dot : 0; 2087 | 2088 | // TODO: Would be faster to return [0,0,0,0] immediately if dot == 0 2089 | 2090 | if(!dest || quat === dest) { 2091 | quat[0] *= -invDot; 2092 | quat[1] *= -invDot; 2093 | quat[2] *= -invDot; 2094 | quat[3] *= invDot; 2095 | return quat; 2096 | } 2097 | dest[0] = -quat[0]*invDot; 2098 | dest[1] = -quat[1]*invDot; 2099 | dest[2] = -quat[2]*invDot; 2100 | dest[3] = quat[3]*invDot; 2101 | return dest; 2102 | }; 2103 | 2104 | 2105 | /** 2106 | * Calculates the conjugate of a quat4 2107 | * If the quaternion is normalized, this function is faster than quat4.inverse and produces the same result. 2108 | * 2109 | * @param {quat4} quat quat4 to calculate conjugate of 2110 | * @param {quat4} [dest] quat4 receiving conjugate values. If not specified result is written to quat 2111 | * 2112 | * @returns {quat4} dest if specified, quat otherwise 2113 | */ 2114 | quat4.conjugate = function (quat, dest) { 2115 | if (!dest || quat === dest) { 2116 | quat[0] *= -1; 2117 | quat[1] *= -1; 2118 | quat[2] *= -1; 2119 | return quat; 2120 | } 2121 | dest[0] = -quat[0]; 2122 | dest[1] = -quat[1]; 2123 | dest[2] = -quat[2]; 2124 | dest[3] = quat[3]; 2125 | return dest; 2126 | }; 2127 | 2128 | /** 2129 | * Calculates the length of a quat4 2130 | * 2131 | * Params: 2132 | * @param {quat4} quat quat4 to calculate length of 2133 | * 2134 | * @returns Length of quat 2135 | */ 2136 | quat4.length = function (quat) { 2137 | var x = quat[0], y = quat[1], z = quat[2], w = quat[3]; 2138 | return Math.sqrt(x * x + y * y + z * z + w * w); 2139 | }; 2140 | 2141 | /** 2142 | * Generates a unit quaternion of the same direction as the provided quat4 2143 | * If quaternion length is 0, returns [0, 0, 0, 0] 2144 | * 2145 | * @param {quat4} quat quat4 to normalize 2146 | * @param {quat4} [dest] quat4 receiving operation result. If not specified result is written to quat 2147 | * 2148 | * @returns {quat4} dest if specified, quat otherwise 2149 | */ 2150 | quat4.normalize = function (quat, dest) { 2151 | if (!dest) { dest = quat; } 2152 | 2153 | var x = quat[0], y = quat[1], z = quat[2], w = quat[3], 2154 | len = Math.sqrt(x * x + y * y + z * z + w * w); 2155 | if (len === 0) { 2156 | dest[0] = 0; 2157 | dest[1] = 0; 2158 | dest[2] = 0; 2159 | dest[3] = 0; 2160 | return dest; 2161 | } 2162 | len = 1 / len; 2163 | dest[0] = x * len; 2164 | dest[1] = y * len; 2165 | dest[2] = z * len; 2166 | dest[3] = w * len; 2167 | 2168 | return dest; 2169 | }; 2170 | 2171 | /** 2172 | * Performs quaternion addition 2173 | * 2174 | * @param {quat4} quat First operand 2175 | * @param {quat4} quat2 Second operand 2176 | * @param {quat4} [dest] quat4 receiving operation result. If not specified result is written to quat 2177 | * 2178 | * @returns {quat4} dest if specified, quat otherwise 2179 | */ 2180 | quat4.add = function (quat, quat2, dest) { 2181 | if(!dest || quat === dest) { 2182 | quat[0] += quat2[0]; 2183 | quat[1] += quat2[1]; 2184 | quat[2] += quat2[2]; 2185 | quat[3] += quat2[3]; 2186 | return quat; 2187 | } 2188 | dest[0] = quat[0]+quat2[0]; 2189 | dest[1] = quat[1]+quat2[1]; 2190 | dest[2] = quat[2]+quat2[2]; 2191 | dest[3] = quat[3]+quat2[3]; 2192 | return dest; 2193 | }; 2194 | 2195 | /** 2196 | * Performs a quaternion multiplication 2197 | * 2198 | * @param {quat4} quat First operand 2199 | * @param {quat4} quat2 Second operand 2200 | * @param {quat4} [dest] quat4 receiving operation result. If not specified result is written to quat 2201 | * 2202 | * @returns {quat4} dest if specified, quat otherwise 2203 | */ 2204 | quat4.multiply = function (quat, quat2, dest) { 2205 | if (!dest) { dest = quat; } 2206 | 2207 | var qax = quat[0], qay = quat[1], qaz = quat[2], qaw = quat[3], 2208 | qbx = quat2[0], qby = quat2[1], qbz = quat2[2], qbw = quat2[3]; 2209 | 2210 | dest[0] = qax * qbw + qaw * qbx + qay * qbz - qaz * qby; 2211 | dest[1] = qay * qbw + qaw * qby + qaz * qbx - qax * qbz; 2212 | dest[2] = qaz * qbw + qaw * qbz + qax * qby - qay * qbx; 2213 | dest[3] = qaw * qbw - qax * qbx - qay * qby - qaz * qbz; 2214 | 2215 | return dest; 2216 | }; 2217 | 2218 | /** 2219 | * Transforms a vec3 with the given quaternion 2220 | * 2221 | * @param {quat4} quat quat4 to transform the vector with 2222 | * @param {vec3} vec vec3 to transform 2223 | * @param {vec3} [dest] vec3 receiving operation result. If not specified result is written to vec 2224 | * 2225 | * @returns dest if specified, vec otherwise 2226 | */ 2227 | quat4.multiplyVec3 = function (quat, vec, dest) { 2228 | if (!dest) { dest = vec; } 2229 | 2230 | var x = vec[0], y = vec[1], z = vec[2], 2231 | qx = quat[0], qy = quat[1], qz = quat[2], qw = quat[3], 2232 | 2233 | // calculate quat * vec 2234 | ix = qw * x + qy * z - qz * y, 2235 | iy = qw * y + qz * x - qx * z, 2236 | iz = qw * z + qx * y - qy * x, 2237 | iw = -qx * x - qy * y - qz * z; 2238 | 2239 | // calculate result * inverse quat 2240 | dest[0] = ix * qw + iw * -qx + iy * -qz - iz * -qy; 2241 | dest[1] = iy * qw + iw * -qy + iz * -qx - ix * -qz; 2242 | dest[2] = iz * qw + iw * -qz + ix * -qy - iy * -qx; 2243 | 2244 | return dest; 2245 | }; 2246 | 2247 | /** 2248 | * Multiplies the components of a quaternion by a scalar value 2249 | * 2250 | * @param {quat4} quat to scale 2251 | * @param {number} val Value to scale by 2252 | * @param {quat4} [dest] quat4 receiving operation result. If not specified result is written to quat 2253 | * 2254 | * @returns {quat4} dest if specified, quat otherwise 2255 | */ 2256 | quat4.scale = function (quat, val, dest) { 2257 | if(!dest || quat === dest) { 2258 | quat[0] *= val; 2259 | quat[1] *= val; 2260 | quat[2] *= val; 2261 | quat[3] *= val; 2262 | return quat; 2263 | } 2264 | dest[0] = quat[0]*val; 2265 | dest[1] = quat[1]*val; 2266 | dest[2] = quat[2]*val; 2267 | dest[3] = quat[3]*val; 2268 | return dest; 2269 | }; 2270 | 2271 | /** 2272 | * Calculates a 3x3 matrix from the given quat4 2273 | * 2274 | * @param {quat4} quat quat4 to create matrix from 2275 | * @param {mat3} [dest] mat3 receiving operation result 2276 | * 2277 | * @returns {mat3} dest if specified, a new mat3 otherwise 2278 | */ 2279 | quat4.toMat3 = function (quat, dest) { 2280 | if (!dest) { dest = mat3.create(); } 2281 | 2282 | var x = quat[0], y = quat[1], z = quat[2], w = quat[3], 2283 | x2 = x + x, 2284 | y2 = y + y, 2285 | z2 = z + z, 2286 | 2287 | xx = x * x2, 2288 | xy = x * y2, 2289 | xz = x * z2, 2290 | yy = y * y2, 2291 | yz = y * z2, 2292 | zz = z * z2, 2293 | wx = w * x2, 2294 | wy = w * y2, 2295 | wz = w * z2; 2296 | 2297 | dest[0] = 1 - (yy + zz); 2298 | dest[1] = xy + wz; 2299 | dest[2] = xz - wy; 2300 | 2301 | dest[3] = xy - wz; 2302 | dest[4] = 1 - (xx + zz); 2303 | dest[5] = yz + wx; 2304 | 2305 | dest[6] = xz + wy; 2306 | dest[7] = yz - wx; 2307 | dest[8] = 1 - (xx + yy); 2308 | 2309 | return dest; 2310 | }; 2311 | 2312 | /** 2313 | * Calculates a 4x4 matrix from the given quat4 2314 | * 2315 | * @param {quat4} quat quat4 to create matrix from 2316 | * @param {mat4} [dest] mat4 receiving operation result 2317 | * 2318 | * @returns {mat4} dest if specified, a new mat4 otherwise 2319 | */ 2320 | quat4.toMat4 = function (quat, dest) { 2321 | if (!dest) { dest = mat4.create(); } 2322 | 2323 | var x = quat[0], y = quat[1], z = quat[2], w = quat[3], 2324 | x2 = x + x, 2325 | y2 = y + y, 2326 | z2 = z + z, 2327 | 2328 | xx = x * x2, 2329 | xy = x * y2, 2330 | xz = x * z2, 2331 | yy = y * y2, 2332 | yz = y * z2, 2333 | zz = z * z2, 2334 | wx = w * x2, 2335 | wy = w * y2, 2336 | wz = w * z2; 2337 | 2338 | dest[0] = 1 - (yy + zz); 2339 | dest[1] = xy + wz; 2340 | dest[2] = xz - wy; 2341 | dest[3] = 0; 2342 | 2343 | dest[4] = xy - wz; 2344 | dest[5] = 1 - (xx + zz); 2345 | dest[6] = yz + wx; 2346 | dest[7] = 0; 2347 | 2348 | dest[8] = xz + wy; 2349 | dest[9] = yz - wx; 2350 | dest[10] = 1 - (xx + yy); 2351 | dest[11] = 0; 2352 | 2353 | dest[12] = 0; 2354 | dest[13] = 0; 2355 | dest[14] = 0; 2356 | dest[15] = 1; 2357 | 2358 | return dest; 2359 | }; 2360 | 2361 | /** 2362 | * Performs a spherical linear interpolation between two quat4 2363 | * 2364 | * @param {quat4} quat First quaternion 2365 | * @param {quat4} quat2 Second quaternion 2366 | * @param {number} slerp Interpolation amount between the two inputs 2367 | * @param {quat4} [dest] quat4 receiving operation result. If not specified result is written to quat 2368 | * 2369 | * @returns {quat4} dest if specified, quat otherwise 2370 | */ 2371 | quat4.slerp = function (quat, quat2, slerp, dest) { 2372 | if (!dest) { dest = quat; } 2373 | 2374 | var cosHalfTheta = quat[0] * quat2[0] + quat[1] * quat2[1] + quat[2] * quat2[2] + quat[3] * quat2[3], 2375 | halfTheta, 2376 | sinHalfTheta, 2377 | ratioA, 2378 | ratioB; 2379 | 2380 | if (Math.abs(cosHalfTheta) >= 1.0) { 2381 | if (dest !== quat) { 2382 | dest[0] = quat[0]; 2383 | dest[1] = quat[1]; 2384 | dest[2] = quat[2]; 2385 | dest[3] = quat[3]; 2386 | } 2387 | return dest; 2388 | } 2389 | 2390 | halfTheta = Math.acos(cosHalfTheta); 2391 | sinHalfTheta = Math.sqrt(1.0 - cosHalfTheta * cosHalfTheta); 2392 | 2393 | if (Math.abs(sinHalfTheta) < 0.001) { 2394 | dest[0] = (quat[0] * 0.5 + quat2[0] * 0.5); 2395 | dest[1] = (quat[1] * 0.5 + quat2[1] * 0.5); 2396 | dest[2] = (quat[2] * 0.5 + quat2[2] * 0.5); 2397 | dest[3] = (quat[3] * 0.5 + quat2[3] * 0.5); 2398 | return dest; 2399 | } 2400 | 2401 | ratioA = Math.sin((1 - slerp) * halfTheta) / sinHalfTheta; 2402 | ratioB = Math.sin(slerp * halfTheta) / sinHalfTheta; 2403 | 2404 | dest[0] = (quat[0] * ratioA + quat2[0] * ratioB); 2405 | dest[1] = (quat[1] * ratioA + quat2[1] * ratioB); 2406 | dest[2] = (quat[2] * ratioA + quat2[2] * ratioB); 2407 | dest[3] = (quat[3] * ratioA + quat2[3] * ratioB); 2408 | 2409 | return dest; 2410 | }; 2411 | 2412 | /** 2413 | * Creates a quaternion from the given 3x3 rotation matrix. 2414 | * If dest is omitted, a new quaternion will be created. 2415 | * 2416 | * @param {mat3} mat the rotation matrix 2417 | * @param {quat4} [dest] an optional receiving quaternion 2418 | * 2419 | * @returns {quat4} the quaternion constructed from the rotation matrix 2420 | * 2421 | */ 2422 | quat4.fromRotationMatrix = function(mat, dest) { 2423 | if (!dest) dest = quat4.create(); 2424 | 2425 | // Algorithm in Ken Shoemake's article in 1987 SIGGRAPH course notes 2426 | // article "Quaternion Calculus and Fast Animation". 2427 | 2428 | var fTrace = mat[0] + mat[4] + mat[8]; 2429 | var fRoot; 2430 | 2431 | if ( fTrace > 0.0 ) { 2432 | // |w| > 1/2, may as well choose w > 1/2 2433 | fRoot = Math.sqrt(fTrace + 1.0); // 2w 2434 | dest[3] = 0.5 * fRoot; 2435 | fRoot = 0.5/fRoot; // 1/(4w) 2436 | dest[0] = (mat[7]-mat[5])*fRoot; 2437 | dest[1] = (mat[2]-mat[6])*fRoot; 2438 | dest[2] = (mat[3]-mat[1])*fRoot; 2439 | } else { 2440 | // |w| <= 1/2 2441 | var s_iNext = quat4.fromRotationMatrix.s_iNext = quat4.fromRotationMatrix.s_iNext || [1,2,0]; 2442 | var i = 0; 2443 | if ( mat[4] > mat[0] ) 2444 | i = 1; 2445 | if ( mat[8] > mat[i*3+i] ) 2446 | i = 2; 2447 | var j = s_iNext[i]; 2448 | var k = s_iNext[j]; 2449 | 2450 | fRoot = Math.sqrt(mat[i*3+i]-mat[j*3+j]-mat[k*3+k] + 1.0); 2451 | dest[i] = 0.5 * fRoot; 2452 | fRoot = 0.5 / fRoot; 2453 | dest[3] = (mat[k*3+j] - mat[j*3+k]) * fRoot; 2454 | dest[j] = (mat[j*3+i] + mat[i*3+j]) * fRoot; 2455 | dest[k] = (mat[k*3+i] + mat[i*3+k]) * fRoot; 2456 | } 2457 | 2458 | return dest; 2459 | }; 2460 | 2461 | /** 2462 | * Alias. See the description for quat4.fromRotationMatrix(). 2463 | */ 2464 | mat3.toQuat4 = quat4.fromRotationMatrix; 2465 | 2466 | (function() { 2467 | var mat = mat3.create(); 2468 | 2469 | /** 2470 | * Creates a quaternion from the 3 given vectors. They must be perpendicular 2471 | * to one another and represent the X, Y and Z axes. 2472 | * 2473 | * If dest is omitted, a new quat4 will be created. 2474 | * 2475 | * Example: The default OpenGL orientation has a view vector [0, 0, -1], 2476 | * right vector [1, 0, 0], and up vector [0, 1, 0]. A quaternion representing 2477 | * this orientation could be constructed with: 2478 | * 2479 | * quat = quat4.fromAxes([0, 0, -1], [1, 0, 0], [0, 1, 0], quat4.create()); 2480 | * 2481 | * @param {vec3} view the view vector, or direction the object is pointing in 2482 | * @param {vec3} right the right vector, or direction to the "right" of the object 2483 | * @param {vec3} up the up vector, or direction towards the object's "up" 2484 | * @param {quat4} [dest] an optional receiving quat4 2485 | * 2486 | * @returns {quat4} dest 2487 | **/ 2488 | quat4.fromAxes = function(view, right, up, dest) { 2489 | mat[0] = right[0]; 2490 | mat[3] = right[1]; 2491 | mat[6] = right[2]; 2492 | 2493 | mat[1] = up[0]; 2494 | mat[4] = up[1]; 2495 | mat[7] = up[2]; 2496 | 2497 | mat[2] = view[0]; 2498 | mat[5] = view[1]; 2499 | mat[8] = view[2]; 2500 | 2501 | return quat4.fromRotationMatrix(mat, dest); 2502 | }; 2503 | })(); 2504 | 2505 | /** 2506 | * Sets a quat4 to the Identity and returns it. 2507 | * 2508 | * @param {quat4} [dest] quat4 to set. If omitted, a 2509 | * new quat4 will be created. 2510 | * 2511 | * @returns {quat4} dest 2512 | */ 2513 | quat4.identity = function(dest) { 2514 | if (!dest) dest = quat4.create(); 2515 | dest[0] = 0; 2516 | dest[1] = 0; 2517 | dest[2] = 0; 2518 | dest[3] = 1; 2519 | return dest; 2520 | }; 2521 | 2522 | /** 2523 | * Sets a quat4 from the given angle and rotation axis, 2524 | * then returns it. If dest is not given, a new quat4 is created. 2525 | * 2526 | * @param {Number} angle the angle in radians 2527 | * @param {vec3} axis the axis around which to rotate 2528 | * @param {quat4} [dest] the optional quat4 to store the result 2529 | * 2530 | * @returns {quat4} dest 2531 | **/ 2532 | quat4.fromAngleAxis = function(angle, axis, dest) { 2533 | // The quaternion representing the rotation is 2534 | // q = cos(A/2)+sin(A/2)*(x*i+y*j+z*k) 2535 | if (!dest) dest = quat4.create(); 2536 | 2537 | var half = angle * 0.5; 2538 | var s = Math.sin(half); 2539 | dest[3] = Math.cos(half); 2540 | dest[0] = s * axis[0]; 2541 | dest[1] = s * axis[1]; 2542 | dest[2] = s * axis[2]; 2543 | 2544 | return dest; 2545 | }; 2546 | 2547 | /** 2548 | * Stores the angle and axis in a vec4, where the XYZ components represent 2549 | * the axis and the W (4th) component is the angle in radians. 2550 | * 2551 | * If dest is not given, src will be modified in place and returned, after 2552 | * which it should not be considered not a quaternion (just an axis and angle). 2553 | * 2554 | * @param {quat4} quat the quaternion whose angle and axis to store 2555 | * @param {vec4} [dest] the optional vec4 to receive the data 2556 | * 2557 | * @returns {vec4} dest 2558 | */ 2559 | quat4.toAngleAxis = function(src, dest) { 2560 | if (!dest) dest = src; 2561 | // The quaternion representing the rotation is 2562 | // q = cos(A/2)+sin(A/2)*(x*i+y*j+z*k) 2563 | 2564 | var sqrlen = src[0]*src[0]+src[1]*src[1]+src[2]*src[2]; 2565 | if (sqrlen > 0) 2566 | { 2567 | dest[3] = 2 * Math.acos(src[3]); 2568 | var invlen = glMath.invsqrt(sqrlen); 2569 | dest[0] = src[0]*invlen; 2570 | dest[1] = src[1]*invlen; 2571 | dest[2] = src[2]*invlen; 2572 | } else { 2573 | // angle is 0 (mod 2*pi), so any axis will do 2574 | dest[3] = 0; 2575 | dest[0] = 1; 2576 | dest[1] = 0; 2577 | dest[2] = 0; 2578 | } 2579 | 2580 | return dest; 2581 | }; 2582 | 2583 | /** 2584 | * Returns a string representation of a quaternion 2585 | * 2586 | * @param {quat4} quat quat4 to represent as a string 2587 | * 2588 | * @returns {string} String representation of quat 2589 | */ 2590 | quat4.str = function (quat) { 2591 | return '[' + quat[0] + ', ' + quat[1] + ', ' + quat[2] + ', ' + quat[3] + ']'; 2592 | }; 2593 | 2594 | /** 2595 | * @class 2 Dimensional Vector 2596 | * @name vec2 2597 | */ 2598 | var vec2 = {}; 2599 | 2600 | /** 2601 | * Creates a new vec2, initializing it from vec if vec 2602 | * is given. 2603 | * 2604 | * @param {vec2} [vec] the vector's initial contents 2605 | * @returns {vec2} a new 2D vector 2606 | */ 2607 | vec2.create = function(vec) { 2608 | var dest = new MatrixArray(2); 2609 | 2610 | if (vec) { 2611 | dest[0] = vec[0]; 2612 | dest[1] = vec[1]; 2613 | } else { 2614 | dest[0] = 0; 2615 | dest[1] = 0; 2616 | } 2617 | return dest; 2618 | }; 2619 | 2620 | /** 2621 | * Creates a new instance of a vec2, initializing it with the given arguments 2622 | * 2623 | * @param {number} x X value 2624 | * @param {number} y Y value 2625 | 2626 | * @returns {vec2} New vec2 2627 | */ 2628 | vec2.createFrom = function (x, y) { 2629 | var dest = new MatrixArray(2); 2630 | 2631 | dest[0] = x; 2632 | dest[1] = y; 2633 | 2634 | return dest; 2635 | }; 2636 | 2637 | /** 2638 | * Adds the vec2's together. If dest is given, the result 2639 | * is stored there. Otherwise, the result is stored in vecB. 2640 | * 2641 | * @param {vec2} vecA the first operand 2642 | * @param {vec2} vecB the second operand 2643 | * @param {vec2} [dest] the optional receiving vector 2644 | * @returns {vec2} dest 2645 | */ 2646 | vec2.add = function(vecA, vecB, dest) { 2647 | if (!dest) dest = vecB; 2648 | dest[0] = vecA[0] + vecB[0]; 2649 | dest[1] = vecA[1] + vecB[1]; 2650 | return dest; 2651 | }; 2652 | 2653 | /** 2654 | * Subtracts vecB from vecA. If dest is given, the result 2655 | * is stored there. Otherwise, the result is stored in vecB. 2656 | * 2657 | * @param {vec2} vecA the first operand 2658 | * @param {vec2} vecB the second operand 2659 | * @param {vec2} [dest] the optional receiving vector 2660 | * @returns {vec2} dest 2661 | */ 2662 | vec2.subtract = function(vecA, vecB, dest) { 2663 | if (!dest) dest = vecB; 2664 | dest[0] = vecA[0] - vecB[0]; 2665 | dest[1] = vecA[1] - vecB[1]; 2666 | return dest; 2667 | }; 2668 | 2669 | /** 2670 | * Multiplies vecA with vecB. If dest is given, the result 2671 | * is stored there. Otherwise, the result is stored in vecB. 2672 | * 2673 | * @param {vec2} vecA the first operand 2674 | * @param {vec2} vecB the second operand 2675 | * @param {vec2} [dest] the optional receiving vector 2676 | * @returns {vec2} dest 2677 | */ 2678 | vec2.multiply = function(vecA, vecB, dest) { 2679 | if (!dest) dest = vecB; 2680 | dest[0] = vecA[0] * vecB[0]; 2681 | dest[1] = vecA[1] * vecB[1]; 2682 | return dest; 2683 | }; 2684 | 2685 | /** 2686 | * Divides vecA by vecB. If dest is given, the result 2687 | * is stored there. Otherwise, the result is stored in vecB. 2688 | * 2689 | * @param {vec2} vecA the first operand 2690 | * @param {vec2} vecB the second operand 2691 | * @param {vec2} [dest] the optional receiving vector 2692 | * @returns {vec2} dest 2693 | */ 2694 | vec2.divide = function(vecA, vecB, dest) { 2695 | if (!dest) dest = vecB; 2696 | dest[0] = vecA[0] / vecB[0]; 2697 | dest[1] = vecA[1] / vecB[1]; 2698 | return dest; 2699 | }; 2700 | 2701 | /** 2702 | * Scales vecA by some scalar number. If dest is given, the result 2703 | * is stored there. Otherwise, the result is stored in vecA. 2704 | * 2705 | * This is the same as multiplying each component of vecA 2706 | * by the given scalar. 2707 | * 2708 | * @param {vec2} vecA the vector to be scaled 2709 | * @param {Number} scalar the amount to scale the vector by 2710 | * @param {vec2} [dest] the optional receiving vector 2711 | * @returns {vec2} dest 2712 | */ 2713 | vec2.scale = function(vecA, scalar, dest) { 2714 | if (!dest) dest = vecA; 2715 | dest[0] = vecA[0] * scalar; 2716 | dest[1] = vecA[1] * scalar; 2717 | return dest; 2718 | }; 2719 | 2720 | /** 2721 | * Calculates the euclidian distance between two vec2 2722 | * 2723 | * Params: 2724 | * @param {vec2} vecA First vector 2725 | * @param {vec2} vecB Second vector 2726 | * 2727 | * @returns {number} Distance between vecA and vecB 2728 | */ 2729 | vec2.dist = function (vecA, vecB) { 2730 | var x = vecB[0] - vecA[0], 2731 | y = vecB[1] - vecA[1]; 2732 | return Math.sqrt(x*x + y*y); 2733 | }; 2734 | 2735 | /** 2736 | * Copies the values of one vec2 to another 2737 | * 2738 | * @param {vec2} vec vec2 containing values to copy 2739 | * @param {vec2} dest vec2 receiving copied values 2740 | * 2741 | * @returns {vec2} dest 2742 | */ 2743 | vec2.set = function (vec, dest) { 2744 | dest[0] = vec[0]; 2745 | dest[1] = vec[1]; 2746 | return dest; 2747 | }; 2748 | 2749 | /** 2750 | * Compares two vectors for equality within a certain margin of error 2751 | * 2752 | * @param {vec2} a First vector 2753 | * @param {vec2} b Second vector 2754 | * 2755 | * @returns {Boolean} True if a is equivalent to b 2756 | */ 2757 | vec2.equal = function (a, b) { 2758 | return a === b || ( 2759 | Math.abs(a[0] - b[0]) < FLOAT_EPSILON && 2760 | Math.abs(a[1] - b[1]) < FLOAT_EPSILON 2761 | ); 2762 | }; 2763 | 2764 | /** 2765 | * Negates the components of a vec2 2766 | * 2767 | * @param {vec2} vec vec2 to negate 2768 | * @param {vec2} [dest] vec2 receiving operation result. If not specified result is written to vec 2769 | * 2770 | * @returns {vec2} dest if specified, vec otherwise 2771 | */ 2772 | vec2.negate = function (vec, dest) { 2773 | if (!dest) { dest = vec; } 2774 | dest[0] = -vec[0]; 2775 | dest[1] = -vec[1]; 2776 | return dest; 2777 | }; 2778 | 2779 | /** 2780 | * Normlize a vec2 2781 | * 2782 | * @param {vec2} vec vec2 to normalize 2783 | * @param {vec2} [dest] vec2 receiving operation result. If not specified result is written to vec 2784 | * 2785 | * @returns {vec2} dest if specified, vec otherwise 2786 | */ 2787 | vec2.normalize = function (vec, dest) { 2788 | if (!dest) { dest = vec; } 2789 | var mag = vec[0] * vec[0] + vec[1] * vec[1]; 2790 | if (mag > 0) { 2791 | mag = Math.sqrt(mag); 2792 | dest[0] = vec[0] / mag; 2793 | dest[1] = vec[1] / mag; 2794 | } else { 2795 | dest[0] = dest[1] = 0; 2796 | } 2797 | return dest; 2798 | }; 2799 | 2800 | /** 2801 | * Computes the cross product of two vec2's. Note that the cross product must by definition 2802 | * produce a 3D vector. If a dest vector is given, it will contain the resultant 3D vector. 2803 | * Otherwise, a scalar number will be returned, representing the vector's Z coordinate, since 2804 | * its X and Y must always equal 0. 2805 | * 2806 | * Examples: 2807 | * var crossResult = vec3.create(); 2808 | * vec2.cross([1, 2], [3, 4], crossResult); 2809 | * //=> [0, 0, -2] 2810 | * 2811 | * vec2.cross([1, 2], [3, 4]); 2812 | * //=> -2 2813 | * 2814 | * See http://stackoverflow.com/questions/243945/calculating-a-2d-vectors-cross-product 2815 | * for some interesting facts. 2816 | * 2817 | * @param {vec2} vecA left operand 2818 | * @param {vec2} vecB right operand 2819 | * @param {vec2} [dest] optional vec2 receiving result. If not specified a scalar is returned 2820 | * 2821 | */ 2822 | vec2.cross = function (vecA, vecB, dest) { 2823 | var z = vecA[0] * vecB[1] - vecA[1] * vecB[0]; 2824 | if (!dest) return z; 2825 | dest[0] = dest[1] = 0; 2826 | dest[2] = z; 2827 | return dest; 2828 | }; 2829 | 2830 | /** 2831 | * Caclulates the length of a vec2 2832 | * 2833 | * @param {vec2} vec vec2 to calculate length of 2834 | * 2835 | * @returns {Number} Length of vec 2836 | */ 2837 | vec2.length = function (vec) { 2838 | var x = vec[0], y = vec[1]; 2839 | return Math.sqrt(x * x + y * y); 2840 | }; 2841 | 2842 | /** 2843 | * Caclulates the squared length of a vec2 2844 | * 2845 | * @param {vec2} vec vec2 to calculate squared length of 2846 | * 2847 | * @returns {Number} Squared Length of vec 2848 | */ 2849 | vec2.squaredLength = function (vec) { 2850 | var x = vec[0], y = vec[1]; 2851 | return x * x + y * y; 2852 | }; 2853 | 2854 | /** 2855 | * Caclulates the dot product of two vec2s 2856 | * 2857 | * @param {vec2} vecA First operand 2858 | * @param {vec2} vecB Second operand 2859 | * 2860 | * @returns {Number} Dot product of vecA and vecB 2861 | */ 2862 | vec2.dot = function (vecA, vecB) { 2863 | return vecA[0] * vecB[0] + vecA[1] * vecB[1]; 2864 | }; 2865 | 2866 | /** 2867 | * Generates a 2D unit vector pointing from one vector to another 2868 | * 2869 | * @param {vec2} vecA Origin vec2 2870 | * @param {vec2} vecB vec2 to point to 2871 | * @param {vec2} [dest] vec2 receiving operation result. If not specified result is written to vecA 2872 | * 2873 | * @returns {vec2} dest if specified, vecA otherwise 2874 | */ 2875 | vec2.direction = function (vecA, vecB, dest) { 2876 | if (!dest) { dest = vecA; } 2877 | 2878 | var x = vecA[0] - vecB[0], 2879 | y = vecA[1] - vecB[1], 2880 | len = x * x + y * y; 2881 | 2882 | if (!len) { 2883 | dest[0] = 0; 2884 | dest[1] = 0; 2885 | dest[2] = 0; 2886 | return dest; 2887 | } 2888 | 2889 | len = 1 / Math.sqrt(len); 2890 | dest[0] = x * len; 2891 | dest[1] = y * len; 2892 | return dest; 2893 | }; 2894 | 2895 | /** 2896 | * Performs a linear interpolation between two vec2 2897 | * 2898 | * @param {vec2} vecA First vector 2899 | * @param {vec2} vecB Second vector 2900 | * @param {Number} lerp Interpolation amount between the two inputs 2901 | * @param {vec2} [dest] vec2 receiving operation result. If not specified result is written to vecA 2902 | * 2903 | * @returns {vec2} dest if specified, vecA otherwise 2904 | */ 2905 | vec2.lerp = function (vecA, vecB, lerp, dest) { 2906 | if (!dest) { dest = vecA; } 2907 | dest[0] = vecA[0] + lerp * (vecB[0] - vecA[0]); 2908 | dest[1] = vecA[1] + lerp * (vecB[1] - vecA[1]); 2909 | return dest; 2910 | }; 2911 | 2912 | /** 2913 | * Returns a string representation of a vector 2914 | * 2915 | * @param {vec2} vec Vector to represent as a string 2916 | * 2917 | * @returns {String} String representation of vec 2918 | */ 2919 | vec2.str = function (vec) { 2920 | return '[' + vec[0] + ', ' + vec[1] + ']'; 2921 | }; 2922 | 2923 | /** 2924 | * @class 2x2 Matrix 2925 | * @name mat2 2926 | */ 2927 | var mat2 = {}; 2928 | 2929 | /** 2930 | * Creates a new 2x2 matrix. If src is given, the new matrix 2931 | * is initialized to those values. 2932 | * 2933 | * @param {mat2} [src] the seed values for the new matrix, if any 2934 | * @returns {mat2} a new matrix 2935 | */ 2936 | mat2.create = function(src) { 2937 | var dest = new MatrixArray(4); 2938 | 2939 | if (src) { 2940 | dest[0] = src[0]; 2941 | dest[1] = src[1]; 2942 | dest[2] = src[2]; 2943 | dest[3] = src[3]; 2944 | } else { 2945 | dest[0] = dest[1] = dest[2] = dest[3] = 0; 2946 | } 2947 | return dest; 2948 | }; 2949 | 2950 | /** 2951 | * Creates a new instance of a mat2, initializing it with the given arguments 2952 | * 2953 | * @param {number} m00 2954 | * @param {number} m01 2955 | * @param {number} m10 2956 | * @param {number} m11 2957 | 2958 | * @returns {mat2} New mat2 2959 | */ 2960 | mat2.createFrom = function (m00, m01, m10, m11) { 2961 | var dest = new MatrixArray(4); 2962 | 2963 | dest[0] = m00; 2964 | dest[1] = m01; 2965 | dest[2] = m10; 2966 | dest[3] = m11; 2967 | 2968 | return dest; 2969 | }; 2970 | 2971 | /** 2972 | * Copies the values of one mat2 to another 2973 | * 2974 | * @param {mat2} mat mat2 containing values to copy 2975 | * @param {mat2} dest mat2 receiving copied values 2976 | * 2977 | * @returns {mat2} dest 2978 | */ 2979 | mat2.set = function (mat, dest) { 2980 | dest[0] = mat[0]; 2981 | dest[1] = mat[1]; 2982 | dest[2] = mat[2]; 2983 | dest[3] = mat[3]; 2984 | return dest; 2985 | }; 2986 | 2987 | /** 2988 | * Compares two matrices for equality within a certain margin of error 2989 | * 2990 | * @param {mat2} a First matrix 2991 | * @param {mat2} b Second matrix 2992 | * 2993 | * @returns {Boolean} True if a is equivalent to b 2994 | */ 2995 | mat2.equal = function (a, b) { 2996 | return a === b || ( 2997 | Math.abs(a[0] - b[0]) < FLOAT_EPSILON && 2998 | Math.abs(a[1] - b[1]) < FLOAT_EPSILON && 2999 | Math.abs(a[2] - b[2]) < FLOAT_EPSILON && 3000 | Math.abs(a[3] - b[3]) < FLOAT_EPSILON 3001 | ); 3002 | }; 3003 | 3004 | /** 3005 | * Sets a mat2 to an identity matrix 3006 | * 3007 | * @param {mat2} [dest] mat2 to set. If omitted a new one will be created. 3008 | * 3009 | * @returns {mat2} dest 3010 | */ 3011 | mat2.identity = function (dest) { 3012 | if (!dest) { dest = mat2.create(); } 3013 | dest[0] = 1; 3014 | dest[1] = 0; 3015 | dest[2] = 0; 3016 | dest[3] = 1; 3017 | return dest; 3018 | }; 3019 | 3020 | /** 3021 | * Transposes a mat2 (flips the values over the diagonal) 3022 | * 3023 | * @param {mat2} mat mat2 to transpose 3024 | * @param {mat2} [dest] mat2 receiving transposed values. If not specified result is written to mat 3025 | * 3026 | * @param {mat2} dest if specified, mat otherwise 3027 | */ 3028 | mat2.transpose = function (mat, dest) { 3029 | // If we are transposing ourselves we can skip a few steps but have to cache some values 3030 | if (!dest || mat === dest) { 3031 | var a00 = mat[1]; 3032 | mat[1] = mat[2]; 3033 | mat[2] = a00; 3034 | return mat; 3035 | } 3036 | 3037 | dest[0] = mat[0]; 3038 | dest[1] = mat[2]; 3039 | dest[2] = mat[1]; 3040 | dest[3] = mat[3]; 3041 | return dest; 3042 | }; 3043 | 3044 | /** 3045 | * Calculates the determinant of a mat2 3046 | * 3047 | * @param {mat2} mat mat2 to calculate determinant of 3048 | * 3049 | * @returns {Number} determinant of mat 3050 | */ 3051 | mat2.determinant = function (mat) { 3052 | return mat[0] * mat[3] - mat[2] * mat[1]; 3053 | }; 3054 | 3055 | /** 3056 | * Calculates the inverse matrix of a mat2 3057 | * 3058 | * @param {mat2} mat mat2 to calculate inverse of 3059 | * @param {mat2} [dest] mat2 receiving inverse matrix. If not specified result is written to mat 3060 | * 3061 | * @param {mat2} dest is specified, mat otherwise, null if matrix cannot be inverted 3062 | */ 3063 | mat2.inverse = function (mat, dest) { 3064 | if (!dest) { dest = mat; } 3065 | var a0 = mat[0], a1 = mat[1], a2 = mat[2], a3 = mat[3]; 3066 | var det = a0 * a3 - a2 * a1; 3067 | if (!det) return null; 3068 | 3069 | det = 1.0 / det; 3070 | dest[0] = a3 * det; 3071 | dest[1] = -a1 * det; 3072 | dest[2] = -a2 * det; 3073 | dest[3] = a0 * det; 3074 | return dest; 3075 | }; 3076 | 3077 | /** 3078 | * Performs a matrix multiplication 3079 | * 3080 | * @param {mat2} matA First operand 3081 | * @param {mat2} matB Second operand 3082 | * @param {mat2} [dest] mat2 receiving operation result. If not specified result is written to matA 3083 | * 3084 | * @returns {mat2} dest if specified, matA otherwise 3085 | */ 3086 | mat2.multiply = function (matA, matB, dest) { 3087 | if (!dest) { dest = matA; } 3088 | var a11 = matA[0], 3089 | a12 = matA[1], 3090 | a21 = matA[2], 3091 | a22 = matA[3]; 3092 | dest[0] = a11 * matB[0] + a12 * matB[2]; 3093 | dest[1] = a11 * matB[1] + a12 * matB[3]; 3094 | dest[2] = a21 * matB[0] + a22 * matB[2]; 3095 | dest[3] = a21 * matB[1] + a22 * matB[3]; 3096 | return dest; 3097 | }; 3098 | 3099 | /** 3100 | * Rotates a 2x2 matrix by an angle 3101 | * 3102 | * @param {mat2} mat The matrix to rotate 3103 | * @param {Number} angle The angle in radians 3104 | * @param {mat2} [dest] Optional mat2 receiving the result. If omitted mat will be used. 3105 | * 3106 | * @returns {mat2} dest if specified, mat otherwise 3107 | */ 3108 | mat2.rotate = function (mat, angle, dest) { 3109 | if (!dest) { dest = mat; } 3110 | var a11 = mat[0], 3111 | a12 = mat[1], 3112 | a21 = mat[2], 3113 | a22 = mat[3], 3114 | s = Math.sin(angle), 3115 | c = Math.cos(angle); 3116 | dest[0] = a11 * c + a12 * s; 3117 | dest[1] = a11 * -s + a12 * c; 3118 | dest[2] = a21 * c + a22 * s; 3119 | dest[3] = a21 * -s + a22 * c; 3120 | return dest; 3121 | }; 3122 | 3123 | /** 3124 | * Multiplies the vec2 by the given 2x2 matrix 3125 | * 3126 | * @param {mat2} matrix the 2x2 matrix to multiply against 3127 | * @param {vec2} vec the vector to multiply 3128 | * @param {vec2} [dest] an optional receiving vector. If not given, vec is used. 3129 | * 3130 | * @returns {vec2} The multiplication result 3131 | **/ 3132 | mat2.multiplyVec2 = function(matrix, vec, dest) { 3133 | if (!dest) dest = vec; 3134 | var x = vec[0], y = vec[1]; 3135 | dest[0] = x * matrix[0] + y * matrix[1]; 3136 | dest[1] = x * matrix[2] + y * matrix[3]; 3137 | return dest; 3138 | }; 3139 | 3140 | /** 3141 | * Scales the mat2 by the dimensions in the given vec2 3142 | * 3143 | * @param {mat2} matrix the 2x2 matrix to scale 3144 | * @param {vec2} vec the vector containing the dimensions to scale by 3145 | * @param {vec2} [dest] an optional receiving mat2. If not given, matrix is used. 3146 | * 3147 | * @returns {mat2} dest if specified, matrix otherwise 3148 | **/ 3149 | mat2.scale = function(matrix, vec, dest) { 3150 | if (!dest) { dest = matrix; } 3151 | var a11 = matrix[0], 3152 | a12 = matrix[1], 3153 | a21 = matrix[2], 3154 | a22 = matrix[3], 3155 | b11 = vec[0], 3156 | b22 = vec[1]; 3157 | dest[0] = a11 * b11; 3158 | dest[1] = a12 * b22; 3159 | dest[2] = a21 * b11; 3160 | dest[3] = a22 * b22; 3161 | return dest; 3162 | }; 3163 | 3164 | /** 3165 | * Returns a string representation of a mat2 3166 | * 3167 | * @param {mat2} mat mat2 to represent as a string 3168 | * 3169 | * @param {String} String representation of mat 3170 | */ 3171 | mat2.str = function (mat) { 3172 | return '[' + mat[0] + ', ' + mat[1] + ', ' + mat[2] + ', ' + mat[3] + ']'; 3173 | }; 3174 | 3175 | /** 3176 | * @class 4 Dimensional Vector 3177 | * @name vec4 3178 | */ 3179 | var vec4 = {}; 3180 | 3181 | /** 3182 | * Creates a new vec4, initializing it from vec if vec 3183 | * is given. 3184 | * 3185 | * @param {vec4} [vec] the vector's initial contents 3186 | * @returns {vec4} a new 2D vector 3187 | */ 3188 | vec4.create = function(vec) { 3189 | var dest = new MatrixArray(4); 3190 | 3191 | if (vec) { 3192 | dest[0] = vec[0]; 3193 | dest[1] = vec[1]; 3194 | dest[2] = vec[2]; 3195 | dest[3] = vec[3]; 3196 | } else { 3197 | dest[0] = 0; 3198 | dest[1] = 0; 3199 | dest[2] = 0; 3200 | dest[3] = 0; 3201 | } 3202 | return dest; 3203 | }; 3204 | 3205 | /** 3206 | * Creates a new instance of a vec4, initializing it with the given arguments 3207 | * 3208 | * @param {number} x X value 3209 | * @param {number} y Y value 3210 | * @param {number} z Z value 3211 | * @param {number} w W value 3212 | 3213 | * @returns {vec4} New vec4 3214 | */ 3215 | vec4.createFrom = function (x, y, z, w) { 3216 | var dest = new MatrixArray(4); 3217 | 3218 | dest[0] = x; 3219 | dest[1] = y; 3220 | dest[2] = z; 3221 | dest[3] = w; 3222 | 3223 | return dest; 3224 | }; 3225 | 3226 | /** 3227 | * Adds the vec4's together. If dest is given, the result 3228 | * is stored there. Otherwise, the result is stored in vecB. 3229 | * 3230 | * @param {vec4} vecA the first operand 3231 | * @param {vec4} vecB the second operand 3232 | * @param {vec4} [dest] the optional receiving vector 3233 | * @returns {vec4} dest 3234 | */ 3235 | vec4.add = function(vecA, vecB, dest) { 3236 | if (!dest) dest = vecB; 3237 | dest[0] = vecA[0] + vecB[0]; 3238 | dest[1] = vecA[1] + vecB[1]; 3239 | dest[2] = vecA[2] + vecB[2]; 3240 | dest[3] = vecA[3] + vecB[3]; 3241 | return dest; 3242 | }; 3243 | 3244 | /** 3245 | * Subtracts vecB from vecA. If dest is given, the result 3246 | * is stored there. Otherwise, the result is stored in vecB. 3247 | * 3248 | * @param {vec4} vecA the first operand 3249 | * @param {vec4} vecB the second operand 3250 | * @param {vec4} [dest] the optional receiving vector 3251 | * @returns {vec4} dest 3252 | */ 3253 | vec4.subtract = function(vecA, vecB, dest) { 3254 | if (!dest) dest = vecB; 3255 | dest[0] = vecA[0] - vecB[0]; 3256 | dest[1] = vecA[1] - vecB[1]; 3257 | dest[2] = vecA[2] - vecB[2]; 3258 | dest[3] = vecA[3] - vecB[3]; 3259 | return dest; 3260 | }; 3261 | 3262 | /** 3263 | * Multiplies vecA with vecB. If dest is given, the result 3264 | * is stored there. Otherwise, the result is stored in vecB. 3265 | * 3266 | * @param {vec4} vecA the first operand 3267 | * @param {vec4} vecB the second operand 3268 | * @param {vec4} [dest] the optional receiving vector 3269 | * @returns {vec4} dest 3270 | */ 3271 | vec4.multiply = function(vecA, vecB, dest) { 3272 | if (!dest) dest = vecB; 3273 | dest[0] = vecA[0] * vecB[0]; 3274 | dest[1] = vecA[1] * vecB[1]; 3275 | dest[2] = vecA[2] * vecB[2]; 3276 | dest[3] = vecA[3] * vecB[3]; 3277 | return dest; 3278 | }; 3279 | 3280 | /** 3281 | * Divides vecA by vecB. If dest is given, the result 3282 | * is stored there. Otherwise, the result is stored in vecB. 3283 | * 3284 | * @param {vec4} vecA the first operand 3285 | * @param {vec4} vecB the second operand 3286 | * @param {vec4} [dest] the optional receiving vector 3287 | * @returns {vec4} dest 3288 | */ 3289 | vec4.divide = function(vecA, vecB, dest) { 3290 | if (!dest) dest = vecB; 3291 | dest[0] = vecA[0] / vecB[0]; 3292 | dest[1] = vecA[1] / vecB[1]; 3293 | dest[2] = vecA[2] / vecB[2]; 3294 | dest[3] = vecA[3] / vecB[3]; 3295 | return dest; 3296 | }; 3297 | 3298 | /** 3299 | * Scales vecA by some scalar number. If dest is given, the result 3300 | * is stored there. Otherwise, the result is stored in vecA. 3301 | * 3302 | * This is the same as multiplying each component of vecA 3303 | * by the given scalar. 3304 | * 3305 | * @param {vec4} vecA the vector to be scaled 3306 | * @param {Number} scalar the amount to scale the vector by 3307 | * @param {vec4} [dest] the optional receiving vector 3308 | * @returns {vec4} dest 3309 | */ 3310 | vec4.scale = function(vecA, scalar, dest) { 3311 | if (!dest) dest = vecA; 3312 | dest[0] = vecA[0] * scalar; 3313 | dest[1] = vecA[1] * scalar; 3314 | dest[2] = vecA[2] * scalar; 3315 | dest[3] = vecA[3] * scalar; 3316 | return dest; 3317 | }; 3318 | 3319 | /** 3320 | * Copies the values of one vec4 to another 3321 | * 3322 | * @param {vec4} vec vec4 containing values to copy 3323 | * @param {vec4} dest vec4 receiving copied values 3324 | * 3325 | * @returns {vec4} dest 3326 | */ 3327 | vec4.set = function (vec, dest) { 3328 | dest[0] = vec[0]; 3329 | dest[1] = vec[1]; 3330 | dest[2] = vec[2]; 3331 | dest[3] = vec[3]; 3332 | return dest; 3333 | }; 3334 | 3335 | /** 3336 | * Compares two vectors for equality within a certain margin of error 3337 | * 3338 | * @param {vec4} a First vector 3339 | * @param {vec4} b Second vector 3340 | * 3341 | * @returns {Boolean} True if a is equivalent to b 3342 | */ 3343 | vec4.equal = function (a, b) { 3344 | return a === b || ( 3345 | Math.abs(a[0] - b[0]) < FLOAT_EPSILON && 3346 | Math.abs(a[1] - b[1]) < FLOAT_EPSILON && 3347 | Math.abs(a[2] - b[2]) < FLOAT_EPSILON && 3348 | Math.abs(a[3] - b[3]) < FLOAT_EPSILON 3349 | ); 3350 | }; 3351 | 3352 | /** 3353 | * Negates the components of a vec4 3354 | * 3355 | * @param {vec4} vec vec4 to negate 3356 | * @param {vec4} [dest] vec4 receiving operation result. If not specified result is written to vec 3357 | * 3358 | * @returns {vec4} dest if specified, vec otherwise 3359 | */ 3360 | vec4.negate = function (vec, dest) { 3361 | if (!dest) { dest = vec; } 3362 | dest[0] = -vec[0]; 3363 | dest[1] = -vec[1]; 3364 | dest[2] = -vec[2]; 3365 | dest[3] = -vec[3]; 3366 | return dest; 3367 | }; 3368 | 3369 | /** 3370 | * Caclulates the length of a vec2 3371 | * 3372 | * @param {vec2} vec vec2 to calculate length of 3373 | * 3374 | * @returns {Number} Length of vec 3375 | */ 3376 | vec4.length = function (vec) { 3377 | var x = vec[0], y = vec[1], z = vec[2], w = vec[3]; 3378 | return Math.sqrt(x * x + y * y + z * z + w * w); 3379 | }; 3380 | 3381 | /** 3382 | * Caclulates the squared length of a vec4 3383 | * 3384 | * @param {vec4} vec vec4 to calculate squared length of 3385 | * 3386 | * @returns {Number} Squared Length of vec 3387 | */ 3388 | vec4.squaredLength = function (vec) { 3389 | var x = vec[0], y = vec[1], z = vec[2], w = vec[3]; 3390 | return x * x + y * y + z * z + w * w; 3391 | }; 3392 | 3393 | /** 3394 | * Performs a linear interpolation between two vec4 3395 | * 3396 | * @param {vec4} vecA First vector 3397 | * @param {vec4} vecB Second vector 3398 | * @param {Number} lerp Interpolation amount between the two inputs 3399 | * @param {vec4} [dest] vec4 receiving operation result. If not specified result is written to vecA 3400 | * 3401 | * @returns {vec4} dest if specified, vecA otherwise 3402 | */ 3403 | vec4.lerp = function (vecA, vecB, lerp, dest) { 3404 | if (!dest) { dest = vecA; } 3405 | dest[0] = vecA[0] + lerp * (vecB[0] - vecA[0]); 3406 | dest[1] = vecA[1] + lerp * (vecB[1] - vecA[1]); 3407 | dest[2] = vecA[2] + lerp * (vecB[2] - vecA[2]); 3408 | dest[3] = vecA[3] + lerp * (vecB[3] - vecA[3]); 3409 | return dest; 3410 | }; 3411 | 3412 | /** 3413 | * Returns a string representation of a vector 3414 | * 3415 | * @param {vec4} vec Vector to represent as a string 3416 | * 3417 | * @returns {String} String representation of vec 3418 | */ 3419 | vec4.str = function (vec) { 3420 | return '[' + vec[0] + ', ' + vec[1] + ', ' + vec[2] + ', ' + vec[3] + ']'; 3421 | }; 3422 | 3423 | /* 3424 | * Exports 3425 | */ 3426 | 3427 | if(root) { 3428 | root.glMatrixArrayType = MatrixArray; 3429 | root.MatrixArray = MatrixArray; 3430 | root.setMatrixArrayType = setMatrixArrayType; 3431 | root.determineMatrixArrayType = determineMatrixArrayType; 3432 | root.glMath = glMath; 3433 | root.vec2 = vec2; 3434 | root.vec3 = vec3; 3435 | root.vec4 = vec4; 3436 | root.mat2 = mat2; 3437 | root.mat3 = mat3; 3438 | root.mat4 = mat4; 3439 | root.quat4 = quat4; 3440 | } 3441 | 3442 | return { 3443 | glMatrixArrayType: MatrixArray, 3444 | MatrixArray: MatrixArray, 3445 | setMatrixArrayType: setMatrixArrayType, 3446 | determineMatrixArrayType: determineMatrixArrayType, 3447 | glMath: glMath, 3448 | vec2: vec2, 3449 | vec3: vec3, 3450 | vec4: vec4, 3451 | mat2: mat2, 3452 | mat3: mat3, 3453 | mat4: mat4, 3454 | quat4: quat4 3455 | }; 3456 | })); 3457 | -------------------------------------------------------------------------------- /scripts/launcher.js: -------------------------------------------------------------------------------- 1 | addEventListener('load', function () { 2 | var canvas = document.getElementById('gl'); 3 | 4 | var viewer = new ModelViewer({ 5 | file: 'assets/Item/Objectcomponents/weapon/axe_1h_blacksmithing_d_01.m2', 6 | glCanvas: canvas, 7 | playBtn: document.getElementById('play') 8 | }); 9 | 10 | document.addEventListener('keydown', function (event) { 11 | switch (event.keyCode) { 12 | case 40: viewer.angleSpeed.X = 0.03; break; 13 | case 38: viewer.angleSpeed.X = -0.03; break; 14 | case 39: viewer.angleSpeed.Y = 0.03; break; 15 | case 37: viewer.angleSpeed.Y = -0.03; break; 16 | } 17 | }); 18 | 19 | document.addEventListener('keyup', function (event) { 20 | switch (event.keyCode) { 21 | case 40: 22 | case 38: 23 | viewer.angleSpeed.X = 0; 24 | break; 25 | 26 | case 39: 27 | case 37: 28 | viewer.angleSpeed.Y = 0; 29 | break; 30 | } 31 | }); 32 | 33 | var lastCoords = null, 34 | sizeMappings = {X: 'width', Y: 'height'}; 35 | 36 | function getCoords(data) { 37 | return {X: data.clientY, Y: data.clientX}; 38 | } 39 | 40 | function onDown(data) { 41 | lastCoords = getCoords(data); 42 | } 43 | 44 | function onUp() { 45 | lastCoords = null; 46 | } 47 | 48 | function onMove(data) { 49 | if (!lastCoords) return; 50 | 51 | var newCoords = getCoords(data); 52 | 53 | for (var name in newCoords) { 54 | viewer.angle[name] += (newCoords[name] - lastCoords[name]) / viewer[sizeMappings[name]] * 4; 55 | } 56 | 57 | lastCoords = newCoords; 58 | } 59 | 60 | canvas.addEventListener('mousedown', function (event) { 61 | onDown(event); 62 | event.preventDefault(); 63 | }); 64 | canvas.addEventListener('mouseup', onUp); 65 | canvas.addEventListener('mousemove', onMove); 66 | 67 | canvas.addEventListener('touchstart', function (event) { onDown(event.touches[0]) }); 68 | canvas.addEventListener('touchend', onUp); 69 | canvas.addEventListener('touchmove', function (event) { onMove(event.touches[0]) }); 70 | }); -------------------------------------------------------------------------------- /scripts/m2.js: -------------------------------------------------------------------------------- 1 | var M2 = function (filename, callback, description_entry) { 2 | this.filename = filename; 3 | this.callback = callback; 4 | this.description_entry = description_entry || 'm2'; 5 | this.load(filename, callback); 6 | this.requestCount = 0; 7 | }; 8 | 9 | M2.prototype = { 10 | typeSet: { 11 | 'jBinary.littleEndian': true, 12 | 'jBinary.all': 'm2', 13 | 14 | /* Common definitions */ 15 | 16 | float2: ['array', 'float32', 2], 17 | float3: ['array', 'float32', 3], 18 | float4: ['array', 'float32', 4], 19 | 20 | nofs: { 21 | count: 'uint32', 22 | offset: 'uint32' 23 | }, 24 | 25 | struct: jBinary.Template({ 26 | baseType: 'nofs', 27 | params: ['innerType'], 28 | read: function () { 29 | var nofs = this.baseRead(), 30 | type = 31 | this.innerType === 'char' 32 | ? ['string0', nofs.count] 33 | : ['array', this.innerType, nofs.count]; 34 | 35 | return this.binary.read(type, nofs.offset); 36 | } 37 | }), 38 | 39 | /* .skin File */ 40 | 41 | skin: { 42 | skinID: 'uint32', 43 | indices: ['struct', 'uint16'], 44 | triangles: ['struct', 'uint16'], 45 | properties: ['struct', 'boneIndices'], 46 | submeshes: ['struct', 'submesh'], 47 | textureUnits: ['struct', 'textureUnit'], 48 | bones: 'uint32' 49 | }, 50 | 51 | boneIndices: ['array', 'uint8', 4], 52 | 53 | submesh: { 54 | meshID: 'uint32', 55 | vertices: 'startn', 56 | triangles: 'startn', 57 | bones: 'startn', 58 | unk: 'uint16', 59 | rootBone: 'uint16', 60 | boundingBox: ['array', 'float3', 2], 61 | radius: 'float' 62 | }, 63 | 64 | startn: { 65 | start: 'uint16', 66 | n: 'uint16' 67 | }, 68 | 69 | textureUnit: { 70 | flags: 'uint16', 71 | shading: 'uint16', 72 | submeshIndex: 'uint16', 73 | submeshIndex2: 'uint16', 74 | colorIndex: 'int16', 75 | renderFlags: 'uint16', 76 | texUnitNumber: 'uint16', 77 | mode: 'uint16', 78 | texture: 'uint16', 79 | texUnitNumber2: 'uint16', 80 | transparency: 'uint16', 81 | textureAnim: 'uint16' 82 | }, 83 | 84 | /* M2 File */ 85 | 86 | m2: { 87 | tag: ['string', 4], 88 | version: 'uint32', 89 | name: ['struct', 'char'], 90 | globalModelFlag: 'uint32', 91 | 92 | globalSequences: ['struct', 'uint16'], 93 | animations: ['struct', 'animation'], 94 | animationLookup: ['struct', 'uint16'], 95 | bones: ['struct', 'bone'], 96 | keyBoneLookup: ['struct', 'uint16'], 97 | vertices: ['struct', 'vertex'], 98 | views: 'views', 99 | colors: ['struct', 'color'], 100 | textures: ['struct', 'texture'], 101 | transparencyLookup: ['struct', 'uint16'], 102 | uvAnimation: ['struct', 'uvAnimation'], 103 | texReplace: ['struct', 'int16'], 104 | renderFlags: ['struct', 'renderFlag'], 105 | boneLookup: ['struct', 'uint16'], 106 | texLookup: ['struct', 'uint16'], 107 | texUnits: ['struct', 'int16'], 108 | transLookup: ['struct', 'uint16'], 109 | uvAnimLookup: ['struct', 'uint16'], 110 | vertexBox: ['array', 'float3', 2], 111 | vertexRadius: 'float', 112 | boundingBox: ['array', 'float3', 2], 113 | boundingRadius: 'float', 114 | boundingTriangles: ['struct', 'boundingTriangle'], 115 | boundingVertices: ['struct', 'boundingVertex'], 116 | boundingNormals: ['struct', 'boundingNormal'], 117 | attachments: ['struct', 'attachment'], 118 | attachLookup: ['struct', 'uint16'], 119 | events: ['struct', 'event'], 120 | lights: ['struct', 'light'], 121 | cameras: ['struct', 'camera'], 122 | cameraLookup: ['struct', 'uint16'], 123 | ribbonEmitters: ['struct', 'ribbonEmitter'], 124 | particleEmitters: ['struct', 'particleEmitter'] 125 | }, 126 | 127 | views: jBinary.Type({ 128 | read: function () { 129 | var m2 = this.binary.getContext(1); 130 | var num = this.binary.read('uint32'); 131 | var views = []; 132 | for (var i = 0; i < num; ++i) { 133 | m2.pushRequest(); 134 | (function (i, filename) { 135 | new M2(filename, 136 | function (skin) { 137 | views[i] = skin; 138 | m2.popRequest(); 139 | }, 140 | 'skin' 141 | ); 142 | })(i, m2.filename.replace(/\.m2$/, '0' + i + '.skin')); 143 | } 144 | return views; 145 | } 146 | }), 147 | 148 | animation: { 149 | animationID: 'uint16', 150 | subAnimationID: 'uint16', 151 | length: 'uint32', 152 | movingSpeed: 'float', 153 | flags: 'uint32', 154 | probability: 'int16', 155 | unk1: 'uint16', 156 | unk2: 'uint32', 157 | unk3: 'uint32', 158 | playbackSpeed: 'uint32', 159 | minimumExtent: 'float3', 160 | maximumExtent: 'float3', 161 | boundRadius: 'float', 162 | nextAnimation: 'int16', 163 | index: 'uint16' 164 | }, 165 | 166 | animationBlock: { 167 | interpolationType: 'uint16', 168 | globalSequenceID: 'int16', 169 | timestamps: 'nofs', 170 | keyFrame: 'nofs' 171 | }, 172 | 173 | fakeAnimationBlock: { 174 | timestamps: 'nofs', 175 | keyFrame: 'nofs' 176 | }, 177 | 178 | bone: { 179 | keyBoneID: 'int32', 180 | flags: 'uint32', 181 | parentBone: 'int16', 182 | unk: ['array', 'uint16', 3], 183 | translation: 'animationBlock', 184 | rotation: 'animationBlock', 185 | scaling: 'animationBlock', 186 | pivot: 'float3' 187 | }, 188 | 189 | vertex: { 190 | position: 'float3', 191 | boneWeight: ['array', 'uint8', 4], 192 | boneIndices: ['array', 'uint8', 4], 193 | normal: 'float3', 194 | textureCoords: 'float2', 195 | unk: 'float2' 196 | }, 197 | 198 | color: { 199 | color: 'animationBlock', 200 | alpha: 'animationBlock' 201 | }, 202 | 203 | texture: { 204 | type: 'uint32', 205 | flags: 'uint32', 206 | filename: ['struct', 'char'] 207 | }, 208 | 209 | uvAnimation: { 210 | translation: 'animationBlock', 211 | rotation: 'animationBlock', 212 | scaling: 'animationBlock' 213 | }, 214 | 215 | renderFlag: { 216 | flags: 'uint16', 217 | blendingMode: 'uint16' 218 | }, 219 | 220 | ribbonEmitter: { 221 | unk1: 'uint32', 222 | boneID: 'uint32', 223 | position: 'float3', 224 | textures: 'nofs', 225 | blendRef: 'nofs', 226 | color: 'animationBlock', 227 | opacity: 'animationBlock', 228 | above: 'animationBlock', 229 | below: 'animationBlock', 230 | resolution: 'float', 231 | length: 'float', 232 | emissionAngle: 'float', 233 | renderFlags: 'uint32', 234 | unk2: 'animationBlock', 235 | unk3: 'animationBlock', 236 | unk4: 'uint32' 237 | }, 238 | 239 | boundingTriangle: ['array', 'uint16', 3], 240 | boundingNormal: 'float3', 241 | boundingVertex: 'float3', 242 | 243 | attachment: { 244 | attachmentID: 'uint32', 245 | boneID: 'uint32', 246 | position: 'float3', 247 | data: 'animationBlock' 248 | }, 249 | 250 | event: { 251 | name: ['string', 4], 252 | data: 'uint32', 253 | boneID: 'uint32', 254 | position: 'float3', 255 | interpolationType: 'uint16', 256 | globalSequence: 'uint16', 257 | timestamp: 'nofs' 258 | }, 259 | 260 | light: { 261 | type: 'uint16', 262 | boneID: 'int16', 263 | position: 'float3', 264 | ambiantColor: 'animationBlock', 265 | ambientIntensity: 'animationBlock', 266 | diffuseColor: 'animationBlock', 267 | diffuseIntensity: 'animationBlock', 268 | attenuationStart: 'animationBlock', 269 | attenuationEnd: 'animationBlock', 270 | unk: 'animationBlock' 271 | }, 272 | 273 | camera: { 274 | type: 'uint32', 275 | fov: 'float', 276 | farClipping: 'float', 277 | nearClipping: 'float', 278 | translationPos: 'animationBlock', 279 | position: 'float3', 280 | translationTar: 'animationBlock', 281 | target: 'float3', 282 | scaling: 'animationBlock' 283 | }, 284 | 285 | particleEmitter: { 286 | unk1: 'uint32', 287 | flags1: 'uint16', 288 | flags2: 'uint16', 289 | position: 'float3', 290 | boneID: 'uint16', 291 | textureID: 'uint16', 292 | modelFilename: ['struct', 'char'], 293 | particleFilename: ['struct', 'char'], 294 | blendingType: 'uint8', 295 | emitterType: 'uint8', 296 | particleColor: 'uint16', 297 | particleType: 'uint8', 298 | headOrTail: 'uint8', 299 | textureTileRotation: 'uint16', 300 | textureRows: 'uint16', 301 | textureCols: 'uint16', 302 | emissionSpeed: 'animationBlock', 303 | speedVariation: 'animationBlock', 304 | verticalRange: 'animationBlock', 305 | horizontalRange: 'animationBlock', 306 | gravity: 'animationBlock', 307 | lifespan: 'animationBlock', 308 | unk2: 'uint32', 309 | emissionRate: 'animationBlock', 310 | unk3: 'uint32', 311 | emissionAreaLength: 'animationBlock', 312 | emissionAreaWidth: 'animationBlock', 313 | gravity2: 'animationBlock', 314 | particleColor: 'fakeAnimationBlock', 315 | particleOpacity: 'fakeAnimationBlock', 316 | particleSize: 'fakeAnimationBlock', 317 | unk4: 'uint32', 318 | intensity: 'fakeAnimationBlock', 319 | unk5: 'fakeAnimationBlock', 320 | unk6: 'float3', 321 | scale: 'float3', 322 | slowdown: 'float', 323 | unk7: 'float2', 324 | rotation: 'float', 325 | unk8: 'float3', 326 | rotation1: 'float3', 327 | rotation2: 'float3', 328 | translation: 'float3', 329 | unk9: 'float4', 330 | unk10: 'nofs', 331 | enabledIn: 'animationBlock' 332 | } 333 | }, 334 | 335 | load: function (filename) { 336 | var that = this; 337 | 338 | jBinary.load(filename, this.typeSet, function (err, binary) { 339 | if (err) throw err; 340 | that.pushRequest(); 341 | that.model = binary.inContext(that, function () { return this.read(that.description_entry) }); 342 | that.popRequest(); 343 | }); 344 | }, 345 | 346 | pushRequest: function () { 347 | this.requestCount += 1; 348 | }, 349 | 350 | popRequest: function () { 351 | this.requestCount -= 1; 352 | if (this.requestCount === 0) { 353 | this.callback(this.model); 354 | } 355 | }, 356 | }; 357 | -------------------------------------------------------------------------------- /scripts/modelviewer.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var lastTime = 0, 3 | vendors = ['ms', 'moz', 'webkit', 'o'], 4 | x, length, currTime, timeToCall; 5 | 6 | for(x = 0, length = vendors.length; x < length && !window.requestAnimationFrame; ++x) { 7 | window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame']; 8 | window.cancelAnimationFrame = 9 | window[vendors[x]+'CancelAnimationFrame'] || window[vendors[x]+'CancelRequestAnimationFrame']; 10 | } 11 | 12 | if (!window.requestAnimationFrame) { 13 | window.requestAnimationFrame = function(callback, element) { 14 | currTime = new Date().getTime(); 15 | timeToCall = Math.max(0, 16 - (currTime - lastTime)); 16 | lastTime = currTime + timeToCall; 17 | return window.setTimeout(function() { callback(currTime + timeToCall); }, 18 | timeToCall); 19 | }; 20 | } 21 | 22 | if (!window.cancelAnimationFrame) { 23 | window.cancelAnimationFrame = function(id) { 24 | clearTimeout(id); 25 | }; 26 | } 27 | })(); 28 | 29 | var ModelViewer = function (opt) { 30 | this.opt = opt; 31 | this.width = window.innerWidth; 32 | this.height = window.innerHeight; 33 | 34 | this.pMatrix = mat4.create(); 35 | this.mvMatrix = mat4.create(); 36 | 37 | this.eye = vec3.create([0.0, -1.5, -0.1]); 38 | this.center = vec3.create([0.2, 3.2, 0.2]); 39 | this.up = vec3.create([0, 0, 1]); 40 | 41 | this.stopped = false; 42 | 43 | this.angle = { 44 | X: 0, 45 | Y: 0, 46 | Z: 0 47 | }; 48 | 49 | this.angleSpeed = { 50 | X: 0, 51 | Y: 0, 52 | Z: 0.01 53 | }; 54 | 55 | var that = this; 56 | opt.playBtn.addEventListener('click', function () { 57 | that.stopped && that.start() || that.stop(); 58 | }); 59 | new M2(opt.file, function (model) { that.parse(model); }); 60 | }; 61 | 62 | ModelViewer.prototype = { 63 | drawScene: function () { 64 | this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT); 65 | 66 | mat4.perspective(45, this.width / this.height, 0.1, 100.0, this.pMatrix); 67 | mat4.identity(this.mvMatrix); 68 | 69 | mat4.lookAt(this.eye, this.center, this.up, this.mvMatrix); 70 | 71 | for (var name in this.angle) { 72 | mat4['rotate' + name](this.mvMatrix, this.angle[name]); 73 | this.angle[name] += this.angleSpeed[name]; 74 | } 75 | 76 | // Vertex 77 | this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.glModel); 78 | this.gl.vertexAttribPointer(this.shaderProgram.vertexPositionAttribute, this.glModel.itemSize, this.gl.FLOAT, false, 0, 0); 79 | 80 | // Texture Coords 81 | this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.glTextureCoords); 82 | this.gl.vertexAttribPointer(this.shaderProgram.textureCoordAttribute, this.glTextureCoords.itemSize, this.gl.FLOAT, false, 0, 0); 83 | 84 | // Texture 85 | this.gl.activeTexture(this.gl.TEXTURE0); 86 | this.gl.bindTexture(this.gl.TEXTURE_2D, this.texture); 87 | this.gl.uniform1i(this.shaderProgram.samplerUniform, 0); 88 | 89 | // Vertex Index 90 | this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, this.glVertexIndexBuffer); 91 | 92 | // Draw 93 | this.setMatrixUniforms(); 94 | this.gl.drawElements(this.gl.TRIANGLES, this.glVertexIndexBuffer.numItems, this.gl.UNSIGNED_SHORT, 0); 95 | 96 | var that = this; 97 | window.requestAnimationFrame(function () { that.drawScene() }); 98 | }, 99 | 100 | initGL: function (canvas) { 101 | try { 102 | this.gl = canvas.getContext("experimental-webgl"); 103 | } catch(e) {} 104 | if (!this.gl) { 105 | alert("Could not initialise WebGL, sorry :-("); 106 | } 107 | }, 108 | 109 | getShader: function (gl, id) { 110 | var shaderScript = document.getElementById(id); 111 | if (!shaderScript) { 112 | return null; 113 | } 114 | 115 | var str = ""; 116 | var k = shaderScript.firstChild; 117 | while (k) { 118 | if (k.nodeType == 3) { 119 | str += k.textContent; 120 | } 121 | k = k.nextSibling; 122 | } 123 | 124 | var shader; 125 | if (shaderScript.type == "x-shader/x-fragment") { 126 | shader = this.gl.createShader(this.gl.FRAGMENT_SHADER); 127 | } else if (shaderScript.type == "x-shader/x-vertex") { 128 | shader = this.gl.createShader(this.gl.VERTEX_SHADER); 129 | } else { 130 | return null; 131 | } 132 | 133 | this.gl.shaderSource(shader, str); 134 | this.gl.compileShader(shader); 135 | 136 | if (!this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS)) { 137 | alert(this.gl.getShaderInfoLog(shader)); 138 | return null; 139 | } 140 | 141 | return shader; 142 | }, 143 | 144 | initShaders: function () { 145 | var fragmentShader = this.getShader(gl, "shader-fs"); 146 | var vertexShader = this.getShader(gl, "shader-vs"); 147 | 148 | this.shaderProgram = this.gl.createProgram(); 149 | this.gl.attachShader(this.shaderProgram, vertexShader); 150 | this.gl.attachShader(this.shaderProgram, fragmentShader); 151 | this.gl.linkProgram(this.shaderProgram); 152 | 153 | if (!this.gl.getProgramParameter(this.shaderProgram, this.gl.LINK_STATUS)) { 154 | alert("Could not initialise shaders"); 155 | } 156 | 157 | this.gl.useProgram(this.shaderProgram); 158 | 159 | this.shaderProgram.vertexPositionAttribute = this.gl.getAttribLocation(this.shaderProgram, "aVertexPosition"); 160 | this.gl.enableVertexAttribArray(this.shaderProgram.vertexPositionAttribute); 161 | 162 | this.shaderProgram.textureCoordAttribute = this.gl.getAttribLocation(this.shaderProgram, "aTextureCoord"); 163 | this.gl.enableVertexAttribArray(this.shaderProgram.textureCoordAttribute); 164 | 165 | this.shaderProgram.pMatrixUniform = this.gl.getUniformLocation(this.shaderProgram, "uPMatrix"); 166 | this.shaderProgram.mvMatrixUniform = this.gl.getUniformLocation(this.shaderProgram, "uMVMatrix"); 167 | this.shaderProgram.samplerUniform = this.gl.getUniformLocation(this.shaderProgram, "uSampler"); 168 | }, 169 | 170 | setMatrixUniforms: function () { 171 | this.gl.uniformMatrix4fv(this.shaderProgram.pMatrixUniform, false, new Float32Array(this.pMatrix)); 172 | this.gl.uniformMatrix4fv(this.shaderProgram.mvMatrixUniform, false, new Float32Array(this.mvMatrix)); 173 | }, 174 | 175 | glStart: function () { 176 | var canvas = this.opt.glCanvas; 177 | canvas.width = this.width; 178 | canvas.height = this.height; 179 | 180 | this.initGL(canvas); 181 | this.initShaders(); 182 | this.createGLModel(); 183 | 184 | this.gl.clearColor(0.0, 0.0, 0.0, 1.0); 185 | this.gl.clearDepth(1.0); 186 | this.gl.enable(this.gl.DEPTH_TEST); 187 | this.gl.depthFunc(this.gl.LEQUAL); 188 | 189 | this.drawScene(); 190 | }, 191 | 192 | start: function () { 193 | this.stopped = false; 194 | this.angleSpeed.Z = 0.01; 195 | this.opt.playBtn.setAttribute('class', 'icon-pause'); 196 | return true; 197 | }, 198 | 199 | stop: function () { 200 | this.stopped = true; 201 | this.angleSpeed.Z = 0; 202 | this.opt.playBtn.setAttribute('class', 'icon-play'); 203 | return true; 204 | }, 205 | 206 | createGLModel: function () { 207 | /* Vertex */ 208 | var vertices = []; 209 | for (var i = 0; i < this.model.vertices.length; ++i) { 210 | for (var j = 0; j < 3; ++j) { 211 | vertices[i * 3 + j] = this.model.vertices[i].position[j]; 212 | } 213 | } 214 | 215 | this.glModel = this.gl.createBuffer(); 216 | this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.glModel); 217 | this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array(vertices), this.gl.STATIC_DRAW); 218 | this.glModel.itemSize = 3; 219 | this.glModel.numItems = this.model.vertices.length; 220 | 221 | /* Vertex Index */ 222 | var indices = this.model.views[0].triangles; 223 | this.glVertexIndexBuffer = this.gl.createBuffer(); 224 | this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, this.glVertexIndexBuffer); 225 | this.gl.bufferData(this.gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), this.gl.STATIC_DRAW); 226 | this.glVertexIndexBuffer.itemSize = 1; 227 | this.glVertexIndexBuffer.numItems = indices.length; 228 | 229 | /* Texture Coords */ 230 | var coords = []; 231 | for (var i = 0; i < this.model.vertices.length; ++i) { 232 | for (var j = 0; j < 2; ++j) { 233 | coords[i * 2 + j] = this.model.vertices[i].textureCoords[j]; 234 | } 235 | } 236 | 237 | this.glTextureCoords = this.gl.createBuffer(); 238 | this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.glTextureCoords); 239 | this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array(coords), this.gl.STATIC_DRAW); 240 | this.glTextureCoords.itemSize = 2; 241 | this.glTextureCoords.numItems = this.model.vertices.length; 242 | 243 | /* Textures */ 244 | this.createTexture('assets/Item/Objectcomponents/weapon/axe_1h_blacksmithing_d_01.png'); 245 | }, 246 | 247 | createTexture: function (path) { 248 | var that = this; 249 | 250 | this.texture = this.gl.createTexture(); 251 | this.texture.image = new Image(); 252 | this.texture.image.onload = function () { 253 | that.handleLoadedTexture(that.texture); 254 | that.start.call(that); 255 | } 256 | this.texture.image.src = path; 257 | }, 258 | 259 | handleLoadedTexture: function (texture) { 260 | this.gl.bindTexture(this.gl.TEXTURE_2D, texture); 261 | this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RGBA, this.gl.RGBA, this.gl.UNSIGNED_BYTE, texture.image); 262 | this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MAG_FILTER, this.gl.LINEAR); 263 | this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR); 264 | this.gl.bindTexture(this.gl.TEXTURE_2D, null); 265 | }, 266 | 267 | parse: function (data) { 268 | this.model = data; 269 | if (typeof console !== 'undefined') { 270 | console.log('Parsed Model', data); 271 | } 272 | this.glStart(); 273 | } 274 | }; --------------------------------------------------------------------------------