1 /** 2 @preserve 3 4 BenchGL - A WebGL-based javascript graphic library. 5 Copyright (c) 2010-2011 Matteo Meli. 6 7 */ 8 9 (function() { 10 11 // benchgl.js 12 // Contains the global variable representing the framework and some utilities. 13 14 // Unique global variable repesenting the framework 15 this.BenchGL = this.BenchGL || {}; 16 17 // Special function to create namespaces 18 BenchGL.namespace = function(name) { 19 var parts = name.split('.'), 20 parent = BenchGL; 21 22 if (parts[0] === 'BenchGL') { 23 parts = parts.slice(1); 24 } 25 26 for (var i = 0; i < parts.length; i++) { 27 if (typeof parent[parts[i]] === 'undefined') { 28 parent[parts[i]] = {}; 29 } 30 parent = parent[parts[i]]; 31 } 32 33 return parent; 34 }; 35 36 // Special function to globalize BenchGL library 37 BenchGL.globalize = function() { 38 for (var module in BenchGL) { 39 for (var object in BenchGL[module]) { 40 window[object] = BenchGL[module][object]; 41 } 42 } 43 }; 44 45 // utils.js 46 // General utility functions. 47 48 // Gets a DOM element from its ID. 49 function $(id) { 50 return document.getElementById(id); 51 } 52 53 // This function provides a way to implement "class" inheritance. 54 $.inherit = (function() { 55 var F = function() {}; 56 return function(C, P) { 57 F.prototype = P.prototype; 58 C.prototype = new F(); 59 C.uber = P.prototype; 60 C.prototype.constructor = C; 61 } 62 }()); 63 64 // Merges two or more objects into one. 65 $.mix = function() { 66 var i, object, key, mix = {}; 67 for (i = 0, l = arguments.length; i < l; i++) { 68 object = arguments[i]; 69 for (key in object) { 70 if (object.hasOwnProperty(key)) { 71 mix[key] = object[key]; 72 } 73 } 74 } 75 return mix; 76 }; 77 78 // Capitalizes a string. 79 $.capitalize = function(text) { 80 if (text && text.length > 0) { 81 text = text.charAt(0).toUpperCase() + text.slice(1); 82 } 83 return text; 84 }; 85 86 // Represents an empty function. 87 $.empty = function() {}; 88 89 /** 90 * Provides requestAnimationFrame in a cross browser way. 91 * Copyright 2010, Google Inc. 92 * All rights reserved. 93 * @ignore 94 */ 95 window.requestAnimFrame = (function() { 96 return window.requestAnimationFrame || 97 window.webkitRequestAnimationFrame || 98 window.mozRequestAnimationFrame || 99 window.oRequestAnimationFrame || 100 window.msRequestAnimationFrame || 101 function(callback, element) { 102 window.setTimeout(callback, 1000 / 60); 103 }; 104 }()); 105 106 // math.js 107 // Provides a math library to handle 3D vectors, matrices and quaternions. 108 109 BenchGL.namespace('BenchGL.math.Vector3'); 110 111 BenchGL.math.Vector3 = (function() { 112 113 // Dependencies 114 var acos = Math.acos, 115 sqrt = Math.sqrt, 116 117 // Private properties and methods 118 Vector3; 119 120 /** 121 * Creates a new Vector3. 122 * @class Represents a vector in homogeneous 3D space. 123 * @param {Number} [x=0] The x coordinate. 124 * @param {Number} [y=0] The y coordinate. 125 * @param {Number} [z=0] The z coordinate. 126 */ 127 Vector3 = function(x, y, z) { 128 this.x = x || 0; 129 this.y = y || 0; 130 this.z = z || 0; 131 }; 132 133 /** 134 * Copies this Vector3 in a new one. 135 * @returns {Vector3} A copy of this Vector3. 136 */ 137 Vector3.prototype.copy = function() { 138 return new Vector3(this.x, this.y, this.z); 139 }; 140 141 /** 142 * Sets this Vector3 coordinates. 143 * @param {Number} x The x coordinate to set. 144 * @param {Number} y The y coordinate to set. 145 * @param {Number} z The z coordinate to set. 146 */ 147 Vector3.prototype.set = function(x, y, z) { 148 this.x = x; 149 this.y = y; 150 this.z = z; 151 }; 152 153 /** 154 * Gets the norm of this Vector3. 155 * @returns {Number} The norm of this Vector3. 156 */ 157 Vector3.prototype.norm = function() { 158 var x = this.x, y = this.y, z = this.z; 159 return sqrt(x * x + y * y + z * z); 160 }; 161 162 /** 163 * Gets the squared norm of this Vector3. 164 * @returns {Number} The squared norm of this Vector3. 165 */ 166 Vector3.prototype.normSqr = function() { 167 var x = this.x, y = this.y, z = this.z; 168 return (x * x + y * y + z * z); 169 }; 170 171 /** 172 * Negates this Vector3. Does not affect this Vector3. 173 * @returns {Vector3} A new vector representing the negation of this Vector3. 174 */ 175 Vector3.prototype.negate = function() { 176 return new Vector3(-this.x, -this.y, -this.z); 177 }; 178 179 /** 180 * Negates this Vector3. Affects this Vector3. 181 * @returns {Vector3} The negated version of this Vector3. 182 */ 183 Vector3.prototype.$negate = function() { 184 this.x = -this.x; 185 this.y = -this.y; 186 this.z = -this.z; 187 return this; 188 }; 189 190 /** 191 * Normalizes this Vector3. Does not affect this Vector3. 192 * @returns {Vector3} A new vector representing the normalized version of this Vector3. 193 */ 194 Vector3.prototype.unit = function() { 195 var x = this.x, y = this.y, z = this.z, d = sqrt(x * x + y * y + z * z); 196 if (d > 0) { 197 return new Vector3(x / d, y / d, z / d); 198 } 199 return this.copy(); 200 }; 201 202 /** 203 * Normalizes this Vector3. Affects this Vector3. 204 * @returns {Vector3} The normalized version of this Vector3. 205 */ 206 Vector3.prototype.$unit = function(){ 207 var x = this.x, y = this.y, z = this.z, d = sqrt(x * x + y * y + z * z); 208 if (d > 0) { 209 this.$scale(1 / d); 210 } 211 return this; 212 }; 213 214 /** 215 * Adds another Vector3 to this Vector3. Does not affect this Vector3. 216 * @param {Vector3} vector The Vector3 to add. 217 * @returns {Vector3} A new vector representing the addition. 218 */ 219 Vector3.prototype.add = function(vector) { 220 var x = this.x + vector.x, 221 y = this.y + vector.y, 222 z = this.z + vector.z; 223 return new Vector3(x, y, z); 224 }; 225 226 /** 227 * Adds another Vector3 to this Vector3. Affects this Vector3. 228 * @param {Vector3} vector The Vector3 to add. 229 * @returns {Vector3} This Vector3 now stores the sum. 230 */ 231 Vector3.prototype.$add = function(vector) { 232 this.x += vector.x; 233 this.y += vector.y; 234 this.z += vector.z; 235 return this; 236 }; 237 238 /** 239 * Subtracts another Vector3 to this Vector3. Does not affect this Vector3. 240 * @param {Vector3} vector The Vector3 to add. 241 * @returns {Vector3} A new vector representing the subtraction. 242 */ 243 Vector3.prototype.sub = function(vector) { 244 var x = this.x - vector.x, y = this.y - vector.y, z = this.z - vector.z; 245 return new Vector3(x, y, z); 246 }; 247 248 /** 249 * Subtracts another Vector3 to this Vector3. Affects this Vector3. 250 * @param {Vector3} vector The Vector3 to add. 251 * @returns {Vector3} This Vector3 now stores the subtraction. 252 */ 253 Vector3.prototype.$sub = function(vector) { 254 this.x -= vector.x; 255 this.y -= vector.y; 256 this.z -= vector.z; 257 return this; 258 }; 259 260 /** 261 * Scales this Vector3 by a factor. Does not affect this Vector3. 262 * @param {Number} f The factor to scale this Vector3 with. 263 * @returns {Vector3} A new vector representing the scaled version of this Vector3. 264 */ 265 Vector3.prototype.scale = function(f) { 266 var x = this.x * f, y = this.y * f, z = this.z * f; 267 return new Vector3(x, y, z); 268 }; 269 270 /** 271 * Scales this Vector3 by a factor. Affects this Vector3 272 * @param {Number} f The factor to scale this Vector3 with. 273 * @returns {Vector3} This Vector3 now is scaled. 274 */ 275 Vector3.prototype.$scale = function(f) { 276 this.x *= f; 277 this.y *= f; 278 this.z *= f; 279 return this; 280 }; 281 282 /** 283 * Calculates the angle between this and another Vector3. 284 * @param {Vector3} vector The other Vector3 to calculate the angle with. 285 * @returns {Number} The angle between the two Vector3. 286 */ 287 Vector3.prototype.angleWith = function(vector) { 288 var dot = this.dot(vector), normThis = this.norm(), normThat = vector.norm(); 289 return acos(dot / (normThis * normThat)); 290 }; 291 292 /** 293 * Calculates the distance between this and another Vector3. 294 * @param {Vector3} vector The other Vector3 to calculate the distance with. 295 * @returns {Number} The distance between the two Vector3. 296 */ 297 Vector3.prototype.distTo = function(vector) { 298 var x = this.x - vector.x, y = this.y - vector.y, z = this.z - vector.z; 299 return sqrt(x * x + y * y + z * z); 300 }; 301 302 /** 303 * Calculates the squared distance between this and another Vector3. 304 * @param {Vector3} vector The other Vector3 to calculate the distance with. 305 * @returns {Number} The squared distance between the two Vector3. 306 */ 307 Vector3.prototype.distToSqr = function(vector) { 308 var x = this.x - vector.x, y = this.y - vector.y, z = this.z - vector.z; 309 return (x * x + y * y + z * z); 310 }; 311 312 /** 313 * Computes the dot product between this and another Vector3. 314 * @param {Vector3} vector The other Vector3 to compute the dot product with. 315 * @returns {Number} The dot product. 316 */ 317 Vector3.prototype.dot = function(vector) { 318 return (this.x * vector.x + this.y * vector.y + this.z * vector.z); 319 }; 320 321 /** 322 * Computes the cross product between this and another Vector3. 323 * Does not affect this Vector3. 324 * @param {Vector3} vector The other Vector3 to compute the dot product with. 325 * @returns {Vector3} A new vector representing the croos product. 326 */ 327 Vector3.prototype.cross = function(vector) { 328 var x = this.x, y = this.y, z = this.z, vx = vector.x, vy = vector.y, vz = vector.z; 329 return new Vector3(y * vz - z * vy, x * vz - z * vx, x * vy - y * vx); 330 }; 331 332 /** 333 * Gets the array version of this Vector3. 334 * @returns {Array} An array representation of this Vector3. 335 */ 336 Vector3.prototype.toArray = function() { 337 return [this.x, this.y, this.z]; 338 }; 339 340 return Vector3; 341 342 }()); 343 344 BenchGL.namespace('BenchGL.math.Matrix4'); 345 346 BenchGL.math.Matrix4 = (function() { 347 348 // Dependencies 349 var sin = Math.sin, 350 cos = Math.cos, 351 sqrt = Math.sqrt, 352 tan = Math.tan, 353 pi = Math.PI, 354 Vec3 = BenchGL.math.Vector3, 355 356 // Private properties and methods 357 Matrix4; 358 359 /** 360 * Creates a new Matrix4. If no argumnets are supplied returns the identity matrix. 361 * @class Represents a four by four matrix. 362 * @param {Number} [m11=1] The element at row 1 column 1. 363 * @param {Number} [m12=0] The element at row 1 column 2. 364 * @param {Number} [m13=0] The element at row 1 column 3. 365 * @param {Number} [m14=0] The element at row 1 column 4. 366 * @param {Number} [m21=0] The element at row 2 column 1. 367 * @param {Number} [m22=1] The element at row 2 column 2. 368 * @param {Number} [m23=0] The element at row 2 column 3. 369 * @param {Number} [m24=0] The element at row 2 column 4. 370 * @param {Number} [m31=0] The element at row 3 column 1. 371 * @param {Number} [m32=0] The element at row 3 column 2. 372 * @param {Number} [m33=1] The element at row 3 column 3. 373 * @param {Number} [m34=0] The element at row 3 column 4. 374 * @param {Number} [m41=0] The element at row 4 column 1. 375 * @param {Number} [m42=0] The element at row 4 column 2. 376 * @param {Number} [m43=0] The element at row 4 column 3. 377 * @param {Number} [m44=1] The element at row 4 column 4. 378 */ 379 Matrix4 = function(m11, m12, m13, m14, 380 m21, m22, m23, m24, 381 m31, m32, m33, m34, 382 m41, m42, m43, m44) { 383 if (typeof m11 !== "undefined") { 384 this.set(m11, m12, m13, m14, 385 m21, m22, m23, m24, 386 m31, m32, m33, m34, 387 m41, m42, m43, m44); 388 } 389 else { 390 this.m11 = this.m22 = this.m33 = this.m44 = 1; 391 this.m12 = this.m13 = this.m14 = 0; 392 this.m21 = this.m23 = this.m24 = 0; 393 this.m31 = this.m32 = this.m34 = 0; 394 this.m41 = this.m42 = this.m43 = 0; 395 } 396 }; 397 398 /** 399 * Sets the elements of this Matrix4. 400 * @param {Number} m11 The element at row 1 column 1. 401 * @param {Number} m12 The element at row 1 column 2. 402 * @param {Number} m13 The element at row 1 column 3. 403 * @param {Number} m14 The element at row 1 column 4. 404 * @param {Number} m21 The element at row 2 column 1. 405 * @param {Number} m22 The element at row 2 column 2. 406 * @param {Number} m23 The element at row 2 column 3. 407 * @param {Number} m24 The element at row 2 column 4. 408 * @param {Number} m31 The element at row 3 column 1. 409 * @param {Number} m32 The element at row 3 column 2. 410 * @param {Number} m33 The element at row 3 column 3. 411 * @param {Number} m34 The element at row 3 column 4. 412 * @param {Number} m41 The element at row 4 column 1. 413 * @param {Number} m42 The element at row 4 column 2. 414 * @param {Number} m43 The element at row 4 column 3. 415 * @param {Number} m44 The element at row 4 column 4. 416 */ 417 Matrix4.prototype.set = function(m11, m12, m13, m14, 418 m21, m22, m23, m24, 419 m31, m32, m33, m34, 420 m41, m42, m43, m44){ 421 this.m11 = m11; 422 this.m12 = m12; 423 this.m13 = m13; 424 this.m14 = m14; 425 this.m21 = m21; 426 this.m22 = m22; 427 this.m23 = m23; 428 this.m24 = m24; 429 this.m31 = m31; 430 this.m32 = m32; 431 this.m33 = m33; 432 this.m34 = m34; 433 this.m41 = m41; 434 this.m42 = m42; 435 this.m43 = m43; 436 this.m44 = m44; 437 return this; 438 }; 439 440 /** 441 * Sets this Matrix4 to the identity matrix. Affects this Matrix4. 442 * @returns {Matrix4} This Matrix4 is now an identity matrix. 443 */ 444 Matrix4.prototype.$identity = function() { 445 this.m11 = this.m22 = this.m33 = this.m44 = 1; 446 this.m12 = this.m13 = this.m14 = 0; 447 this.m21 = this.m23 = this.m24 = 0; 448 this.m31 = this.m32 = this.m34 = 0; 449 this.m41 = this.m42 = this.m43 = 0; 450 return this; 451 }; 452 453 /** 454 * Copies this Matrix4 in another one. 455 * @returns {Matrix4} A copy of this Matrix4. 456 */ 457 Matrix4.prototype.copy = function() { 458 var m11 = this.m11, m12 = this.m12, m13 = this.m13, m14 = this.m14, 459 m21 = this.m21, m22 = this.m22, m23 = this.m23, m24 = this.m24, 460 m31 = this.m31, m32 = this.m32, m33 = this.m33, m34 = this.m34, 461 m41 = this.m41, m42 = this.m42, m43 = this.m43, m44 = this.m44; 462 463 return new Matrix4(m11, m12, m13, m14, 464 m21, m22, m23, m24, 465 m31, m32, m33, m34, 466 m41, m42, m43, m44); 467 }; 468 469 /** 470 * Adds this Matrix4 to another one. Does not affect this Matrix4. 471 * @param {Matrix4} m The Matrix4 to add. 472 * @returns {Matrix4} A new matrix containing the addition. 473 */ 474 Matrix4.prototype.add = function(m) { 475 var r = new Matrix4(); 476 477 r.m11 = this.m11 + m.m11; 478 r.m12 = this.m12 + m.m12; 479 r.m13 = this.m13 + m.m13; 480 r.m14 = this.m14 + m.m14; 481 r.m21 = this.m21 + m.m21; 482 r.m22 = this.m22 + m.m22; 483 r.m23 = this.m23 + m.m23; 484 r.m24 = this.m24 + m.m24; 485 r.m31 = this.m31 + m.m31; 486 r.m32 = this.m32 + m.m32; 487 r.m33 = this.m33 + m.m33; 488 r.m34 = this.m34 + m.m34; 489 r.m41 = this.m41 + m.m41; 490 r.m42 = this.m42 + m.m42; 491 r.m43 = this.m43 + m.m43; 492 r.m44 = this.m44 + m.m44; 493 494 return r; 495 }; 496 497 /** 498 * Adds this Matrix4 to another one. Affects this Matrix4. 499 * @param {Matrix4} m The Matrix4 to add. 500 * @returns {Matrix4} This Matrix4 now contains the addition. 501 */ 502 Matrix4.prototype.$add = function(m){ 503 this.m11 += m.m11; 504 this.m12 += m.m12; 505 this.m13 += m.m13; 506 this.m14 += m.m14; 507 this.m21 += m.m21; 508 this.m22 += m.m22; 509 this.m23 += m.m23; 510 this.m24 += m.m24; 511 this.m31 += m.m31; 512 this.m32 += m.m32; 513 this.m33 += m.m33; 514 this.m34 += m.m34; 515 this.m41 += m.m41; 516 this.m42 += m.m42; 517 this.m43 += m.m43; 518 this.m44 += m.m44; 519 return this; 520 }; 521 522 /** 523 * Subtracts this Matrix4 to another one. Does not affect this Matrix4. 524 * @param {Matrix4} m The Matrix4 to add. 525 * @returns {Matrix4} A new matrix containing the subtraction. 526 */ 527 Matrix4.prototype.sub = function(m){ 528 var r = new Matrix4(); 529 530 r.m11 = this.m11 - m.m11; 531 r.m12 = this.m12 - m.m12; 532 r.m13 = this.m13 - m.m13; 533 r.m14 = this.m14 - m.m14; 534 r.m21 = this.m21 - m.m21; 535 r.m22 = this.m22 - m.m22; 536 r.m23 = this.m23 - m.m23; 537 r.m24 = this.m24 - m.m24; 538 r.m31 = this.m31 - m.m31; 539 r.m32 = this.m32 - m.m32; 540 r.m33 = this.m33 - m.m33; 541 r.m34 = this.m34 - m.m34; 542 r.m41 = this.m41 - m.m41; 543 r.m42 = this.m42 - m.m42; 544 r.m43 = this.m43 - m.m43; 545 r.m44 = this.m44 - m.m44; 546 547 return r; 548 }; 549 550 /** 551 * Subtracts this Matrix4 to another one. Affects this Matrix4. 552 * @param {Matrix4} m The Matrix4 to add. 553 * @returns {Matrix4} This Matrix4 now contains the subtraction. 554 */ 555 Matrix4.prototype.$sub = function(m){ 556 this.m11 -= m.m11; 557 this.m12 -= m.m12; 558 this.m13 -= m.m13; 559 this.m14 -= m.m14; 560 this.m21 -= m.m21; 561 this.m22 -= m.m22; 562 this.m23 -= m.m23; 563 this.m24 -= m.m24; 564 this.m31 -= m.m31; 565 this.m32 -= m.m32; 566 this.m33 -= m.m33; 567 this.m34 -= m.m34; 568 this.m41 -= m.m41; 569 this.m42 -= m.m42; 570 this.m43 -= m.m43; 571 this.m44 -= m.m44; 572 return this; 573 }; 574 575 /** 576 * Multiplies a Vector3 by this Matrix4. (r = M*v) 577 * @returns {Vector3} A new vector with the result of the multiplication. 578 */ 579 Matrix4.prototype.multiplyVec3 = function(vector) { 580 var vx = vector.x, 581 vy = vector.y, 582 vz = vector.z; 583 584 return new Vec3(this.m11 * vx + this.m12 * vy + this.m13 * vz + this.m14, 585 this.m21 * vx + this.m22 * vy + this.m23 * vz + this.m24, 586 this.m31 * vx + this.m32 * vy + this.m33 * vz + this.m34); 587 }; 588 589 /** 590 * Multiplies this Matrix4 by another one. Does not affect this Matrix4. 591 * @returns {Matrix4} A new matrix with the result of the multiplication. 592 */ 593 Matrix4.prototype.multiplyMat4 = function(m) { 594 var a11 = this.m11, a12 = this.m12, a13 = this.m13, a14 = this.m14, 595 a21 = this.m21, a22 = this.m22, a23 = this.m23, a24 = this.m24, 596 a31 = this.m31, a32 = this.m32, a33 = this.m33, a34 = this.m34, 597 a41 = this.m41, a42 = this.m42, a43 = this.m43, a44 = this.m44, 598 b11 = m.m11, b12 = m.m12, b13 = m.m13, b14 = m.m14, 599 b21 = m.m21, b22 = m.m22, b23 = m.m23, b24 = m.m24, 600 b31 = m.m31, b32 = m.m32, b33 = m.m33, b34 = m.m34, 601 b41 = m.m41, b42 = m.m42, b43 = m.m43, b44 = m.m44, 602 m11, m12, m13, m14, 603 m21, m22, m23, m24, 604 m31, m32, m33, m34, 605 m41, m42, m43, m44; 606 607 m11 = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41; 608 m12 = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42; 609 m13 = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43; 610 m14 = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44; 611 612 m21 = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41; 613 m22 = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42; 614 m23 = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43; 615 m24 = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44; 616 617 m31 = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41; 618 m32 = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42; 619 m33 = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43; 620 m34 = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44; 621 622 m41 = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41; 623 m42 = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42; 624 m43 = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43; 625 m44 = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44; 626 627 return new Matrix4(m11, m12, m13, m14, 628 m21, m22, m23, m24, 629 m31, m32, m33, m34, 630 m41, m42, m43, m44); 631 }; 632 633 /** 634 * Multiplies this Matrix4 by another one. Affects this Matrix4. 635 * @returns {Matrix4} This Matrix4 now stores the result of the multiplication. 636 */ 637 Matrix4.prototype.$multiplyMat4 = function(m){ 638 var a11 = this.m11, a12 = this.m12, a13 = this.m13, a14 = this.m14, 639 a21 = this.m21, a22 = this.m22, a23 = this.m23, a24 = this.m24, 640 a31 = this.m31, a32 = this.m32, a33 = this.m33, a34 = this.m34, 641 a41 = this.m41, a42 = this.m42, a43 = this.m43, a44 = this.m44, 642 b11 = m.m11, b12 = m.m12, b13 = m.m13, b14 = m.m14, 643 b21 = m.m21, b22 = m.m22, b23 = m.m23, b24 = m.m24, 644 b31 = m.m31, b32 = m.m32, b33 = m.m33, b34 = m.m34, 645 b41 = m.m41, b42 = m.m42, b43 = m.m43, b44 = m.m44, 646 m11, m12, m13, m14, 647 m21, m22, m23, m24, 648 m31, m32, m33, m34, 649 m41, m42, m43, m44; 650 651 m11 = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41; 652 m12 = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42; 653 m13 = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43; 654 m14 = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44; 655 656 m21 = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41; 657 m22 = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42; 658 m23 = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43; 659 m24 = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44; 660 661 m31 = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41; 662 m32 = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42; 663 m33 = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43; 664 m34 = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44; 665 666 m41 = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41; 667 m42 = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42; 668 m43 = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43; 669 m44 = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44; 670 671 this.set(m11, m12, m13, m14, 672 m21, m22, m23, m24, 673 m31, m32, m33, m34, 674 m41, m42, m43, m44); 675 676 return this; 677 }; 678 679 /** 680 * Transpose this Matrix4. Does not affect this Matrix4. 681 * @returns {Matrix4} A new matrix representing the transpose of this Matrix4. 682 */ 683 Matrix4.prototype.transpose = function(){ 684 var m11 = this.m11, m12 = this.m12, m13 = this.m13, m14 = this.m14, 685 m21 = this.m21, m22 = this.m22, m23 = this.m23, m24 = this.m24, 686 m31 = this.m31, m32 = this.m32, m33 = this.m33, m34 = this.m34, 687 m41 = this.m41, m42 = this.m42, m43 = this.m43, m44 = this.m44; 688 689 return new Matrix4(m11, m21, m31, m41, 690 m12, m22, m32, m42, 691 m13, m23, m33, m43, 692 m14, m24, m34, m44); 693 }; 694 695 /** 696 * Transpose this Matrix4. Affects this Matrix4. 697 * @returns {Matrix4} This Matrix4 is now transposed. 698 */ 699 Matrix4.prototype.$transpose = function(){ 700 var m11 = this.m11, m12 = this.m12, m13 = this.m13, m14 = this.m14, 701 m21 = this.m21, m22 = this.m22, m23 = this.m23, m24 = this.m24, 702 m31 = this.m31, m32 = this.m32, m33 = this.m33, m34 = this.m34, 703 m41 = this.m41, m42 = this.m42, m43 = this.m43, m44 = this.m44; 704 705 this.set(m11, m21, m31, m41, 706 m12, m22, m32, m42, 707 m13, m23, m33, m43, 708 m14, m24, m34, m44); 709 710 return this; 711 }; 712 713 /** 714 * Inverts this Matrix4. Does not affect this Matrix4. 715 * @returns {Matrix4} A new matrix representing the inverted of this MAtrix4. 716 */ 717 Matrix4.prototype.invert = function() { 718 var m11 = this.m11, m12 = this.m12, m13 = this.m13, m14 = this.m14, 719 m21 = this.m21, m22 = this.m22, m23 = this.m23, m24 = this.m24, 720 m31 = this.m31, m32 = this.m32, m33 = this.m33, m34 = this.m34, 721 m41 = this.m41, m42 = this.m42, m43 = this.m43, m44 = this.m44, 722 723 s0 = m11 * m22 - m12 * m21, 724 s1 = m11 * m23 - m13 * m21, 725 s2 = m11 * m24 - m14 * m21, 726 s3 = m12 * m23 - m13 * m22, 727 s4 = m12 * m24 - m14 * m22, 728 s5 = m13 * m24 - m14 * m23, 729 c0 = m31 * m42 - m32 * m41, 730 c1 = m31 * m43 - m33 * m41, 731 c2 = m31 * m44 - m34 * m41, 732 c3 = m32 * m43 - m33 * m42, 733 c4 = m32 * m44 - m34 * m42, 734 c5 = m33 * m44 - m34 * m43, 735 736 det = s0 * c5 - s1 * c4 + s2 * c3 + s3 * c2 - s4 * c1 + s5 * c0, 737 738 a11 = m22 * c5 - m23 * c4 + m24 * c3, 739 a12 = -m12 * c5 + m13 * c4 - m14 * c3, 740 a13 = m42 * s5 - m43 * s4 + m44 * s3, 741 a14 = -m32 * s5 + m33 * s4 - m34 * s3, 742 a21 = -m21 * c5 + m23 * c2 - m24 * c1, 743 a22 = m11 * c5 - m13 * c2 + m14 * c1, 744 a23 = -m41 * s5 + m43 * s2 - m44 * s1, 745 a24 = m31 * s5 - m33 * s2 + m34 * s1, 746 a31 = m21 * c4 - m22 * c2 + m24 * c0, 747 a32 = -m11 * c4 + m12 * c2 - m14 * c0, 748 a33 = m41 * s4 - m42 * s2 + m44 * s0, 749 a34 = -m31 * s4 + m32 * s2 - m34 * s0, 750 a41 = -m21 * c3 + m22 * c1 - m23 * c0, 751 a42 = m11 * c3 - m12 * c1 + m13 * c0, 752 a43 = -m41 * s3 + m42 * s1 - m43 * s0, 753 a44 = m31 * s3 - m32 * s1 + m33 * s0; 754 755 return new Matrix4(a11 / det, a12 / det, a13 / det, a14 / det, 756 a21 / det, a22 / det, a23 / det, a24 / det, 757 a31 / det, a32 / det, a33 / det, a34 / det, 758 a41 / det, a42 / det, a43 / det, a44 / det); 759 }; 760 761 /** 762 * Inverts this Matrix4. Affects this Matrix4. 763 * @returns {Matrix4} This Matrix4 is now inverted. 764 */ 765 Matrix4.prototype.$invert = function() { 766 var m11 = this.m11, m12 = this.m12, m13 = this.m13, m14 = this.m14, 767 m21 = this.m21, m22 = this.m22, m23 = this.m23, m24 = this.m24, 768 m31 = this.m31, m32 = this.m32, m33 = this.m33, m34 = this.m34, 769 m41 = this.m41, m42 = this.m42, m43 = this.m43, m44 = this.m44, 770 771 s0 = m11 * m22 - m12 * m21, 772 s1 = m11 * m23 - m13 * m21, 773 s2 = m11 * m24 - m14 * m21, 774 s3 = m12 * m23 - m13 * m22, 775 s4 = m12 * m24 - m14 * m22, 776 s5 = m13 * m24 - m14 * m23, 777 c0 = m31 * m42 - m32 * m41, 778 c1 = m31 * m43 - m33 * m41, 779 c2 = m31 * m44 - m34 * m41, 780 c3 = m32 * m43 - m33 * m42, 781 c4 = m32 * m44 - m34 * m42, 782 c5 = m33 * m44 - m34 * m43, 783 784 det = s0 * c5 - s1 * c4 + s2 * c3 + s3 * c2 - s4 * c1 + s5 * c0, 785 786 a11 = m22 * c5 - m23 * c4 + m24 * c3, 787 a12 = -m12 * c5 + m13 * c4 - m14 * c3, 788 a13 = m42 * s5 - m43 * s4 + m44 * s3, 789 a14 = -m32 * s5 + m33 * s4 - m34 * s3, 790 a21 = -m21 * c5 + m23 * c2 - m24 * c1, 791 a22 = m11 * c5 - m13 * c2 + m14 * c1, 792 a23 = -m41 * s5 + m43 * s2 - m44 * s1, 793 a24 = m31 * s5 - m33 * s2 + m34 * s1, 794 a31 = m21 * c4 - m22 * c2 + m24 * c0, 795 a32 = -m11 * c4 + m12 * c2 - m14 * c0, 796 a33 = m41 * s4 - m42 * s2 + m44 * s0, 797 a34 = -m31 * s4 + m32 * s2 - m34 * s0, 798 a41 = -m21 * c3 + m22 * c1 - m23 * c0, 799 a42 = m11 * c3 - m12 * c1 + m13 * c0, 800 a43 = -m41 * s3 + m42 * s1 - m43 * s0, 801 a44 = m31 * s3 - m32 * s1 + m33 * s0; 802 803 this.set(a11 / det, a12 / det, a13 / det, a14 / det, 804 a21 / det, a22 / det, a23 / det, a24 / det, 805 a31 / det, a32 / det, a33 / det, a34 / det, 806 a41 / det, a42 / det, a43 / det, a44 / det); 807 808 return this; 809 }; 810 811 /** 812 * Gets a Float32Array representation of this Matrix4. 813 * @returns {Float32Array} A new Float32Array containing the elements of this Matrix. 814 */ 815 Matrix4.prototype.toFloat32Array = function() { 816 return new Float32Array([this.m11, this.m21, this.m31, this.m41, 817 this.m12, this.m22, this.m32, this.m42, 818 this.m13, this.m23, this.m33, this.m43, 819 this.m14, this.m24, this.m34, this.m44]); 820 }; 821 822 /** 823 * Creates the identity matrix. 824 * @returns {Matrix4} The identity matrix. 825 */ 826 Matrix4.Identity = function() { 827 return new Matrix4(); 828 }; 829 830 /** 831 * Creates a translation matrix. 832 * @param {Number} x The translation on x coordinate. 833 * @param {Number} y The translation on y coordinate. 834 * @param {Number} z The translation on z coordinate. 835 * @returns {Matrix4} A translation matrix. 836 */ 837 Matrix4.Translate = function(x, y, z){ 838 return new Matrix4(1, 0, 0, x, 839 0, 1, 0, y, 840 0, 0, 1, z, 841 0, 0, 0, 1); 842 }; 843 844 /** 845 * Creates a scale matrix. 846 * @param {Number} x The scale on x coordinate. 847 * @param {Number} y The scale on y coordinate. 848 * @param {Number} z The scale on z coordinate. 849 * @returns {Matrix4} A scale matrix. 850 */ 851 Matrix4.Scale = function(x, y, z){ 852 return new Matrix4(x, 0, 0, 0, 853 0, y, 0, 0, 854 0, 0, z, 0, 855 0, 0, 0, 1); 856 }; 857 858 /** 859 * Creates a general rotation matrix around an axis. 860 * @param {Number} angle The rotation angle in radians. 861 * @param {Number} x The axis x coordinate. 862 * @param {Number} y The axis y coordinate. 863 * @param {Number} z The axis z coordinate. 864 * @returns {Matrix4} A general rotation matrix. 865 */ 866 Matrix4.Rotate = function(angle, x, y, z) { 867 var axis = new Vec3(x, y, z).$unit(), 868 ax = axis.x, ay = axis.y, az = axis.z, 869 s = sin(angle), c = cos(angle), t = 1 - c; 870 return new Matrix4(t * ax * ax + c, t * ax * ay - az * s, t * ax * az + ay * s, 0, 871 t * ax * ay + az * s, t * ay * ay + c, t * ay * az - ax * s, 0, 872 t * ax * az - ay * s, t * ay * az + ax * s, t * az * az + c, 0, 873 0, 0, 0, 1); 874 }; 875 876 /** 877 * Creates a rotation matrix around an x, y and z axis. 878 * @param {Number} rx The rotation angle around x axis. 879 * @param {Number} ry The rotation angle around y axis. 880 * @param {Number} rz The rotation angle around z axis. 881 * @returns {Matrix4} A XYZ rotation matrix. 882 */ 883 Matrix4.RotateXYZ = function(rx, ry, rz) { 884 var sx = sin(rx), cx = cos(rx), 885 sy = sin(ry), cy = cos(ry), 886 sz = sin(rz), cz = cos(rz); 887 888 return new Matrix4(cy * cz, -cx * sz + sx * sy * cz, sx * sz + cx * sy * cz, 0, 889 cy * sz, cx * cz + sx * sy * sz, -sx * cz + cx * sy * sz, 0, 890 -sy, sx * cy, cx * cy, 0, 891 0, 0, 0, 1); 892 }; 893 894 /** 895 * Creates a lookAt matrix. 896 * @param {Vector3} eye The eye vector. 897 * @param {Vector3} direction The direction vector. 898 * @param {Vector3} up The up vector. 899 * @returns {Matrix4} A lookAt Matrix4. 900 */ 901 Matrix4.LookAt = function(eye, direction, up) { 902 var x, y, z; 903 904 z = eye.sub(direction); 905 z.$unit(); 906 x = up.cross(z); 907 x.$unit(); 908 y = z.cross(x); 909 y.$unit(); 910 911 return new Matrix4(x.x, x.y, x.z, -x.dot(eye), 912 y.x, y.y, y.z, -y.dot(eye), 913 z.x, z.y, z.z, -z.dot(eye), 914 0, 0, 0, 1); 915 }; 916 917 /** 918 * Creates a frustum matrix. 919 * @param {Number} left The left clipping plane. 920 * @param {Number} right The right clipping plane. 921 * @param {Number} bottom The bottom clipping plane. 922 * @param {Number} top The top clipping plane. 923 * @param {Number} near The near clipping plane. 924 * @param {Number} far The far clipping plane. 925 * @returns {Matrix4} A frustum matrix. 926 */ 927 Matrix4.Frustum = function(left, right, bottom, top, near, far) { 928 var x = 2.0 * near / (right - left), 929 y = 2.0 * near / (top - bottom), 930 a = (right + left) / (right - left), 931 b = (top + bottom) / (top - bottom), 932 c = -(far + near) / (far - near), 933 d = -2.0 * far * near / (far - near); 934 935 return new Matrix4(x, 0, a, 0, 936 0, y, b, 0, 937 0, 0, c, d, 938 0, 0, -1, 0); 939 }; 940 941 /** 942 * Creates a perspective matrix. 943 * @param {Number} fovy The vertical field of view angle. 944 * @param {Number} aspect The aspect ratio aka the horizontal field of view angle. 945 * @param {Number} near The near clipping plane. 946 * @param {Number} far The far clipping plane. 947 * @returns {Matrix4} A perspective matrix. 948 */ 949 Matrix4.Perspective = function(fovy, aspect, near, far) { 950 var ymax = near * tan(fovy * pi / 360.0), 951 ymin = -ymax, 952 xmin = ymin * aspect, 953 xmax = ymax * aspect; 954 955 return Matrix4.Frustum(xmin, xmax, ymin, ymax, near, far); 956 }; 957 958 return Matrix4; 959 960 }()); 961 962 BenchGL.namespace('BenchGL.math.Quaternion'); 963 964 BenchGL.math.Quaternion = (function() { 965 966 // Dependencies 967 var sin = Math.sin, 968 cos = Math.cos, 969 Mat4 = BenchGL.math.Matrix4, 970 971 // Private properties and methods 972 Quaternion; 973 /** 974 * Creates a new Quaternion. 975 * @class Represents a quaternion. 976 * @param {Number} a The scalar part. 977 * @param {Number} b The i component of the vector part. 978 * @param {Number} c The j component of the vector part. 979 * @param {Number} d The k component of the vector part. 980 */ 981 Quaternion = function(a, b, c, d) { 982 if (typeof a !== "undefined") { 983 this.a = a; 984 this.b = b; 985 this.c = c; 986 this.d = d; 987 } 988 else { 989 this.a = 1; 990 this.b = 0; 991 this.c = 0; 992 this.d = 0; 993 } 994 }; 995 996 /** 997 * Sets this Quaternion as the identity quaternion. 998 * @returns {Quaternion} This Quaternion is now the identity quaternion. 999 */ 1000 Quaternion.prototype.identity = function() { 1001 this.a = 1; 1002 this.b = 0; 1003 this.c = 0; 1004 this.d = 0; 1005 return this; 1006 }; 1007 1008 /** 1009 * Multiplies this Quaternion with another one. Does not affect this Quaternion. 1010 * @param {Quaternion} quat The Quaternion to multiply with. 1011 * @returns {Quaternion} A new quaternion containing the multiplication's result. 1012 */ 1013 Quaternion.prototype.multiply = function(quat) { 1014 var a1 = this.m, 1015 b1 = this.b, 1016 c1 = this.c, 1017 d1 = this.d, 1018 a2 = quat.a, 1019 b2 = quat.b, 1020 c2 = quat.c, 1021 d2 = quat.d, 1022 1023 a = a1 * a2 - b1 * b2 - c1 * c2 - d1 * d2, 1024 b = a1 * b2 + b1 * a2 + c1 * d2 - d1 * c2, 1025 c = a1 * c2 - b1 * d2 + c1 * a2 + d1 * b2, 1026 d = a1 * d2 + b1 * c2 - c1 * b2 + d1 * a2; 1027 1028 return new Quaternion(a, b, c, d); 1029 }; 1030 1031 /** 1032 * Multiplies this Quaternion with another one. Affects this Quaternion. 1033 * @param {Quaternion} quat The Quaternion to multiply with. 1034 * @returns {Quaternion} This Quaternion now stores the result of the multiplication. 1035 */ 1036 Quaternion.prototype.$multiply = function(quat) { 1037 var a1 = this.m, 1038 b1 = this.b, 1039 c1 = this.c, 1040 d1 = this.d, 1041 a2 = quat.a, 1042 b2 = quat.b, 1043 c2 = quat.c, 1044 d2 = quat.d, 1045 1046 a = a1 * a2 - b1 * b2 - c1 * c2 - d1 * d2, 1047 b = a1 * b2 + b1 * a2 + c1 * d2 - d1 * c2, 1048 c = a1 * c2 - b1 * d2 + c1 * a2 + d1 * b2, 1049 d = a1 * d2 + b1 * c2 - c1 * b2 + d1 * a2; 1050 1051 this.m = a; 1052 this.b = b; 1053 this.c = c; 1054 this.d = d; 1055 1056 return this; 1057 }; 1058 1059 /** 1060 * Gets a Matrix4 representing this Quaternion. 1061 * @returns {Matrix4} A matrix representing this Quaternion. 1062 */ 1063 Quaternion.prototype.toMatrix4 = function() { 1064 var a = this.m, b = this.b, c = this.c, d = this.d, 1065 b2 = b * b, c2 = c * c, d2 = d * d, 1066 bc = b * c, bd = b * d, cd = c * d, ab = a * b, ac = a * c, ad = a * d; 1067 1068 return new Mat4(1 - 2 * c2 - 2 * d2, 2 * bc - 2 * ad, 2 * ac + 2 * bd, 0, 1069 2 * bc + 2 * ad, 1 - 2 * b2 - 2 * d2, 2 * cd - 2 * ab, 0, 1070 2 * bd - 2 * ac, 2 * ab + 2 * cd, 1 - 2 * b2 - 2 * c2, 0, 1071 0, 0, 0, 1); 1072 }; 1073 1074 /** 1075 * Creates a new quaternion from euler angles. 1076 * @returns {Quaternion} A new quaternion from the euler angles. 1077 */ 1078 Quaternion.FromEulerAngles = function(pitch, roll, yaw) { 1079 var p = pitch * 0.5, y = yaw * 0.5, r = roll * 0.5, 1080 cp = cos(p), sp = sin(p), 1081 cy = cos(y), sy = sin(y), 1082 cr = cos(r), sr = sin(r), 1083 1084 a = cp * cy * cr + sp * sy * sr, 1085 b = sp * cy * cr - cp * sy * sr, 1086 c = cp * sy * cr + sp * cy * sr, 1087 d = cp * cy * sr - sp * sy * cr; 1088 1089 return new Quaternion(a, b, c, d); 1090 }; 1091 1092 return Quaternion; 1093 1094 }()); 1095 1096 BenchGL.namespace('BenchGL.math.MatrixStack'); 1097 1098 BenchGL.math.MatrixStack = (function() { 1099 1100 // Dependencies 1101 var Mat4 = BenchGL.math.Matrix4, 1102 pi = Math.PI, 1103 1104 // Private properties and methods 1105 MatrixStack; 1106 1107 /** 1108 * Creates a new MatrixStack. 1109 * @class Representa a stack for 4x4 matrices. 1110 */ 1111 MatrixStack = function() { 1112 this.stack = []; 1113 this.current = 0; 1114 this.stack.push(Mat4.Identity()); 1115 }; 1116 1117 /** 1118 * Gets the matrix on top of this MatrixStack. 1119 * @returns {Matrix4} The matrix on top of the stack. 1120 */ 1121 MatrixStack.prototype.top = function() { 1122 return this.stack[this.current]; 1123 }; 1124 1125 /** 1126 * Pushes a copy of the top matrix on this MatrixStack. 1127 * @returns {MatrixStack} Returns this MatrixStack. 1128 */ 1129 MatrixStack.prototype.push = function() { 1130 this.stack.push(this.stack[this.current].copy()); 1131 this.current++; 1132 return this; 1133 }; 1134 1135 /** 1136 * Pops the top matrix on top of this MatrixStack. 1137 * @returns {MatrixStack} Returns this MatrixStack. 1138 */ 1139 MatrixStack.prototype.pop = function() { 1140 if (this.current > 0) { 1141 this.stack.pop(); 1142 this.current--; 1143 } 1144 return this; 1145 }; 1146 1147 /** 1148 * Loads a matrix on top of this MatrixStack. 1149 * @param {Matrix4} The matrix to load. 1150 * @returns {MatrixStack} Returns this MatrixStack. 1151 */ 1152 MatrixStack.prototype.load = function(matrix) { 1153 this.stack[this.current] = matrix; 1154 return this; 1155 }; 1156 1157 /** 1158 * Loads the identity matrix on top of this MatrixStack. 1159 * @returns {MatrixStack} Returns this MatrixStack. 1160 */ 1161 MatrixStack.prototype.loadIdentity = function() { 1162 this.stack[this.current] = Mat4.Identity(); 1163 return this; 1164 }; 1165 1166 /** 1167 * Multiplies a matrix with the one on top of this MatrixStack. 1168 * @param {Matrix4} The matrix to multiply with. 1169 * @returns {MatrixStack} Returns this MatrixStack. 1170 */ 1171 MatrixStack.prototype.multiply = function(matrix) { 1172 this.stack[this.current].$multiplyMat4(matrix); 1173 return this; 1174 }; 1175 1176 /** 1177 * Apply a rotation to the matrix on top of this MatrixStack. 1178 * @param {Number} angle The rotation angle. 1179 * @param {Number} x The rotation axis x coordinate. 1180 * @param {Number} y The rotation axis y coordinate. 1181 * @param {Number} z The rotation axis z coordinate. 1182 * @returns {MatrixStack} Returns this MatrixStack. 1183 */ 1184 MatrixStack.prototype.rotate = function(angle, x, y, z) { 1185 this.multiply(Mat4.Rotate(angle * pi / 180, x, y, z)); 1186 return this; 1187 }; 1188 1189 /** 1190 * Apply a rotation to the matrix on top of this MatrixStack. 1191 * @param {Number} rx The rotation angle around X axis. 1192 * @param {Number} ry The rotation angle around Y axis. 1193 * @param {Number} rz The rotation angle around Z axis. 1194 * @returns {MatrixStack} Returns this MatrixStack. 1195 */ 1196 MatrixStack.prototype.rotateXYZ = function(rx, ry, rz) { 1197 var conversion = pi / 180; 1198 this.multiply(Mat4.RotateXYZ(rx * conversion, ry * conversion, rz * conversion)); 1199 return this; 1200 }; 1201 1202 /** 1203 * Apply a scale transformation to the matrix on top of this MatrixStack. 1204 * @param {Number} x The x scale factor. 1205 * @param {Number} y The y scale factor. 1206 * @param {Number} z The z scale factor. 1207 * @returns {MatrixStack} Returns this MatrixStack. 1208 */ 1209 MatrixStack.prototype.scale = function(x, y, z) { 1210 this.multiply(Mat4.Scale(x, y, z)); 1211 return this; 1212 }; 1213 1214 /** 1215 * Apply a translation to the matrix on top of this MatrixStack. 1216 * @param {Number} x The x translation factor. 1217 * @param {Number} y The y translation factor. 1218 * @param {Number} z The z translation factor. 1219 * @returns {MatrixStack} Returns this MatrixStack. 1220 */ 1221 MatrixStack.prototype.translate = function(x, y, z) { 1222 this.multiply(Mat4.Translate(x, y, z)); 1223 return this; 1224 }; 1225 1226 /** 1227 * Apply a lookAt transformation to the matrix on top of this MatrixStack. 1228 * @param {Vector3} eye The eye vector. 1229 * @param {Vector3} direction The direction vector. 1230 * @param {Vector3} up The up vector. 1231 * @returns {MatrixStack} Returns this MatrixStack. 1232 */ 1233 MatrixStack.prototype.lookAt = function(eye, direction, up) { 1234 this.multiply(Mat4.LookAt(eye, direction, up)); 1235 return this; 1236 }; 1237 1238 /** 1239 * Apply a perspective transformation to the matrix on top of this MatrixStack. 1240 * @param {Number} fovy The vertical field of view angle. 1241 * @param {Number} aspect The aspect ratio aka the horizontal field of view angle. 1242 * @param {Number} near The near clipping plane. 1243 * @param {Number} far The far clipping plane. 1244 * @returns {MatrixStack} Returns this MatrixStack. 1245 */ 1246 MatrixStack.prototype.perspective = function(fovy, aspect, near, far) { 1247 this.multiply(Mat4.Perspective(fovy, aspect, near, far)); 1248 return this; 1249 }; 1250 1251 /** 1252 * Apply a frustum transformation to the matrix on top of this MatrixStack. 1253 * @param {Number} left The left clipping plane. 1254 * @param {Number} right The right clipping plane. 1255 * @param {Number} bottom The bottom clipping plane. 1256 * @param {Number} top The top clipping plane. 1257 * @param {Number} near The near clipping plane. 1258 * @param {Number} far The far clipping plane. 1259 * @returns {MatrixStack} Returns this MatrixStack. 1260 */ 1261 MatrixStack.prototype.frustum = function(left, rigth, bottom, top, near, far) { 1262 this.multiply(Mat4.Frustum(left, rigth, bottom, top, near, far)); 1263 return this; 1264 }; 1265 1266 return MatrixStack; 1267 1268 }()); 1269 1270 BenchGL.namespace('BenchGL.math.TransformStack'); 1271 1272 BenchGL.math.TransformStack = (function() { 1273 1274 // Private properties and methods 1275 var TransformStack; 1276 1277 /** 1278 * Creates a new TransformStack. 1279 * @class Represents a full stack for model, view and projection matrices (OpenGL-like). 1280 */ 1281 TransformStack = function() { 1282 this.modelStack = new MatrixStack(); 1283 this.viewStack = new MatrixStack(); 1284 this.projStack = new MatrixStack(); 1285 this.currentStack = null; 1286 }; 1287 1288 /** 1289 * Gets the current model matrix. 1290 * @returns {Matrix4} The current model matrix. 1291 */ 1292 TransformStack.prototype.getModelMatrix = function() { 1293 return this.modelStack.top(); 1294 }; 1295 1296 /** 1297 * Gets the transposed version of the current model matrix. 1298 * @returns {Matrix4} The current model matrix transposed. 1299 */ 1300 TransformStack.prototype.getModelMatrixTranspose = function() { 1301 return this.modelStack.top().transpose(); 1302 }; 1303 1304 /** 1305 * Gets the inverse of the current model matrix. 1306 * @returns {Matrix4} The current model matrix inverted. 1307 */ 1308 TransformStack.prototype.getModelMatrixInverse = function() { 1309 return this.modelStack.top().invert(); 1310 }; 1311 1312 /** 1313 * Gets the inverse of the transposed of the current model matrix. 1314 * @returns {Matrix4} The current model matrix inverted transposed. 1315 */ 1316 TransformStack.prototype.getModelMatrixInverseTranspose = function() { 1317 return this.modelStack.top().invert().$transpose(); 1318 }; 1319 1320 /** 1321 * Gets the current view matrix. 1322 * @returns {Matrix4} The current view matrix. 1323 */ 1324 TransformStack.prototype.getViewMatrix = function() { 1325 return this.viewStack.top(); 1326 }; 1327 1328 /** 1329 * Gets the transposed version of the current view matrix. 1330 * @returns {Matrix4} The current view matrix transposed. 1331 */ 1332 TransformStack.prototype.getViewMatrixTranspose = function() { 1333 return this.viewStack.top().transpose(); 1334 }; 1335 1336 /** 1337 * Gets the inverse of the current view matrix. 1338 * @returns {Matrix4} The current view matrix inverted. 1339 */ 1340 TransformStack.prototype.getViewMatrixInverse = function() { 1341 return this.viewStack.top().invert(); 1342 }; 1343 1344 /** 1345 * Gets the inverse of the transposed of the current view matrix. 1346 * @returns {Matrix4} The current view matrix inverted transposed. 1347 */ 1348 TransformStack.prototype.getViewMatrixInverseTranspose = function() { 1349 return this.viewStack.top().invert().$transpose(); 1350 }; 1351 1352 /** 1353 * Gets the current projection matrix. 1354 * @returns {Matrix4} The current projection matrix. 1355 */ 1356 TransformStack.prototype.getProjectionMatrix = function() { 1357 return this.projStack.top(); 1358 }; 1359 1360 /** 1361 * Gets the transposed version of the current projection matrix. 1362 * @returns {Matrix4} The current projection matrix transposed. 1363 */ 1364 TransformStack.prototype.getProjectionMatrixTranspose = function() { 1365 return this.projStack.top().transpose(); 1366 }; 1367 1368 /** 1369 * Gets the inverse of the current projection matrix. 1370 * @returns {Matrix4} The current projection matrix inverted. 1371 */ 1372 TransformStack.prototype.getProjectionMatrixInverse = function() { 1373 return this.projStack.top().invert(); 1374 }; 1375 1376 /** 1377 * Gets the inverse of the transposed of the current projection matrix. 1378 * @returns {Matrix4} The current projection matrix inverted transposed. 1379 */ 1380 TransformStack.prototype.getProjectionMatrixInverseTranspose = function() { 1381 return this.projStack.top().inverse().$transpose(); 1382 }; 1383 1384 /** 1385 * Gets the current modelView matrix. 1386 * @returns {Matrix4} The current projection matrix. 1387 */ 1388 TransformStack.prototype.getModelViewMatrix = function() { 1389 return this.viewStack.top().multiplyMat4(this.modelStack.top()); 1390 }; 1391 1392 /** 1393 * Gets the transposed version of the current modelView matrix. 1394 * @returns {Matrix4} The current modelView matrix transposed. 1395 */ 1396 TransformStack.prototype.getModelViewMatrixTranspose = function() { 1397 return this.getModelViewMatrix().transpose(); 1398 }; 1399 1400 /** 1401 * Gets the inverse of the current modelView matrix. 1402 * @returns {Matrix4} The current modelView matrix inverted. 1403 */ 1404 TransformStack.prototype.getModelViewMatrixInverse = function() { 1405 return this.getModelViewMatrix().invert(); 1406 }; 1407 1408 /** 1409 * Gets the inverse of the transposed of the current modelView matrix. 1410 * @returns {Matrix4} The current modelView matrix inverted transposed. 1411 */ 1412 TransformStack.prototype.getModelViewMatrixInverseTranspose = function() { 1413 return this.getModelViewMatrix().invert().$transpose(); 1414 }; 1415 1416 /** 1417 * Gets the normal matrix (aka the inverse transpose of the modelView matrix). 1418 * @returns {Matrix4} The current modelView matrix inverted transposed. 1419 */ 1420 TransformStack.prototype.getNormalMatrix = function() { 1421 return this.getModelViewMatrixInverseTranspose(); 1422 }; 1423 1424 /** 1425 * Sets the current stack as the projection stack. 1426 * @returns {TransformStack} Returns this TransformStack. 1427 */ 1428 TransformStack.prototype.projection = function() { 1429 this.currentStack = this.projStack; 1430 return this; 1431 }; 1432 1433 /** 1434 * Sets the current stack as the view stack. 1435 * @returns {TransformStack} Returns this TransformStack. 1436 */ 1437 TransformStack.prototype.view = function() { 1438 this.currentStack = this.viewStack; 1439 return this; 1440 }; 1441 1442 /** 1443 * Sets the current stack as the model stack. 1444 * @returns {TransformStack} Returns this TransformStack. 1445 */ 1446 TransformStack.prototype.model = function() { 1447 this.currentStack = this.modelStack; 1448 return this; 1449 }; 1450 1451 /** 1452 * Push the matrix on top of the current stack. 1453 * @returns {TransformStack} Returns this TransformStack. 1454 */ 1455 TransformStack.prototype.pushMatrix = function() { 1456 this.currentStack.push(); 1457 return this; 1458 }; 1459 1460 /** 1461 * Pops out the matrix on top of the current stack. 1462 * @returns {TransformStack} Returns this TransformStack. 1463 */ 1464 TransformStack.prototype.popMatrix = function() { 1465 this.currentStack.pop(); 1466 return this; 1467 }; 1468 1469 /** 1470 * Loads the identity on the current stack. 1471 * @returns {TransformStack} Returns this TransformStack. 1472 */ 1473 TransformStack.prototype.loadIdentity = function() { 1474 this.currentStack.loadIdentity(); 1475 return this; 1476 }; 1477 1478 /** 1479 * Multiply a matrix with the current stack. 1480 * @param {Matrix4} The matrix to multiply with. 1481 * @returns {TransformStack} Returns this TransformStack. 1482 */ 1483 TransformStack.prototype.multiply = function(matrix) { 1484 this.currentStack.multiply(matrix); 1485 return this; 1486 }; 1487 1488 /** 1489 * Apply a perspective transformation to the current stack. 1490 * @param {Number} fovy The vertical field of view angle. 1491 * @param {Number} aspect The aspect ratio aka the horizontal field of view angle. 1492 * @param {Number} near The near clipping plane. 1493 * @param {Number} far The far clipping plane. 1494 * @returns {TransformStack} Returns this TransformStack. 1495 */ 1496 TransformStack.prototype.perspective = function(fovy, aspect, near, far) { 1497 this.currentStack.perspective(fovy, aspect, near, far); 1498 return this; 1499 }; 1500 1501 /** 1502 * Apply a lookAt transformation to the current stack. 1503 * @param {Vector3} eye The eye vector. 1504 * @param {Vector3} direction The direction vector. 1505 * @param {Vector3} up The up vector. 1506 * @returns {TransformStack} Returns this TransformStack. 1507 */ 1508 TransformStack.prototype.lookAt = function(eye, direction, up) { 1509 this.currentStack.lookAt(eye, direction, up); 1510 return this; 1511 }; 1512 1513 /** 1514 * Apply a rotation to the current stack. 1515 * @param {Number} angle The rotation angle. 1516 * @param {Number} x The rotation axis x coordinate. 1517 * @param {Number} y The rotation axis y coordinate. 1518 * @param {Number} z The rotation axis z coordinate. 1519 * @returns {TransformStack} Returns this TransformStack. 1520 */ 1521 TransformStack.prototype.rotate = function(angle, x, y, z) { 1522 this.currentStack.rotate(angle, x, y, z); 1523 return this; 1524 }; 1525 1526 /** 1527 * Apply a rotation around x, y and z axes to the current stack. 1528 * @param {Number} rx The rotation angle around X axis. 1529 * @param {Number} ry The rotation angle around Y axis. 1530 * @param {Number} rz The rotation angle around Z axis. 1531 * @returns {TransformStack} Returns this TransformStack. 1532 */ 1533 TransformStack.prototype.rotateXYZ = function(rx, ry, rz) { 1534 this.currentStack.rotateXYZ(rx, ry, rz); 1535 return this; 1536 }; 1537 1538 /** 1539 * Apply a translation to the current stack. 1540 * @param {Number} x The x translation factor. 1541 * @param {Number} y The y translation factor. 1542 * @param {Number} z The z translation factor. 1543 * @returns {TransformStack} Returns this TransformStack. 1544 */ 1545 TransformStack.prototype.translate = function(x, y, z) { 1546 this.currentStack.translate(x, y, z); 1547 return this; 1548 }; 1549 1550 /** 1551 * Apply a scale transformation to the current stack. 1552 * @param {Number} x The x scale factor. 1553 * @param {Number} y The y scale factor. 1554 * @param {Number} z The z scale factor. 1555 * @returns {TransformStack} Returns this TransformStack. 1556 */ 1557 TransformStack.prototype.scale = function(x, y, z) { 1558 this.currentStack.scale(x, y, z); 1559 return this; 1560 }; 1561 1562 return TransformStack; 1563 1564 }()); 1565 1566 // skin.js 1567 // The skin module has the tools to manipulate colors, materials, lights and textures. 1568 1569 BenchGL.namespace('BenchGL.skin.Color'); 1570 1571 BenchGL.skin.Color = (function() { 1572 1573 // Dependencies 1574 var Vec3 = BenchGL.math.Vector3, 1575 1576 // Private properties and methods 1577 Color; 1578 1579 /** 1580 * Creates a new Color. 1581 * @class Represents an RGB(A) color. 1582 * @param {Number} r The red component of the color. 1583 * @param {Number} g The green component of the color. 1584 * @param {Number} b The blue component of the color. 1585 * @param {Number} [a=1] The alpha component of the color. 1586 */ 1587 Color = function(r, g, b, a) { 1588 this.r = r; 1589 this.g = g; 1590 this.b = b; 1591 this.a = a || 1; 1592 }; 1593 1594 /** 1595 * Gets an array representaton of this Color in RGB format. 1596 * @returns {Number[]} An array representing an RGB color. 1597 */ 1598 Color.prototype.toRGBArray = function() { 1599 return [this.r, this.g, this.b]; 1600 }; 1601 1602 /** 1603 * Gets an array representaton of this Color in RGBA format. 1604 * @returns {Number[]} An array representing an RGBA color. 1605 */ 1606 Color.prototype.toRGBAArray = function(){ 1607 return [this.r, this.g, this.b, this.a]; 1608 }; 1609 1610 return Color; 1611 1612 }()); 1613 1614 BenchGL.namespace('BenchGL.skin.Material'); 1615 1616 BenchGL.skin.Material = (function() { 1617 1618 // Dependencies 1619 var Color = BenchGL.skin.Color, 1620 1621 // Private properties and methods 1622 Material; 1623 1624 /** 1625 * Creates a new Material. 1626 * @class Represents a material, thus a way to express 1627 * interaction between light and geometric models. 1628 * @param {Object} [options] Contains parameters to create the material. 1629 * @param {Object} [options.ambient] Holds the ambient color. 1630 * @param {Object} [options.diffuse] Holds the diffuse color. 1631 * @param {Object} [options.specular] Holds the specular color. 1632 * @param {Object} [options.emissive] Holds the emissive color. 1633 * @param {Number} [options.shininess] Specifies the shininess of the material. 1634 */ 1635 Material = function(options) { 1636 options = $.mix({ 1637 ambient: { 1638 r: 0.0, 1639 g: 0.0, 1640 b: 0.0 1641 }, 1642 diffuse: { 1643 r: 1.0, 1644 g: 1.0, 1645 b: 1.0 1646 }, 1647 specular: { 1648 r: 1.0, 1649 g: 1.0, 1650 b: 1.0 1651 }, 1652 emissive: { 1653 r: 1.0, 1654 g: 1.0, 1655 b: 1.0 1656 }, 1657 shininess: 0.1 1658 }, options || {}); 1659 1660 var ambient = options.ambient, 1661 diffuse = options.diffuse, 1662 specular = options.specular, 1663 emissive = options.emissive; 1664 1665 this.ambient = new Color(ambient.r, ambient.g, ambient.b); 1666 this.diffuse = new Color(diffuse.r, diffuse.g, diffuse.b); 1667 this.specular = new Color(specular.r, specular.g, specular.b); 1668 this.emissive = new Color(emissive.r, emissive.g, emissive.b); 1669 this.shininess = options.shininess; 1670 }; 1671 1672 /** 1673 * Sets the ambient color of this Material. 1674 * @param {Number} r The red component of the color. 1675 * @param {Number} g The green component of the color. 1676 * @param {Number} b The blue component of the color. 1677 * @returns {Material} Returns this Material. 1678 */ 1679 Material.prototype.setAmbient = function(r, g, b) { 1680 this.ambient = new Color(r, g, b); 1681 return this; 1682 }; 1683 1684 /** 1685 * Sets the diffuse color of this Material. 1686 * @param {Number} r The red component of the color. 1687 * @param {Number} g The green component of the color. 1688 * @param {Number} b The blue component of the color. 1689 * @returns {Material} Returns this Material. 1690 */ 1691 Material.prototype.setDiffuse = function(r, g, b) { 1692 this.diffuse = new Color(r, g, b); 1693 return this; 1694 }; 1695 1696 /** 1697 * Sets the specular color of this Material. 1698 * @param {Number} r The red component of the color. 1699 * @param {Number} g The green component of the color. 1700 * @param {Number} b The blue component of the color. 1701 * @returns {Material} Returns this Material. 1702 */ 1703 Material.prototype.setSpecular = function(r, g, b) { 1704 this.specular = new Color(r, g, b); 1705 return this; 1706 }; 1707 1708 /** 1709 * Sets the emissive color of this Material. 1710 * @param {Number} r The red component of the color. 1711 * @param {Number} g The green component of the color. 1712 * @param {Number} b The blue component of the color. 1713 * @returns {Material} Returns this Material. 1714 */ 1715 Material.prototype.setEmissive = function(r, g, b) { 1716 this.emissive = new Color(r, g, b); 1717 return this; 1718 }; 1719 1720 /** 1721 * Sets the shininess of this Material. 1722 * @param {Number} shininess The shininess to set. 1723 * @returns {Material} Returns this Material. 1724 */ 1725 Material.prototype.setShininess = function(shininess) { 1726 this.shininess = shininess; 1727 return this; 1728 }; 1729 1730 return Material; 1731 1732 }()); 1733 1734 BenchGL.namespace('BenchGL.skin.Light'); 1735 1736 BenchGL.skin.Light = (function() { 1737 1738 // Dependencies 1739 var Vec3 = BenchGL.math.Vector3, 1740 Color = BenchGL.skin.Color, 1741 Material = BenchGL.skin.Material, 1742 1743 // Private properties and methods 1744 Light; 1745 1746 /** 1747 * Creates a new Light. 1748 * @class Represent a light in a 3D world. 1749 * @param {Object} [options] Contains parameters to create the light. 1750 * @param {Object} [options.position] Holds the position of the light. 1751 * @param {Object} [options.diffuse] Holds the diffuse color. 1752 * @param {Object} [options.specular] Holds the specular color. 1753 * @param {Object} [options.direction] Holds the direction of the light. 1754 * @param {Number} [options.cutoff] Holds the cutoff factor. 1755 * @param {Number} [options.exponent] Holds the cutoff factor. 1756 * @param {Number} [options.constant] Holds the costant attenuation factor. 1757 * @param {Number} [options.linear] Holds the linear attenuation factor. 1758 * @param {Number} [options.quadratic] Holds the quadratic attenuation factor. 1759 * @param {Number} [options.active] Activate the light. 1760 */ 1761 Light = function(options) { 1762 options = $.mix({ 1763 position: { 1764 x: 0.0, 1765 y: 0.0, 1766 z: -1.0 1767 }, 1768 ambient: { 1769 r: 0.0, 1770 g: 0.0, 1771 b: 0.0 1772 }, 1773 diffuse: { 1774 r: 1.0, 1775 g: 1.0, 1776 b: 1.0 1777 }, 1778 specular: { 1779 r: 1.0, 1780 g: 1.0, 1781 b: 1.0 1782 }, 1783 direction: { 1784 x: 0.0, 1785 y: 0.0, 1786 z: -1.0 1787 }, 1788 cutoff: 180.0, 1789 exponent: 0.0, 1790 constant: 1.0, 1791 linear: 0.0, 1792 quadratic: 0.0, 1793 active: true 1794 }, options || {}); 1795 1796 var position = options.position, 1797 ambient = options.ambient, 1798 diffuse = options.diffuse, 1799 specular = options.specular, 1800 direction = options.direction; 1801 1802 this.position = new Vec3(position.x, position.y, position.z); 1803 this.ambient = new Color(ambient.r, ambient.g, ambient.b); 1804 this.diffuse = new Color(diffuse.r, diffuse.g, diffuse.b); 1805 this.specular = new Color(specular.r, specular.g, specular.b); 1806 this.direction = new Color(direction.x, direction.y, direction.z); 1807 this.cutOff = options.cutoff; 1808 this.exponent = options.exponent; 1809 this.constant = options.constant; 1810 this.linear = options.linear; 1811 this.quadratic = options.quadratic; 1812 this.active = options.active; 1813 }; 1814 1815 /** 1816 * Sets the position of this Light. 1817 * @param {Number} x The x coordinate to set. 1818 * @param {Number} y The y coordinate to set. 1819 * @param {Number} z The z coordinate to set. 1820 * @returns {Light} Returns this Light. 1821 */ 1822 Light.prototype.setPosition = function(x, y, z) { 1823 this.position = new Vec3(x, y, z); 1824 return this; 1825 }; 1826 1827 /** 1828 * Sets the ambient color of this Light. 1829 * @param {Number} r The red component of the color. 1830 * @param {Number} g The green component of the color. 1831 * @param {Number} b The blue component of the color. 1832 * @returns {Light} Returns this Light. 1833 */ 1834 Light.prototype.setAmbient = function(r, g, b) { 1835 this.ambient = new Color(r, g, b); 1836 return this; 1837 }; 1838 1839 /** 1840 * Sets the diffuse color of this Light. 1841 * @param {Number} r The red component of the color. 1842 * @param {Number} g The green component of the color. 1843 * @param {Number} b The blue component of the color. 1844 * @returns {Light} Returns this Light. 1845 */ 1846 Light.prototype.setDiffuse = function(r, g, b) { 1847 this.diffuse = new Color(r, g, b); 1848 return this; 1849 }; 1850 1851 /** 1852 * Sets the specular color of this Light. 1853 * @param {Number} r The red component of the color. 1854 * @param {Number} g The green component of the color. 1855 * @param {Number} b The blue component of the color. 1856 * @returns {Light} Returns this Light. 1857 */ 1858 Light.prototype.setSpecular = function(r, g, b) { 1859 this.specular = new Color(r, g, b); 1860 return this; 1861 }; 1862 1863 /** 1864 * Sets the direction of this Light. 1865 * @param {Number} x The x coordinate to set. 1866 * @param {Number} y The y coordinate to set. 1867 * @param {Number} z The z coordinate to set. 1868 * @returns {Light} Returns this Light. 1869 */ 1870 Light.prototype.setDirection = function(x, y, z) { 1871 this.direction = new Vec3(x, y, z).$unit(); 1872 return this; 1873 }; 1874 1875 /** 1876 * Sets the exponent of this Light's intensity. 1877 * @param {Number} exponent The exponent to set. 1878 * @returns {Light} Returns this Light. 1879 */ 1880 Light.prototype.setExponent = function(exponent) { 1881 this.exponent = exponent; 1882 return this; 1883 }; 1884 1885 /** 1886 * Sets the cutoff of this Light. 1887 * @param {Number} cutoff The cutoff to set. 1888 * @returns {Light} Returns this Light. 1889 */ 1890 Light.prototype.setCutoff = function(cutoff) { 1891 this.cutoff = cutoff; 1892 return this; 1893 }; 1894 1895 /** 1896 * Sets the constant attenuation factor of this Light. 1897 * @param {Number} constant The attenuation factor to set. 1898 * @returns {Light} Returns this Light. 1899 */ 1900 Light.prototype.setConstantAttenuation = function(constant) { 1901 this.constant = constant; 1902 return this; 1903 }; 1904 1905 /** 1906 * Sets the linear attenuation factor of this Light. 1907 * @param {Number} linear The attenuation factor to set. 1908 * @returns {Light} Returns this Light. 1909 */ 1910 Light.prototype.setLinearAttenuation = function(linear) { 1911 this.linear = linear; 1912 return this; 1913 }; 1914 1915 /** 1916 * Sets the quadratic attenuation factor of this Light. 1917 * @param {Number} quadratic The attenuation factor to set. 1918 * @returns {Light} Returns this Light. 1919 */ 1920 Light.prototype.setQuadraticAttenuation = function(quadratic) { 1921 this.quadratic = quadratic; 1922 return this; 1923 }; 1924 1925 /** 1926 * Switches the light on and off. 1927 * @param {Boolean} active True to activate this Light, false otherwise. 1928 * @returns {Light} Returns this Light. 1929 */ 1930 Light.prototype.setActive = function(active) { 1931 this.active = active; 1932 return this; 1933 }; 1934 1935 return Light; 1936 1937 }()); 1938 1939 BenchGL.namespace('BenchGL.skin.Texture'); 1940 1941 BenchGL.skin.Texture = (function() { 1942 1943 // Private properties and methods 1944 var Texture; 1945 1946 /** 1947 * Creates a new Texture. 1948 * @class Represent a texture. 1949 * @param {Object} options Contains some info to generate the texture. 1950 * @param {Image} options.image The image to generate the texture from. 1951 * @param {Number} [options.level] The level of the texture. 1952 * @param {Number} [options.verticalFlip] Indicates to flip the y coordinate over the texture. 1953 * @param {Number} [options.internalFmt] The WebGL internal format of the texture. 1954 * @param {Number} [options.format] The format of the texture. 1955 * @param {Number} [options.type] The data type of the colors for each pixel in the image. 1956 * @param {Number} [options.magFilter] Specifies magnification filters to use in mipmapping. 1957 * @param {Number} [options.minFilter] Specifies minification filters to use in mipmapping. 1958 * @param {Number} [options.target] The WebGL target for the texture. 1959 * @param {Boolean} [options.mipmap] Indicates if mipmap of the texture have to be created. 1960 */ 1961 Texture = function(options) { 1962 options = $.mix({ 1963 level: 0, 1964 verticalFlip: true, 1965 internalFmt: gl.RGBA, 1966 format: gl.RGBA, 1967 type: gl.UNSIGNED_BYTE, 1968 magFilter: gl.LINEAR, 1969 minFilter: gl.LINEAR_MIPMAP_NEAREST, 1970 mipmap: true, 1971 target: gl.TEXTURE_2D 1972 }, options || {}); 1973 1974 var texture = gl.createTexture(); 1975 1976 this.options = options; 1977 this.handler = texture; 1978 1979 gl.bindTexture(options.target, texture); 1980 gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, options.verticalFlip); 1981 gl.texImage2D(options.target, options.level, options.internalFmt, options.format, options.type, options.image); 1982 gl.texParameteri(options.target, gl.TEXTURE_MAG_FILTER, options.magFilter); 1983 gl.texParameteri(options.target, gl.TEXTURE_MIN_FILTER, options.minFilter); 1984 if (options.mipmap) { 1985 this.generateMipmap(); 1986 } 1987 gl.bindTexture(options.target, null); 1988 }; 1989 1990 /** 1991 * Destroys this Texture. 1992 */ 1993 Texture.prototype.destroy = function() { 1994 gl.deleteTexture(this.handler); 1995 }; 1996 1997 /** 1998 * Binds this Texture to a sampler unit in the current shader program. 1999 * @param {Number} unit The unit to bind this Texture to. 2000 * @returns {Texture} Returns this Texture. 2001 */ 2002 Texture.prototype.bind = function(unit) { 2003 gl.activeTexture(gl.TEXTURE0 + unit); 2004 gl.bindTexture(this.options.target, this.handler); 2005 return this; 2006 }; 2007 2008 /** 2009 * Unbinds this Texture from a sampler unit in the current shader program. 2010 * @param {Number} unit The unit to unbind this Texture from. 2011 * @returns {Texture} Returns this Texture. 2012 */ 2013 Texture.prototype.unbind = function(unit) { 2014 gl.activeTexture(gl.TEXTURE0 + unit); 2015 gl.bindTexture(this.options.target, null); 2016 return this; 2017 }; 2018 2019 /** 2020 * Generates mipmaps for this Texture. 2021 * @returns {Texture} Returns this Texture. 2022 */ 2023 Texture.prototype.generateMipmap = function() { 2024 gl.generateMipmap(this.options.target); 2025 return this; 2026 }; 2027 2028 return Texture; 2029 2030 }()); 2031 2032 // io.js 2033 // Offers structures and functions to perform asynchronous IO operations. 2034 2035 BenchGL.namespace('BenchGL.io.XHRequest'); 2036 2037 BenchGL.io.XHRequest = (function() { 2038 2039 var XHRequest; 2040 2041 /** 2042 * Creates a new XHRequest. 2043 * @class Wraps an XMLHttpRequest object to load resources asynchronously. 2044 * @param {Object} [options] The request's options. 2045 * @param {String} [options.url] The url for the request. 2046 * @param {String} [options.method] The method for the request. 2047 * @param {Boolean} [options.async] Is the request asynchronous? 2048 * @param {Boolean} [options.binary] Is the response in binary format? 2049 * @param {Function} [options.onProgress] Callback to call during request processing. 2050 * @param {Function} [options.onLoad] Callback to call after request loading. 2051 * @param {Function} [options.onError] Callback to call in case of error. 2052 * @param {Function} [options.onAbort] Callback to call if the request is aborted. 2053 * @param {Function} [options.onSuccess] Callback to call in case of success. 2054 */ 2055 XHRequest = function(options) { 2056 options = $.mix({ 2057 url: 'www.webrendering.sourceforge.net', 2058 method: 'GET', 2059 async: true, 2060 binary: false, 2061 onProgress: $.empty, 2062 onLoad: $.empty, 2063 onError: $.empty, 2064 onAbort: $.empty, 2065 onSuccess: $.empty 2066 }, options || {}); 2067 2068 var myself = this; 2069 2070 this.options = options; 2071 this.request = new XMLHttpRequest(); 2072 2073 this.request.addEventListener('progress', function(e) { myself.onProgress(e); }, false); 2074 this.request.addEventListener('load', function(e) { myself.onLoad(e); }, false); 2075 this.request.addEventListener('error', function(e) { myself.onError(e); }, false); 2076 this.request.addEventListener('abort', function(e) { myself.onAbort(e); }, false); 2077 }; 2078 2079 /** 2080 * Executes the request wrapped in this XHRequest. 2081 */ 2082 XHRequest.prototype.send = function() { 2083 var options = this.options, 2084 url = this.options.url, 2085 method = this.options.method, 2086 async = this.options.async, 2087 binary = this.options.binary, 2088 request = this.request; 2089 2090 // Opens the request 2091 request.open(method, url, async); 2092 2093 // Handle async requests 2094 if (async) { 2095 request.onreadystatechange = function(e) { 2096 if (request.readyState === 4) { 2097 if (request.status === 200) { 2098 options.onSuccess(request.responseText); 2099 } 2100 else { 2101 options.onError(request.status); 2102 } 2103 } 2104 }; 2105 } 2106 2107 // Handles binary requests 2108 if (binary) { 2109 request.sendAsBinary(null); 2110 } 2111 else { 2112 request.send(null); 2113 } 2114 2115 // If not async wait for the response 2116 if (!async) { 2117 if (request.status === 200) { 2118 options.onSuccess(request.responseText); 2119 } 2120 else { 2121 options.onError(request.status); 2122 } 2123 } 2124 }; 2125 2126 /** 2127 * Handles the 'onprogress' event of this XHRequest. 2128 */ 2129 XHRequest.prototype.onProgress = function(e) { 2130 this.options.onProgress(e); 2131 }; 2132 2133 /** 2134 * Handles the 'onerror' event of this XHRequest. 2135 */ 2136 XHRequest.prototype.onError = function(e) { 2137 this.options.onError(e); 2138 }; 2139 2140 /** 2141 * Handles the 'onabort' event of this XHRequest. 2142 */ 2143 XHRequest.prototype.onAbort = function(e) { 2144 this.options.onAbort(e); 2145 }; 2146 2147 /** 2148 * Handles the 'onload' event of this XHRequest. 2149 */ 2150 XHRequest.prototype.onLoad = function(e) { 2151 this.options.onLoad(e); 2152 }; 2153 2154 return XHRequest; 2155 2156 }()); 2157 2158 BenchGL.namespace('BenchGL.io.TextureRequest'); 2159 2160 BenchGL.io.TextureRequest = (function() { 2161 2162 var TextureRequest; 2163 2164 /** 2165 * Creates a new TextureRequest. 2166 * @class Represents multiple asynchronous requests for images to build up Texture objects. 2167 * @param {Object} options Information about the requested textures. 2168 */ 2169 TextureRequest = function(options) { 2170 this.texturesReqs = options; 2171 }; 2172 2173 /** 2174 * Executes all the request of this TextureRequest, 2175 * using a callback function to handle each one of them on completion. 2176 * @param {Function} callback A callback function to handle results on completion. 2177 */ 2178 TextureRequest.prototype.send = function(callback) { 2179 var texturesReqs = this.texturesReqs, 2180 keys = Object.keys(texturesReqs); 2181 2182 keys.map(function(key) { 2183 var textureOpt = texturesReqs[key]; 2184 textureOpt.image = new Image(); 2185 textureOpt.image.onload = function() { 2186 if (callback) { 2187 callback(key, textureOpt); 2188 } 2189 }; 2190 textureOpt.image.src = textureOpt.src; 2191 }); 2192 }; 2193 2194 return TextureRequest; 2195 2196 }()); 2197 2198 // ui.js 2199 // The ui module handles user interaction and events. 2200 2201 BenchGL.namespace('BenchGL.ui.Canvas'); 2202 2203 BenchGL.ui.Canvas = (function() { 2204 2205 var Canvas; 2206 2207 /** 2208 * Creates a new Canvas. 2209 * @class Represents a wrap object for a canvas HTML5 element. 2210 * @param {HTMLCanvasElement} canvas The canvas element. 2211 * @options {Object} options Contains callbacks to handle events in the browser. 2212 */ 2213 Canvas = function(canvas, options) { 2214 options = $.mix({ 2215 onKeyDown: $.empty, 2216 onKeyUp: $.empty, 2217 onMouseDown: $.empty, 2218 onMouseUp: $.empty, 2219 onMouseMove: $.empty, 2220 onMouseWheel: $.empty, 2221 onMouseOut: $.empty 2222 }, options || {}); 2223 2224 //canvas.contentEditable = true; 2225 2226 this.canvas = canvas; 2227 this.events = options; 2228 this.keysDown = {}; 2229 this.mouseDown = {}; 2230 this.mousePosition = { 2231 x: 0.0, 2232 y: 0.0 2233 }; 2234 this.mouseLastPosition = { 2235 x: 0.0, 2236 y: 0.0 2237 }; 2238 this.mouseDelta = { 2239 x: 0.0, 2240 y: 0.0 2241 }; 2242 2243 var myself = this; 2244 document.addEventListener('keydown', function(e) { myself.onKeyDown(e); }, false); 2245 document.addEventListener('keyup', function(e) { myself.onKeyUp(e); }, false); 2246 canvas.addEventListener('mousedown', function(e) { myself.onMouseDown(e); }, false); 2247 canvas.addEventListener('mouseup', function(e) { myself.onMouseUp(e); }, false); 2248 canvas.addEventListener('mousemove', function(e) { myself.onMouseMove(e); }, false); 2249 canvas.addEventListener('mousewheel', function(e) { myself.onMouseWheel(e); }, false); 2250 canvas.addEventListener('DOMMouseScroll', function(e) { myself.onMouseWheel(e); }, false); 2251 }; 2252 2253 /** 2254 * Handles the 'keydown' event, if supplied. 2255 * @param {Event} e Information about the event occured. 2256 */ 2257 Canvas.prototype.onKeyDown = function(e){ 2258 this.keysDown[e.keyCode] = true; 2259 this.events.onKeyDown(e); 2260 }; 2261 2262 /** 2263 * Handles the 'keyup' event, if supplied. 2264 * @param {Event} e Information about the event occured. 2265 */ 2266 Canvas.prototype.onKeyUp = function(e){ 2267 this.keysDown[e.keyCode] = false; 2268 this.events.onKeyUp(e); 2269 }; 2270 2271 /** 2272 * Handles the 'mousedown' event, if supplied. 2273 * @param {Event} e Information about the event occured. 2274 */ 2275 Canvas.prototype.onMouseDown = function(e){ 2276 var x = e.clientX, y = this.canvas.height - e.clientY - 1; 2277 2278 this.mousePosition.x = x; 2279 this.mousePosition.y = y; 2280 this.mouseLastPosition.x = x; 2281 this.mouseLastPosition.y = y; 2282 this.mouseDelta.x = 0.0; 2283 this.mouseDelta.y = 0.0; 2284 this.mouseDown[e.button] = true; 2285 2286 this.events.onMouseDown(e, x, y); 2287 }; 2288 2289 /** 2290 * Handles the 'mouseup' event, if supplied. 2291 * @param {Event} e Information about the event occured. 2292 */ 2293 Canvas.prototype.onMouseUp = function(e){ 2294 var x = e.clientX, y = this.canvas.height - e.clientY - 1; 2295 2296 this.mousePosition.x = x; 2297 this.mousePosition.y = y; 2298 this.mouseLastPosition.x = x; 2299 this.mouseLastPosition.y = y; 2300 this.mouseDelta.x = 0.0; 2301 this.mouseDelta.y = 0.0; 2302 this.mouseDown[e.button] = false; 2303 2304 this.events.onMouseUp(e, x, y); 2305 }; 2306 2307 /** 2308 * Handles the 'mousemove' event, if supplied. 2309 * @param {Event} e Information about the event occured. 2310 */ 2311 Canvas.prototype.onMouseMove = function(e){ 2312 var x = e.clientX, y = this.canvas.height - e.clientY - 1; 2313 2314 this.mouseLastPosition.x = this.mousePosition.x; 2315 this.mouseLastPosition.y = this.mousePosition.y; 2316 this.mousePosition.x = x; 2317 this.mousePosition.y = y; 2318 this.mouseDelta.x = this.mousePosition.x - this.mouseLastPosition.x; 2319 this.mouseDelta.y = this.mousePosition.y - this.mouseLastPosition.y; 2320 2321 this.events.onMouseMove(e, this.mouseDelta.x, this.mouseDelta.y); 2322 }; 2323 2324 /** 2325 * Handles the 'mousewheel' event, if supplied. 2326 * @param {Event} e Information about the event occured. 2327 */ 2328 Canvas.prototype.onMouseWheel = function(e) { 2329 var x = e.clientX, y = this.canvas.height - e.clientY - 1, delta = 0; 2330 2331 this.mouseLastPosition.x = this.mousePosition.x; 2332 this.mouseLastPosition.y = this.mousePosition.y; 2333 this.mousePosition.x = x; 2334 this.mousePosition.y = y; 2335 this.mouseDelta.x = 0; 2336 this.mouseDelta.y = 0; 2337 2338 if (!e) { 2339 e = window.event; 2340 } 2341 if (e.wheelDelta) { 2342 delta = e.wheelDelta / 120; 2343 if (window.opera) { 2344 delta = -delta; 2345 } 2346 } 2347 else if (e.detail){ 2348 delta = -e.detail / 3; 2349 } 2350 2351 if (delta) { 2352 this.events.onMouseWheel(e, delta); 2353 } 2354 }; 2355 2356 return Canvas; 2357 2358 }()); 2359 2360 BenchGL.namespace('BenchGL.ui.Camera'); 2361 2362 BenchGL.ui.Camera = (function() { 2363 2364 // Dependencies 2365 var Vec3 = BenchGL.math.Vector3, 2366 MatStack = BenchGL.math.MatrixStack, 2367 2368 // Private properties and methods 2369 Camera; 2370 2371 /** 2372 * Creates a new Camera. 2373 * @class Represents a camera with a point of view over a 3D scene. 2374 * @param {Object} options The options to set up this Camera. 2375 * @param {Number} options.fovy The field of view vertical angle. 2376 * @param {Number} options.aspect The aspect ratio. 2377 * @param {Number} options.near The near clipping plane. 2378 * @param {Number} options.far The far clipping plane. 2379 * @param {Number[]} [options.eye] The position vector of this Camera. 2380 * @param {Number[]} [options.direction] The viewing direction vector of this Camera. 2381 * @param {Number[]} [options.up] The up vector of this Camera. 2382 */ 2383 Camera = function(options) { 2384 var e = options.eye, 2385 d = options.direction, 2386 u = options.up, 2387 fovy = options.fovy, 2388 aspect = options.aspect, 2389 near = options.near, 2390 far = options.far; 2391 2392 this.fovy = fovy; 2393 this.aspect = aspect; 2394 this.near = near; 2395 this.far = far; 2396 this.eye = (e && new Vec3(e.x, e.y, e.z)) || new Vec3(0, 0, 0); 2397 this.direction = (d && new Vec3(d.x, d.y, d.z)) || new Vec3(0, 0, -1); 2398 this.up = (u && new Vec3(u.x, u.y, u.z)) || new Vec3(0, 1, 0); 2399 2400 this.projStack = new MatStack(); 2401 this.viewStack = new MatStack(); 2402 this.modelStack = new MatStack(); 2403 2404 this.viewStack.lookAt(this.eye, this.direction, this.up); 2405 this.projStack.perspective(fovy, aspect, near, far); 2406 }; 2407 2408 /** 2409 * Gets this Camera's projection stack. 2410 * @returns {MatrixStack} A projection matrix stack. 2411 */ 2412 Camera.prototype.proj = function() { 2413 return this.projstack; 2414 }; 2415 2416 /** 2417 * Gets this Camera's view stack. 2418 * @returns {MatrixStack} A view matrix stack 2419 */ 2420 Camera.prototype.view = function() { 2421 return this.viewStack; 2422 }; 2423 2424 /** 2425 * Gets this Camera's model stack. 2426 * @returns {MatrixStack} A model matrix stack 2427 */ 2428 Camera.prototype.model = function() { 2429 return this.modelStack; 2430 }; 2431 2432 /** 2433 * Gets the projection matrix of this Camera. 2434 * @returns {Matrix4} A matrix representing a projective transformation. 2435 */ 2436 Camera.prototype.projMatrix = function() { 2437 return this.projStack.top(); 2438 }; 2439 2440 /** 2441 * Gets the view matrix of this Camera. 2442 * @returns {Matrix4} A matrix representing a transformation from world to camera space. 2443 */ 2444 Camera.prototype.viewMatrix = function() { 2445 return this.viewStack.top(); 2446 }; 2447 2448 /** 2449 * Gets the model matrix of this Camera. 2450 * @returns {Matrix4} A matrix representing a common transformation to apply to the scene. 2451 */ 2452 Camera.prototype.modelMatrix = function() { 2453 return this.modelStack.top(); 2454 }; 2455 2456 /** 2457 * Gets the modelView matrix of this Camera. 2458 * @returns {Matrix4} A matrix representing the full tranformation from object to camera space. 2459 */ 2460 Camera.prototype.modelViewMatrix = function() { 2461 return this.viewStack.top().multiplyMat4(this.modelStack.top()); 2462 }; 2463 2464 /** 2465 * Resets this Camera, loading identity matrices on top of the view and model stacks. 2466 */ 2467 Camera.prototype.reset = function() { 2468 this.viewStack.loadIdentity(); 2469 this.modelStack.loadIdentity(); 2470 }; 2471 2472 /** 2473 * Updates this Camera's local reference frame. 2474 */ 2475 Camera.prototype.update = function() { 2476 this.viewStack.lookAt(this.eye, this.direction, this.up); 2477 }; 2478 2479 return Camera; 2480 2481 }()); 2482 2483 BenchGL.namespace('BenchGL.ui.Logger'); 2484 2485 BenchGL.ui.Logger = (function() { 2486 2487 // Private properties and methods 2488 var instance, 2489 Logger; 2490 2491 /** 2492 * Gets an instance of a Logger. 2493 * @class Helps logging stuff. 2494 */ 2495 Logger = function Logger() { 2496 if (instance) { 2497 return instance; 2498 } 2499 instance = this; 2500 }; 2501 2502 /** 2503 * Logs a message to console. 2504 * @param {Object} message The message to log. 2505 */ 2506 Logger.prototype.log = function(message) { 2507 console.log(message); 2508 }; 2509 2510 return Logger; 2511 2512 }()); 2513 2514 BenchGL.namespace('BenchGL.ui.Timer'); 2515 2516 BenchGL.ui.Timer = (function() { 2517 2518 // Private properties and methods 2519 var nowTime = 0, 2520 lastTime = 0, 2521 elapsedTime = 0, 2522 Timer; 2523 2524 /** 2525 * Creates a new Timer. 2526 * @class A class for timing purpouses, like calculating FPS for an application. 2527 */ 2528 Timer = function() { 2529 this.fps = 0; 2530 this.lastDelta = 0; 2531 this.maxSamples = 50; 2532 this.samples = []; 2533 }; 2534 2535 /** 2536 * Starts this Timer. 2537 */ 2538 Timer.prototype.start = function() { 2539 nowTime = new Date().getTime(); 2540 lastTime = nowTime; 2541 elapsedTime = 0; 2542 return this; 2543 }; 2544 2545 /** 2546 * Stops this Timer. 2547 */ 2548 Timer.prototype.stop = function() { 2549 var now = new Date().getTime(), 2550 sample, i, l, fps = 0; 2551 2552 lastTime = nowTime; 2553 nowTime = now; 2554 elapsedTime = nowTime - lastTime; 2555 sample = 1000.0 / elapsedTime; 2556 2557 if (this.samples.unshift(sample) > this.maxSamples) { 2558 this.samples.pop(); 2559 } 2560 2561 for (i = 0, l = this.samples.length; i < l; i++) { 2562 fps += this.samples[i]; 2563 } 2564 fps /= this.samples.length; 2565 2566 this.fps = Math.round(fps); 2567 this.lastDelta = elapsedTime; 2568 return this; 2569 }; 2570 2571 /** 2572 * Clears this Timer. 2573 */ 2574 Timer.prototype.clear = function() { 2575 nowTime = 0; 2576 lastTime = 0; 2577 elapsedTime = 0; 2578 2579 this.fps = 0; 2580 this.lastDelta = 0; 2581 this.samples = []; 2582 return this; 2583 }; 2584 2585 return Timer; 2586 2587 }()); 2588 2589 // worker.js 2590 // Part of the extra module, provides support to Web Workers. 2591 2592 BenchGL.namespace('BenchGL.extra.WorkerPool'); 2593 2594 BenchGL.extra.WorkerPool = (function() { 2595 2596 // Private properties and methods 2597 var WorkerPool; 2598 2599 /** 2600 * Creates a new WorkerPool. 2601 * @class Coordinates a group of WebWorkers. 2602 * @param {String} filename The filename for the worker script. 2603 * @param {Number} n The number of WebWorkers to create. 2604 */ 2605 WorkerPool = function(filename, n){ 2606 this.workers = []; 2607 this.configs = []; 2608 while (n--) { 2609 this.workers.push(new Worker(filename)); 2610 } 2611 }; 2612 2613 /** 2614 * Maps configurations to the workers in this WorkerPool. 2615 * @param {Function} mapper The mapping function. 2616 */ 2617 WorkerPool.prototype.map = function(mapper) { 2618 var i, l; 2619 for (i = 0, l = this.workers.length; i < l; i++) { 2620 this.configs.push(mapper(i)); 2621 } 2622 }; 2623 2624 /** 2625 * Starts and merges the computation of the workers in this WorkerGroup. 2626 * @param {Function} reducer The function to merge the results. 2627 * @param {Function} callback A callback function to call when te work is done. 2628 * @param {Object} base A base result to start the computation from. 2629 */ 2630 WorkerPool.prototype.reduce = function(reducer, callback, base) { 2631 var total = base, 2632 l = this.workers.length, 2633 worker, i, 2634 /** 2635 * @ignore For documentation tool only. 2636 */ 2637 message = function(e){ 2638 l--; 2639 if (total === "undefined") { 2640 total = e.data; 2641 } 2642 else { 2643 reducer(total, e.data); 2644 } 2645 if (l === 0) { 2646 callback(total); 2647 } 2648 }; 2649 2650 for (i = 0, l = this.workers.length; i < l; i++) { 2651 worker = this.workers[i]; 2652 worker.onmessage = message; 2653 worker.postMessage(this.configs[i]); 2654 } 2655 }; 2656 2657 /** 2658 * Shuts down all the workers in this WorkerGroup. 2659 */ 2660 WorkerPool.prototype.shutDown = function() { 2661 var workers = this.workers, 2662 worker, i, l; 2663 2664 for (i = 0, l = workers.length; i < l; i++) { 2665 worker = workers[i]; 2666 worker.terminate(); 2667 } 2668 }; 2669 2670 /** 2671 * Cleans the configurations previously assigned to workers in this WorkerGroup. 2672 */ 2673 WorkerPool.prototype.clean = function(){ 2674 this.configs = []; 2675 }; 2676 2677 return WorkerPool; 2678 2679 }()); 2680 2681 // shader.js 2682 // Module webgl: Offers WebGL shader encapsulation. 2683 2684 BenchGL.namespace('BenchGL.webgl.Shader'); 2685 2686 BenchGL.webgl.Shader = (function() { 2687 2688 // Private properties and methods 2689 var Shader; 2690 2691 /** 2692 * Creates a new Shader. 2693 * @class Represents a WebGL shader. 2694 * @param {Number} type The WebGL type of this shader. 2695 * @param {String} source The source string for the shader. 2696 */ 2697 Shader = function(type, source) { 2698 var shader = gl.createShader(type), 2699 valid = false, 2700 log = ''; 2701 2702 gl.shaderSource(shader, source); 2703 gl.compileShader(shader); 2704 2705 valid = gl.getShaderParameter(shader, gl.COMPILE_STATUS) !== 0; 2706 log += gl.getShaderInfoLog(shader); 2707 2708 this.source = source; 2709 this.handler = shader; 2710 this.type = type; 2711 this.valid = valid; 2712 this.log = log; 2713 }; 2714 2715 /** 2716 * Destroys this Shader. 2717 */ 2718 Shader.prototype.destroy = function() { 2719 gl.deleteShader(this.handler); 2720 }; 2721 2722 /** 2723 * Static container for the default vertex shaders. 2724 * @static 2725 */ 2726 Shader.Vertex = { 2727 Default : [ 2728 "#ifdef GL_ES", 2729 "precision highp float;", 2730 "#endif", 2731 2732 "attribute vec3 a_position;", 2733 "attribute vec3 a_normal;", 2734 "attribute vec2 a_texcoord;", 2735 "attribute vec4 a_color;", 2736 2737 "uniform mat4 u_modelViewMatrix;", 2738 "uniform mat4 u_projectionMatrix;", 2739 "uniform mat4 u_normalMatrix;", 2740 "uniform mat4 u_viewMatrix;", 2741 2742 "uniform bool u_enableLighting;", 2743 "uniform vec3 u_ambientColor;", 2744 "uniform vec3 u_directionalColor;", 2745 "uniform vec3 u_lightingDirection;", 2746 2747 "uniform bool u_enableLight1;", 2748 "uniform vec3 u_lightColor1;", 2749 "uniform vec3 u_lightPosition1;", 2750 2751 "uniform bool u_enableLight2;", 2752 "uniform vec3 u_lightColor2;", 2753 "uniform vec3 u_lightPosition2;", 2754 2755 "uniform bool u_enableLight3;", 2756 "uniform vec3 u_lightColor3;", 2757 "uniform vec3 u_lightPosition3;", 2758 2759 "uniform bool u_enableLight4;", 2760 "uniform vec3 u_lightColor4;", 2761 "uniform vec3 u_lightPosition4;", 2762 2763 "varying vec4 v_color;", 2764 "varying vec2 v_texcoord;", 2765 "varying vec3 v_lightFactor;", 2766 2767 "void main(void) {", 2768 " vec4 ecPosition = u_modelViewMatrix * vec4(a_position, 1.0);", 2769 2770 " if (!u_enableLighting) {", 2771 " v_lightFactor = vec3(1.0, 1.0, 1.0);", 2772 " } else {", 2773 " vec3 lightDirection;", 2774 " vec3 lightPosition;", 2775 " vec3 lightFactor1 = vec3(0.0, 0.0, 0.0);", 2776 " vec3 lightFactor2 = vec3(0.0, 0.0, 0.0);", 2777 " vec3 lightFactor3 = vec3(0.0, 0.0, 0.0);", 2778 " vec3 lightFactor4 = vec3(0.0, 0.0, 0.0);", 2779 2780 " vec3 normal = normalize((u_normalMatrix * vec4(a_normal, 1.0)).xyz);", 2781 2782 " vec3 directionalFactor = max(0.0, dot(normal, -u_lightingDirection)) * u_directionalColor;", 2783 2784 " if (u_enableLight1) {", 2785 " lightPosition = (u_viewMatrix * vec4(u_lightPosition1, 1.0)).xyz;", 2786 " lightDirection = normalize(lightPosition - ecPosition.xyz);", 2787 " lightFactor1 = max(0.0, dot(normal, lightDirection)) * u_lightColor1;", 2788 " }", 2789 2790 " if (u_enableLight2) {", 2791 " lightPosition = (u_viewMatrix * vec4(u_lightPosition2, 1.0)).xyz;", 2792 " lightDirection = normalize(lightPosition - ecPosition.xyz);", 2793 " lightFactor2 = max(0.0, dot(normal, lightDirection)) * u_lightColor2;", 2794 " }", 2795 2796 " if (u_enableLight3) {", 2797 " lightPosition = (u_viewMatrix * vec4(u_lightPosition3, 1.0)).xyz;", 2798 " lightDirection = normalize(lightPosition - ecPosition.xyz);", 2799 " lightFactor3 = max(0.0, dot(normal, lightDirection)) * u_lightColor3;", 2800 " }", 2801 2802 " if (u_enableLight4) {", 2803 " lightPosition = (u_viewMatrix * vec4(u_lightPosition4, 1.0)).xyz;", 2804 " lightDirection = normalize(lightPosition - ecPosition.xyz);", 2805 " lightFactor4 = max(0.0, dot(normal, lightDirection)) * u_lightColor4;", 2806 " }", 2807 2808 " v_lightFactor = u_ambientColor + directionalFactor + ", 2809 " lightFactor1 + lightFactor2 + lightFactor3 + lightFactor4;", 2810 " }", 2811 2812 " v_color = a_color;", 2813 " v_texcoord = a_texcoord;", 2814 " gl_Position = u_projectionMatrix * ecPosition;", 2815 "}" 2816 2817 ].join("\n") 2818 }; 2819 2820 /** 2821 * Static container for the default fragment shaders. 2822 * @static 2823 */ 2824 Shader.Fragment = { 2825 Default : [ 2826 "#ifdef GL_ES", 2827 "precision highp float;", 2828 "#endif", 2829 2830 "uniform bool u_enableTexturing;", 2831 "uniform bool u_useTexture0;", 2832 "uniform sampler2D tex0;", 2833 2834 "varying vec4 v_color;", 2835 "varying vec2 v_texcoord;", 2836 "varying vec3 v_lightFactor;", 2837 2838 "void main(void) {", 2839 " vec4 fColor = vec4(v_color.rgb * v_lightFactor, v_color.a);", 2840 2841 " if (u_enableTexturing) {", 2842 " if (u_useTexture0) {", 2843 " fColor = vec4(texture2D(tex0, vec2(v_texcoord.s, v_texcoord.t)).rgb * v_lightFactor, 1.0);", 2844 " }", 2845 " }", 2846 2847 " gl_FragColor = fColor;", 2848 "}" 2849 ].join("\n") 2850 }; 2851 2852 return Shader; 2853 2854 }()); 2855 2856 // program.js 2857 // Module webgl: Gives shader program support. 2858 2859 BenchGL.namespace('BenchGL.webgl.ProgramAttribute'); 2860 2861 BenchGL.webgl.ProgramAttribute = (function() { 2862 2863 // Private properties and methods 2864 var ProgramAttribute; 2865 2866 /** 2867 * Creates a new ProgramAttribute. 2868 * @class Represents an attribute variable in a shader program. 2869 * @param {Program} program The program in wich the attribute lives. 2870 * @param {String} name The name of the attribute. 2871 * @param {Number} type The WebGL type of the attribute. 2872 * @param {Number} size The size of the attributes in bytes. 2873 * @param {Number} location The index location in the shader. 2874 */ 2875 ProgramAttribute = function(program, name, type, size, location) { 2876 this.program = program; 2877 this.name = name; 2878 this.type = type; 2879 this.size = size; 2880 this.location = location; 2881 }; 2882 2883 /** 2884 * Sets the index of this ProgramAttribute. 2885 * @param {Number} n The index to set. 2886 */ 2887 ProgramAttribute.prototype.setIndex = function(n) { 2888 gl.bindAttribLocation(this.program.handler, n, this.name); 2889 this.location = n; 2890 }; 2891 2892 /** 2893 * Gets the index of this ProgramAttribute. 2894 * @returns {Number} The current index of this ProgramAttribute in the program 2895 */ 2896 ProgramAttribute.prototype.getIndex = function() { 2897 return this.location; 2898 }; 2899 2900 return ProgramAttribute; 2901 2902 }()); 2903 2904 BenchGL.namespace('BenchGL.webgl.ProgramUniform'); 2905 2906 BenchGL.webgl.ProgramUniform = (function() { 2907 2908 // Private properties and methods 2909 var ProgramUniform; 2910 2911 /** 2912 * Creates a new ProgramUniform. 2913 * @class Represents an uniform variable in a shader program. 2914 * @param {Program} program The program in wich the uniform lives. 2915 * @param {String} name The name of the uniform. 2916 * @param {Number} type The WebGL type of the uniform. 2917 * @param {Number} size The size of the attributes in bytes. 2918 * @param {Number} location The index location in the shader. 2919 */ 2920 ProgramUniform = function(program, name, type, size, location) { 2921 this.program = program; 2922 this.name = name; 2923 this.type = type; 2924 this.size = size; 2925 this.location = location; 2926 this.func = null; 2927 this.value = null; 2928 2929 switch (type) { 2930 case gl.BOOL: 2931 this.func = function(v){ 2932 gl.uniform1i(this.location, v); 2933 }; 2934 break; 2935 case gl.BOOL_VEC2: 2936 this.func = function(v){ 2937 gl.uniform2iv(this.location, new Uint16Array(v)); 2938 }; 2939 break; 2940 case gl.BOOL_VEC3: 2941 this.func = function(v){ 2942 gl.uniform3iv(this.location, new Uint16Array(v)); 2943 }; 2944 break; 2945 case gl.BOOL_VEC4: 2946 this.func = function(v){ 2947 gl.uniform4iv(this.location, new Uint16Array(v)); 2948 }; 2949 break; 2950 case gl.INT: 2951 this.func = function(v){ 2952 gl.uniform1i(this.location, v); 2953 }; 2954 break; 2955 case gl.INT_VEC2: 2956 this.func = function(v){ 2957 gl.uniform2iv(this.location, new Uint16Array(v)); 2958 }; 2959 break; 2960 case gl.INT_VEC3: 2961 this.func = function(v){ 2962 gl.uniform3iv(this.location, new Uint16Array(v)); 2963 }; 2964 break; 2965 case gl.INT_VEC4: 2966 this.func = function(v){ 2967 gl.uniform4iv(this.location, new Uint16Array(v)); 2968 }; 2969 break; 2970 case gl.FLOAT: 2971 this.func = function(v){ 2972 gl.uniform1f(this.location, v); 2973 }; 2974 break; 2975 case gl.FLOAT_VEC2: 2976 this.func = function(v){ 2977 gl.uniform2fv(this.location, new Float32Array(v)); 2978 }; 2979 break; 2980 case gl.FLOAT_VEC3: 2981 this.func = function(v){ 2982 gl.uniform3fv(this.location, new Float32Array(v)); 2983 }; 2984 break; 2985 case gl.FLOAT_VEC4: 2986 this.func = function(v){ 2987 gl.uniform4fv(this.location, new Float32Array(v)); 2988 }; 2989 break; 2990 case gl.FLOAT_MAT2: 2991 this.func = function(v){ 2992 gl.uniformMatrix2fv(this.location, false, v.toFloat32Array()); 2993 }; 2994 break; 2995 case gl.FLOAT_MAT3: 2996 this.func = function(v){ 2997 gl.uniformMatrix3fv(this.location, false, v.toFloat32Array()); 2998 }; 2999 break; 3000 case gl.FLOAT_MAT4: 3001 this.func = function(v){ 3002 gl.uniformMatrix4fv(this.location, false, v.toFloat32Array()); 3003 }; 3004 break; 3005 default: 3006 throw { 3007 name: "UnknownUniformType", 3008 message: "The uniform variable type is unknown." 3009 }; 3010 } 3011 }; 3012 3013 /** 3014 * Sets the value of this ProgramUniform. 3015 * @param {Object} v The value to set. 3016 */ 3017 ProgramUniform.prototype.setValue = function(v) { 3018 this.value = v; 3019 this.func(v); 3020 }; 3021 3022 /** 3023 * Get the value of this ProgramUniform. 3024 */ 3025 ProgramUniform.prototype.getValue = function() { 3026 return this.value; 3027 }; 3028 3029 return ProgramUniform; 3030 3031 }()); 3032 3033 BenchGL.namespace('BenchGL.webgl.ProgramSampler'); 3034 3035 BenchGL.webgl.ProgramSampler = (function() { 3036 3037 // Private properties and methods 3038 var ProgramSampler; 3039 3040 /** 3041 * Creates a new ProgramSampler. 3042 * @class Represents an sampler variable in a shader program. 3043 * @param {Program} program The program in wich the sampler lives. 3044 * @param {String} name The name of the sampler. 3045 * @param {Number} type The WebGL type of the sampler. 3046 * @param {Number} size The size of the attributes in bytes. 3047 * @param {Number} location The index location in the shader. 3048 */ 3049 ProgramSampler = function(program, name, type, size, location) { 3050 this.program = program; 3051 this.name = name; 3052 this.type = type; 3053 this.size = size; 3054 this.location = location; 3055 this.unit = -1; // The sampler unit for WebGL ( 0 < unit < 31 ) 3056 }; 3057 3058 /** 3059 * Gets the unit of this ProgramSampler. 3060 */ 3061 ProgramSampler.prototype.getUnit = function() { 3062 return this.unit; 3063 }; 3064 3065 /** 3066 * Sets the unit of this ProgramSampler. 3067 * @param {Number} n The unit to set. 3068 */ 3069 ProgramSampler.prototype.setUnit = function(n) { 3070 gl.uniform1i(this.location, n); 3071 this.unit = n; 3072 }; 3073 3074 return ProgramSampler; 3075 3076 }()); 3077 3078 BenchGL.namespace('BenchGL.webgl.Program'); 3079 3080 BenchGL.webgl.Program = (function() { 3081 3082 // Dependencies 3083 var Shader = BenchGL.webgl.Shader, 3084 ProgramAttribute = BenchGL.webgl.ProgramAttribute, 3085 ProgramUniform = BenchGL.webgl.ProgramUniform, 3086 ProgramSampler = BenchGL.webgl.ProgramSampler, 3087 XHR = BenchGL.io.XHRequest, 3088 3089 // Private properties and methods 3090 Program; 3091 3092 /** 3093 * Creates a new Program. 3094 * @class Represents a shader program in the WebGL pipeline. 3095 * @param {Shader} vertex The vertex shader. 3096 * @param {Shader} fragment The fragment shader. 3097 */ 3098 Program = function(vertex, fragment) { 3099 var program = gl.createProgram(), 3100 valid = false, log = ''; 3101 3102 gl.attachShader(program, vertex.handler); 3103 gl.attachShader(program, fragment.handler); 3104 gl.linkProgram(program); 3105 3106 valid = gl.getProgramParameter(program, gl.LINK_STATUS) !== 0; 3107 if (valid) { 3108 log += "Compiled succesfully!\n"; 3109 } 3110 else { 3111 log += "Compilation error: "; 3112 log += gl.getProgramInfoLog(program); 3113 log += "\n"; 3114 } 3115 3116 this.vertex = vertex; 3117 this.fragment = fragment; 3118 this.handler = program; 3119 this.valid = valid; 3120 this.log = log; 3121 3122 this.attributes = {}; 3123 this.uniforms = {}; 3124 this.samplers = {}; 3125 3126 this.buffers = {}; 3127 this.cachedBuffers = {}; 3128 3129 this.build(); 3130 }; 3131 3132 /** 3133 * Builds up this Program, encapsulating WebGL entities in BenchGL versions. 3134 * @private 3135 * @returns {Program} Returns this Program. 3136 */ 3137 Program.prototype.build = function() { 3138 var program = this.handler, 3139 attributes = this.attributes, 3140 uniforms = this.uniforms, 3141 samplers = this.samplers, 3142 attributesCount = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES), 3143 uniformsCount = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS), 3144 a, u, location, attribute, uniform, sampler, i; 3145 3146 for (i = 0; i < attributesCount; ++i) { 3147 a = gl.getActiveAttrib(program, i); 3148 if (a) { 3149 location = gl.getAttribLocation(program, a.name); 3150 attribute = new ProgramAttribute(this, a.name, a.type, a.size, location); 3151 attributes[a.name] = attribute; 3152 } 3153 } 3154 3155 for (i = 0; i < uniformsCount; ++i) { 3156 u = gl.getActiveUniform(program, i); 3157 if (u) { 3158 location = gl.getUniformLocation(program, u.name); 3159 if (u.type === gl.SAMPLER_2D || u.type === gl.SAMPLER_CUBE) { 3160 sampler = new ProgramSampler(this, u.name, u.type, u.size, location); 3161 samplers[u.name] = sampler; 3162 } 3163 else { 3164 uniform = new ProgramUniform(this, u.name, u.type, u.size, location); 3165 uniforms[u.name] = uniform; 3166 } 3167 } 3168 } 3169 3170 return this; 3171 }; 3172 3173 /** 3174 * Sets the vertex shader for this Program. 3175 * @param {Shader} shader The vertex shader to set. 3176 * @returns {Program} Returns this Program. 3177 */ 3178 Program.prototype.setVertexShader = function(shader) { 3179 var program = this.handler, 3180 valid = false, 3181 log = ''; 3182 3183 gl.detachShader(program, this.vertex.handler); 3184 gl.attachShader(program, shader.handler); 3185 gl.linkProgram(program); 3186 3187 valid = gl.getProgramParameter(program, gl.LINK_STATUS) !== 0; 3188 if (valid) { 3189 log += "Recompiled succesfully!\n"; 3190 } 3191 else { 3192 log += "Compilation error: "; 3193 log += gl.getProgramInfoLog(program); 3194 log += "\n"; 3195 } 3196 3197 this.vertex = shader; 3198 this.valid = valid; 3199 this.log = log; 3200 this.attributes = {}; 3201 this.uniforms = {}; 3202 this.samplers = {}; 3203 this.buffers = {}; 3204 3205 this.build(); 3206 3207 return this; 3208 }; 3209 3210 /** 3211 * Sets the fragment shader for this Program. 3212 * @param {Shader} shader The fragment shader to set. 3213 * @returns {Program} Returns this Program. 3214 */ 3215 Program.prototype.setFragmentShader = function(shader) { 3216 var program = this.handler, 3217 valid = false, 3218 log = ''; 3219 3220 gl.detachShader(program, this.fragment.handler); 3221 gl.attachShader(program, shader.handler); 3222 gl.linkProgram(program); 3223 3224 valid = gl.getProgramParameter(program, gl.LINK_STATUS) !== 0; 3225 if (valid) { 3226 log += "Recompiled succesfully!\n"; 3227 } 3228 else { 3229 log += "Compilation error: "; 3230 log += gl.getProgramInfoLog(program); 3231 log += "\n"; 3232 } 3233 3234 this.fragment = shader; 3235 this.valid = valid; 3236 this.log = log; 3237 this.attributes = {}; 3238 this.uniforms = {}; 3239 this.samplers = {}; 3240 this.buffers = {}; 3241 3242 this.build(); 3243 3244 return this; 3245 }; 3246 3247 /** 3248 * Sets the shaders for this Program. 3249 * @param {Shader} vertex The vertex shader to set. 3250 * @param {Shader} fragment The fragment shader to set. 3251 * @returns {Program} Returns this Program. 3252 */ 3253 Program.prototype.setShaders = function(vertex, fragment) { 3254 var program = this.handler, 3255 valid = false, 3256 log = ''; 3257 3258 gl.detachShader(program, this.vertex.handler); 3259 gl.detachShader(program, this.fragment.handler); 3260 gl.attachShader(program, vertex.handler); 3261 gl.attachShader(program, fragment.handler); 3262 gl.linkProgram(program); 3263 3264 valid = gl.getProgramParameter(program, gl.LINK_STATUS) !== 0; 3265 if (valid) { 3266 log += "Recompiled succesfully!\n"; 3267 } 3268 else { 3269 log += "Compilation error: "; 3270 log += gl.getProgramInfoLog(program); 3271 log += "\n"; 3272 } 3273 3274 this.vertex = vertex; 3275 this.fragment = fragment; 3276 this.valid = valid; 3277 this.log = log; 3278 this.attributes = {}; 3279 this.uniforms = {}; 3280 this.samplers = {}; 3281 this.buffers = {}; 3282 3283 this.build(); 3284 3285 return this; 3286 }; 3287 3288 /** 3289 * Links this Program. 3290 */ 3291 Program.prototype.link = function() { 3292 gl.linkProgram(this.handler); 3293 }; 3294 3295 /** 3296 * Destroys this Program. 3297 */ 3298 Program.prototype.destroy = function() { 3299 gl.deleteProgram(this.handler); 3300 this.vertex.destroy(); 3301 this.fragment.destroy(); 3302 }; 3303 3304 /** 3305 * Binds this Program as the current shader program in the WebGL pipeline. 3306 */ 3307 Program.prototype.bind = function() { 3308 gl.useProgram(this.handler); 3309 }; 3310 3311 /** 3312 * Unbinds this Program as the current shader program in the WebGL pipeline. 3313 */ 3314 Program.prototype.unbind = function(){ 3315 gl.useProgram(null); 3316 }; 3317 3318 /** 3319 * Binds an attribute buffer in this Program. 3320 * @param {String} name The name of the attribute. 3321 * @param {Object} [options] The options for the attribute to bind. 3322 * @param {Number} [options.attributeType] The WebGL attribute type. 3323 * @param {Number} [options.dataType] The WebGL type of an element in the attribute buffer. 3324 * @param {Number} [options.drawType] The WebGL drawing mode. 3325 * @param {Number} [options.size] The size of the attribute elements in bytes. 3326 * @param {Number} [options.stride] The stride of the attribute buffer. 3327 * @param {Number} [options.offset] The offset of the attribute buffer. 3328 * @param {Object} [options.data] The attribute buffer data. 3329 * @returns {Program} Returns this Program. 3330 */ 3331 Program.prototype.bindAttribute = function(name, options) { 3332 options = $.mix({ 3333 attributeType : gl.ARRAY_BUFFER, 3334 dataType : gl.FLOAT, 3335 drawType : gl.STATIC_DRAW, 3336 size : 1, 3337 stride : 0, 3338 offset : 0 3339 }, this.cachedBuffers[name] || {}, options || {}); 3340 3341 var attributeName = options.name || name, 3342 attributeType = options.attributeType, 3343 dataType = options.dataType, 3344 drawType = options.drawType, 3345 size = options.size, 3346 stride = options.stride, 3347 offset = options.offset, 3348 data = options.data, 3349 hasBuffer = name in this.buffers, 3350 buffer = hasBuffer ? this.buffers[name] : gl.createBuffer(), 3351 hasData = 'data' in options, 3352 index = this.attributes[attributeName] && this.attributes[attributeName].getIndex(), 3353 isAttribute = index !== undefined; 3354 3355 if (!hasBuffer) { 3356 this.buffers[name] = buffer; 3357 isAttribute && gl.enableVertexAttribArray(index); 3358 } 3359 3360 gl.bindBuffer(attributeType, buffer); 3361 3362 if (hasData) { 3363 gl.bufferData(attributeType, data, drawType); 3364 } 3365 3366 isAttribute && gl.vertexAttribPointer(index, size, dataType, false, stride, offset); 3367 3368 delete options.data; 3369 this.cachedBuffers[name] = options; 3370 3371 return this; 3372 }; 3373 3374 /** 3375 * Binds all the attributes in this Program following a mapping. 3376 * @param {Object} mapping The mapping that specifies values for the attributes. 3377 * @returns {Program} Returns this Program. 3378 */ 3379 Program.prototype.bindAttributes = function(mapping) { 3380 for (var name in mapping) { 3381 this.bindAttribute(name, mapping[name]); 3382 } 3383 return this; 3384 }; 3385 3386 /** 3387 * Binds an uniform variable in this Program. 3388 * @param {String} name The name of the uniform. 3389 * @param {Object} value The value for the uniform to bind. 3390 * @returns {Program} Returns this Program. 3391 */ 3392 Program.prototype.bindUniform = function(name, value) { 3393 if (this.uniforms[name]) { 3394 this.uniforms[name].setValue(value); 3395 } 3396 return this; 3397 }; 3398 3399 /** 3400 * Binds all the uniform variables in this Program following a mapping. 3401 * @param {Object} mapping The mapping that specifies values for the uniforms. 3402 * @returns {Program} Returns this Program. 3403 */ 3404 Program.prototype.bindUniforms = function(mapping) { 3405 for (var name in mapping) { 3406 this.bindUniform(name, mapping[name]); 3407 } 3408 return this; 3409 }; 3410 3411 /** 3412 * Binds a sampler variable to a unit in this Program. 3413 * @param {String} name The name of the sampler. 3414 * @param {Number} value The value for the unit to bind the sampler to. 3415 * @returns {Program} Returns this Program. 3416 */ 3417 Program.prototype.bindSampler = function(name, value) { 3418 if (this.samplers[name]) { 3419 this.samplers[name].setUnit(value); 3420 } 3421 return this; 3422 }; 3423 3424 /** 3425 * Binds all the sampler variables in this Program following a mapping. 3426 * @param {Object} mapping The mapping that specifies units for the sampler. 3427 * @returns {Program} Returns this Program. 3428 */ 3429 Program.prototype.bindSamplers = function(mapping) { 3430 for (var name in mapping) { 3431 this.bindSampler(name, mapping[name]); 3432 } 3433 return this; 3434 }; 3435 3436 /** 3437 * Static factory method to generate a shader Program in different ways. 3438 * @static 3439 * @param {Object} options The options for creating the Program. 3440 * @returns {Program} A new Program. 3441 */ 3442 Program.factory = function(options) { 3443 var type = (options && options.type) || 'defaults', 3444 method = 'From' + $.capitalize(type); 3445 3446 if (typeof Program[method] !== "function") { 3447 throw { 3448 name: "UnknownProgramType", 3449 message: "Type '" + method + "' does not exist." 3450 }; 3451 } 3452 3453 return Program[method](options); 3454 }; 3455 3456 /** 3457 * Static method to create a shader Program from shader's URLs. 3458 * @static 3459 * @param {Object} [options] The options for creating the Program. 3460 * @param {String} [options.vertex] The URL string for the vertex shader. 3461 * @param {String} [options.fragment] The URL string for the fragment shader. 3462 * @param {Function} [options.onSuccess] A callback to call in case of success. 3463 * @param {Function} [options.onError] A callback to call in case of failure. 3464 * @returns {Program} A new Program. 3465 */ 3466 Program.FromUrls = function(options) { 3467 options = $.mix({ 3468 vertex: '', 3469 fragment: '', 3470 onSuccess: $.empty, 3471 onError: $.empty 3472 }, options || {}); 3473 3474 new XHR({ 3475 url: options.vertex, 3476 onError: function(e){ 3477 options.onError(e); 3478 }, 3479 onSuccess: function(vs){ 3480 new XHR({ 3481 url: options.fragment, 3482 onError: function(e){ 3483 options.onError(e); 3484 }, 3485 onSuccess: function(fs){ 3486 options.onSuccess(Program.FromSources({ 3487 vertex: vs, 3488 fragment: fs 3489 })); 3490 } 3491 }).send(); 3492 } 3493 }).send(); 3494 }; 3495 3496 /** 3497 * Static method to create a shader Program from shader's scripts inside a webpage. 3498 * @static 3499 * @param {Object} [options] The options for creating the Program. 3500 * @param {String} options.vertex The HTML element containing the vertex shader. 3501 * @param {String} options.fragment The HTML elemente containing the fragment shader. 3502 * @returns {Program} A new Program. 3503 */ 3504 Program.FromScripts = function(options) { 3505 var vs = options.vertex, 3506 fs = options.fragment, 3507 vertex = new Shader(gl.VERTEX_SHADER, $(vs).innerHTML), 3508 fragment = new Shader(gl.FRAGMENT_SHADER, $(fs).innerHTML); 3509 return new Program(vertex, fragment); 3510 }; 3511 3512 /** 3513 * Static method to create a shader Program from shader's sources. 3514 * @static 3515 * @param {Object} [options] The options for creating the Program. 3516 * @param {String} options.vertex The source string for the vertex shader. 3517 * @param {String} options.fragment The source string for the fragment shader. 3518 * @returns {Program} A new Program. 3519 */ 3520 Program.FromSources = function(options) { 3521 var vs = options.vertex, 3522 fs = options.fragment, 3523 vertex = new Shader(gl.VERTEX_SHADER, vs), 3524 fragment = new Shader(gl.FRAGMENT_SHADER, fs); 3525 return new Program(vertex, fragment); 3526 }; 3527 3528 /** 3529 * Static method to create a shader Program from shader's default sources. 3530 * @static 3531 * @param {Object} [options] The options for creating the Program. 3532 * @param {String} [options.vertex='Deafult'] The id string for the vertex shader. 3533 * @param {String} [options.fragment='Default'] The id string for the fragment shader. 3534 * @returns {Program} A new Program. 3535 */ 3536 Program.FromDefaults = function(options){ 3537 var vs = (options && $.capitalize(options.vertex)) || 'Default', 3538 fs = (options && $.capitalize(options.fragment)) || 'Default', 3539 vertex = new Shader(gl.VERTEX_SHADER, Shader.Vertex[vs]), 3540 fragment = new Shader(gl.FRAGMENT_SHADER, Shader.Fragment[fs]); 3541 return new Program(vertex, fragment); 3542 }; 3543 3544 return Program; 3545 3546 }()); 3547 3548 // model.js 3549 // Modeule drawing: Provides a Model object to create and manipulate shapes. 3550 3551 BenchGL.namespace('BenchGL.drawing.Model'); 3552 3553 BenchGL.drawing.Model = (function() { 3554 3555 // Dependencies 3556 var MatStack = BenchGL.math.MatrixStack, 3557 Mat = BenchGL.skin.Material, 3558 XHR = BenchGL.io.XHRequest, 3559 sin = Math.sin, 3560 cos = Math.cos, 3561 pi = Math.PI, 3562 id = 0, 3563 3564 // Private properties and methods 3565 Model; 3566 3567 /** 3568 * Creates a new Model. 3569 * @class Represents a geometric model. 3570 * @param {Object} [options] Parameters to initialize the model. 3571 * @param {Number} [drawType] The WebGL primitive type. 3572 * @param {Boolean} [useColors] Use color on a per-vertex basis. 3573 * @param {Boolean} [dynamic] Avoid caching model's geometry. 3574 * @param {Number[]} [colors=[1, 1, 1, 1]] The per-vertex color. 3575 */ 3576 Model = function(options) { 3577 options = $.mix({ 3578 drawType : gl.TRIANGLES, 3579 useColors : true, 3580 dynamic : true, 3581 colors : [1, 1, 1, 1] 3582 }, options || {}); 3583 3584 this.id = options.id || id++; 3585 this.drawType = options.drawType; 3586 this.useColors = options.useColors; 3587 this.dynamic = options.dynamic; 3588 this.vertices = options.vertices; 3589 this.normals = options.normals; 3590 this.texcoords = options.texcoords; 3591 this.colors = options.colors; 3592 this.indices = options.indices; 3593 3594 this.material = new Mat(); 3595 this.uniforms = {}; 3596 this.textures = []; 3597 3598 this.matrixStack = new MatStack(); 3599 3600 if (this.useColors) { 3601 this.normalizeColors(); 3602 } 3603 }; 3604 3605 /** 3606 * Gets the model matrix associated to this Model. 3607 * @returns {Matrix4} A matrix representing the position of this Model in the scene. 3608 */ 3609 Model.prototype.matrix = function() { 3610 return this.matrixStack.top(); 3611 }; 3612 3613 /** 3614 * Resets the matrix of this Model. 3615 */ 3616 Model.prototype.reset = function() { 3617 this.matrixStack.loadIdentity(); 3618 }; 3619 3620 /** 3621 * Translates this Model through its matrix. 3622 * @param {Number} x The x-axis translation component. 3623 * @param {Number} y The y-axis translation component. 3624 * @param {Number} z The z-axis translation component. 3625 */ 3626 Model.prototype.translate = function(x, y, z) { 3627 this.matrixStack.translate(x, y, z); 3628 }; 3629 3630 /** 3631 * Scales this Model through its matrix. 3632 * @param {Number} x The x-axis scale factor. 3633 * @param {Number} y The y-axis scale factor. 3634 * @param {Number} z The z-axis scale factor. 3635 */ 3636 Model.prototype.scale = function(x, y, z) { 3637 this.matrixStack.scale(x, y, z); 3638 }; 3639 3640 /** 3641 * Rotates this Model through its matrix. 3642 * @param {Number} angle The rotation angle. 3643 * @param {Number} x The x component of the rotation axis. 3644 * @param {Number} y The y component of the rotation axis. 3645 * @param {Number} z The z component of the rotation axis. 3646 */ 3647 Model.prototype.rotate = function(angle, x, y, z) { 3648 this.matrixStack.rotate(angle, x, y, z); 3649 }; 3650 3651 /** 3652 * Rotates this Model (around the X, Y and Z axis) through its matrix. 3653 * @param {Number} rx The rotation angle around X axis. 3654 * @param {Number} ry The rotation angle around Y axis. 3655 * @param {Number} rz The rotation angle around Z axis 3656 */ 3657 Model.prototype.rotateXYZ = function(rx, ry, rz) { 3658 this.matrixStack.rotateXYZ(rx, ry, rz); 3659 }; 3660 3661 /** 3662 * Normalizes the per-vertex color to all vertices in this Model. 3663 */ 3664 Model.prototype.normalizeColors = function() { 3665 if (!this.vertices) return; 3666 3667 var colors = this.colors, 3668 totalLength = this.vertices.length * 4 / 3, 3669 count = totalLength / colors.length, 3670 result = new Float32Array(totalLength); 3671 3672 if (colors.length < totalLength) { 3673 while (count--) { 3674 result.set(colors, count * colors.length); 3675 } 3676 this.colors = result; 3677 } 3678 }; 3679 3680 /** 3681 * Sets the texture for this Model. 3682 * @param {String[]} arguments The textures' names. 3683 */ 3684 Model.prototype.setTextures = function() { 3685 var textures = this.textures; 3686 for (var i = 0, l = arguments.length; i < l; i ++) { 3687 textures.push(arguments[i]); 3688 } 3689 }; 3690 3691 /** 3692 * Sets the material ambient color of this Model. 3693 * @param {Number} r The red component of the color. 3694 * @param {Number} g The red component of the color. 3695 * @param {Number} b The red component of the color. 3696 */ 3697 Model.prototype.setMaterialAmbient = function(r, g, b) { 3698 this.material.setAmbient(r, g, b); 3699 }; 3700 3701 /** 3702 * Sets the material diffuse color of this Model. 3703 * @param {Number} r The red component of the color. 3704 * @param {Number} g The red component of the color. 3705 * @param {Number} b The red component of the color. 3706 */ 3707 Model.prototype.setMaterialDiffuse = function(r, g, b) { 3708 this.material.setDiffuse(r, g, b); 3709 }; 3710 3711 /** 3712 * Sets the material specular color of this Model. 3713 * @param {Number} r The red component of the color. 3714 * @param {Number} g The red component of the color. 3715 * @param {Number} b The red component of the color. 3716 */ 3717 Model.prototype.setMaterialSpecular = function(r, g, b) { 3718 this.material.setSpecular(r, g, b); 3719 }; 3720 3721 /** 3722 * Sets the material emissive color of this Model. 3723 * @param {Number} r The red component of the color. 3724 * @param {Number} g The red component of the color. 3725 * @param {Number} b The red component of the color. 3726 */ 3727 Model.prototype.setMaterialEmissive = function(r, g, b) { 3728 this.material.setEmissive(r, g, b); 3729 }; 3730 3731 /** 3732 * Sets the material shininess of this Model. 3733 * @param {Number} r The red component of the color. 3734 * @param {Number} g The red component of the color. 3735 * @param {Number} b The red component of the color. 3736 */ 3737 Model.prototype.setMaterialShininess = function(shininess) { 3738 this.material.setShininess(shininess); 3739 }; 3740 3741 /** 3742 * Sets custom property to this Model to bind to a uniform value in a shader program. 3743 * @param {String} name The name of the uniform variable to bind the property. 3744 * @param {Object} value The value of the property to set. 3745 */ 3746 Model.prototype.setUniform = function(name, value) { 3747 this.uniforms[name] = value; 3748 }; 3749 3750 /** 3751 * Binds the vertices of this Model to a shader Program. 3752 * @param {Program} program The program to bind the vertices. 3753 * @param {Boolean} [update] Avoid using cached vertices. 3754 */ 3755 Model.prototype.bindVertices = function(program, update) { 3756 if (!this.vertices) return; 3757 3758 if (update || this.dynamic) { 3759 program.bindAttribute(this.id + '-vertices', { 3760 name: 'a_position', 3761 data: new Float32Array(this.vertices), 3762 size: 3 3763 }); 3764 } else { 3765 program.bindAttribute(this.id + '-vertices'); 3766 } 3767 }; 3768 3769 /** 3770 * Binds the normals of this Model to a shader Program. 3771 * @param {Program} program The program to bind the normals. 3772 * @param {Boolean} [update] Avoid using cached normals. 3773 */ 3774 Model.prototype.bindNormals = function(program, update) { 3775 if (!this.normals) return; 3776 3777 if (update || this.dynamic) { 3778 program.bindAttribute(this.id + '-normals', { 3779 name : 'a_normal', 3780 data : new Float32Array(this.normals), 3781 size : 3 3782 }); 3783 } else { 3784 program.bindAttribute(this.id + '-normals'); 3785 } 3786 }; 3787 3788 /** 3789 * Binds the texture coordinates of this Model to a shader Program. 3790 * @param {Program} program The program to bind the texture coordinates. 3791 * @param {Boolean} [update] Avoid using cached texture coordinates. 3792 */ 3793 Model.prototype.bindTexcoords = function(program, update) { 3794 if (!this.texcoords) return; 3795 3796 if (update || this.dynamic) { 3797 program.bindAttribute(this.id + '-texcoords', { 3798 name: 'a_texcoord', 3799 data: new Float32Array(this.texcoords), 3800 size: 2 3801 }); 3802 } else { 3803 program.bindAttribute(this.id + '-texcoords'); 3804 } 3805 }; 3806 3807 /** 3808 * Binds the colors of this Model to a shader Program. 3809 * @param {Program} program The program to bind the colors. 3810 * @param {Boolean} [update] Avoid using cached colors. 3811 */ 3812 Model.prototype.bindColors = function(program, update) { 3813 if (!this.colors || !this.useColors) return; 3814 3815 if (update || this.dynamic) { 3816 program.bindAttribute(this.id + '-colors', { 3817 name : 'a_color', 3818 data: new Float32Array(this.colors), 3819 size: 4 3820 }); 3821 } else { 3822 program.bindAttribute(this.id + '-colors'); 3823 } 3824 }; 3825 3826 /** 3827 * Binds the indices of this Model to a shader Program. 3828 * @param {Program} program The program to bind the indices. 3829 * @param {Boolean} [update] Avoid using cached indices. 3830 */ 3831 Model.prototype.bindIndices = function(program, update) { 3832 if (!this.indices) return; 3833 3834 if (update || this.dynamic) { 3835 program.bindAttribute(this.id + '-indices', { 3836 attributeType : gl.ELEMENT_ARRAY_BUFFER, 3837 data : new Uint16Array(this.indices), 3838 }); 3839 } else { 3840 program.bindAttribute(this.id + '-indices'); 3841 } 3842 }; 3843 3844 /** 3845 * Binds the custom properties of this Model to their uniforms variable. 3846 * @param {Program} program The program to bind the properties. 3847 */ 3848 Model.prototype.bindUniforms = function(program) { 3849 program.bindUniforms(this.uniforms); 3850 }; 3851 3852 /** 3853 * Binds the material properties of this Model to their uniforms variable. 3854 * @param {Program} program The program to bind the properties. 3855 */ 3856 Model.prototype.bindMaterial = function(program) { 3857 var material = this.material, 3858 uniforms = {}; 3859 3860 uniforms.u_matAmbient = material.ambient.toRGBAArray(); 3861 uniforms.u_matDiffuse = material.diffuse.toRGBAArray(); 3862 uniforms.u_matSpecular = material.specular.toRGBAArray(); 3863 uniforms.u_matEmissive = material.emissive.toRGBAArray(); 3864 uniforms.u_matShininess = material.shininess; 3865 3866 program.bindUniforms(uniforms); 3867 }; 3868 3869 /** 3870 * Binds the textures with this Model to their sampler variable. 3871 * @param {Program} program The program to bind the properties. 3872 * @param {Texture[]} texture The previously loaded textures. 3873 */ 3874 Model.prototype.bindTextures = function(program, textures) { 3875 var names = this.textures; 3876 for (i = 0, l = names.length; i < l; i++) { 3877 var texture = textures[names[i]]; 3878 if (texture) { 3879 program.bindUniform('u_useTexture' + i, true); 3880 program.bindSamplers('tex' + i, i); 3881 texture.bind(i); 3882 } 3883 } 3884 this.textures = []; 3885 }; 3886 3887 /** 3888 * Draws this Model. 3889 */ 3890 Model.prototype.draw = function() { 3891 if (this.indices) { 3892 // Draw the model with as an indexed vertex array 3893 gl.drawElements(this.drawType, this.indices.length, gl.UNSIGNED_SHORT, 0); 3894 } else if (this.vertices) { 3895 // Draw the model with as a simple flat vertex array 3896 gl.drawArrays(this.drawType, 0, this.vertices.length / 3); 3897 } 3898 }; 3899 3900 /** 3901 * Static factory method to generate different kinds of Models. 3902 * @static 3903 * @param {Number} type The type of Model to create. 3904 * @param {Object} options. 3905 * @returns {Model} A new instance of Model. 3906 */ 3907 Model.factory = function(type, options) { 3908 type = $.capitalize(type); 3909 3910 if (typeof Model[type] !== "function") { 3911 throw { 3912 name: "UnknownModelType", 3913 message: "Method '" + type + "' does not exist." 3914 }; 3915 } 3916 3917 return Model[type](options); 3918 }; 3919 3920 /** 3921 * Static method to generate a triangle Model. 3922 * @static 3923 * @param {Object} [options] Contains geometric model data. 3924 * @param {Number[]} [options.vertices] Contains geometric vertices data. 3925 * @param {Number[]} [options.normals] Contains geometric normals data. 3926 * @param {Number[]} [options.texcoords] Contains texture coordinates data. 3927 * @param {Number[]} [options.colors] Contains colors data. 3928 * @returns {Model} A new instance of Model. 3929 */ 3930 Model.Triangle = function(options) { 3931 return new Model($.mix({ 3932 vertices: [0, 1, 0, -1, -1, 0, 1, -1, 0], 3933 normals: [0, 0, 1, 0, 0, 1, 0, 0, 1], 3934 texcoords: [1, 1, 0, 0, 1, 0], 3935 colors: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] 3936 }, options || {})); 3937 }; 3938 3939 /** 3940 * Static method to generate a rectangle Model. 3941 * @static 3942 * @param {Object} [options] Contains geometric model data. 3943 * @param {Number[]} [options.vertices] Contains geometric vertices data. 3944 * @param {Number[]} [options.normals] Contains geometric normals data. 3945 * @param {Number[]} [options.texcoords] Contains texture coordinates data. 3946 * @param {Number[]} [options.colors] Contains colors data. 3947 * @param {Number[]} [options.indices] Contains indices data. 3948 * @returns {Model} A new instance of Model. 3949 */ 3950 Model.Rectangle = function(options) { 3951 return new Model($.mix({ 3952 vertices: [-1, -1, 0, 1, -1, 0, -1, 1, 0, 1, 1, 0], 3953 normals: [0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1], 3954 texcoords: [0, 0, 1, 0, 0, 1, 1, 1], 3955 colors: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], 3956 indices: [0, 1, 2, 3, 1, 2] 3957 }, options || {})); 3958 }; 3959 3960 /** 3961 * Static method to generate a circle Model. 3962 * @static 3963 * @param {Object} [options] Contains parameters to generate a circle. 3964 * @param {Number[]} [options.slices] The circle's approximation slices. 3965 * @param {Number[]} [options.radius] The circle's radius. 3966 * @returns {Model} A new instance of Model. 3967 */ 3968 Model.Circle = function(options) { 3969 var n = (options) ? options.slices || 16 : 16, 3970 r = (options) ? options.radius || 1 : 1, 3971 vertexCoords = [0, 0, 0], 3972 normalCoords = [0, 0, 1], 3973 textureCoords = [0.5, 0.5], 3974 i, angle, x, y, u, v; 3975 3976 for (i = 0; i <= n; i++) { 3977 angle = pi * i / n; 3978 x = r * cos(angle); 3979 y = r * sin(angle); 3980 u = (cos(angle) + 1) * 0.5; 3981 v = (sin(angle) + 1) * 0.5; 3982 3983 vertexCoords.push(x); 3984 vertexCoords.push(y); 3985 vertexCoords.push(0); 3986 normalCoords.push(0); 3987 normalCoords.push(0); 3988 normalCoords.push(1); 3989 textureCoords.push(u); 3990 textureCoords.push(v); 3991 } 3992 3993 return new Model($.mix({ 3994 drawType : gl.TRIANGLE_FAN, 3995 vertices: vertexCoords, 3996 normals: normalCoords, 3997 texcoords: textureCoords, 3998 colors: [1, 1, 1, 1] 3999 }, options || {})); 4000 }; 4001 4002 /** 4003 * Static method to generate a cube Model. 4004 * @static 4005 * @param {Object} [options] Contains geometric model data. 4006 * @param {Number[]} [options.vertices] Contains geometric vertices data. 4007 * @param {Number[]} [options.normals] Contains geometric normals data. 4008 * @param {Number[]} [options.texcoords] Contains texture coordinates data. 4009 * @param {Number[]} [options.colors] Contains colors data. 4010 * @param {Number[]} [options.indices] Contains indices data. 4011 * @returns {Model} A new instance of Model. 4012 */ 4013 Model.Cube = function(options) { 4014 return new Model($.mix({ 4015 vertices: [ 4016 -1, -1, 1, 1, -1, 1, 1, 1, 1, -1, 1, 1, 4017 -1, -1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 4018 -1, 1, -1, -1, 1, 1, 1, 1, 1, 1, 1, -1, 4019 -1, -1, -1, 1, -1, -1, 1, -1, 1, -1, -1, 1, 4020 1, -1, -1, 1, 1, -1, 1, 1, 1, 1, -1, 1, 4021 -1, -1, -1, -1, -1, 1, -1, 1, 1, -1, 1, -1], 4022 normals: [ 4023 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 4024 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 4025 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 4026 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 4027 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 4028 -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0], 4029 texcoords: [ 4030 0, 0, 1, 0, 1, 1, 0, 1, 4031 1, 0, 1, 1, 0, 1, 0, 0, 4032 0, 1, 0, 0, 1, 0, 1, 1, 4033 1, 1, 0, 1, 0, 0, 1, 0, 4034 1, 0, 1, 1, 0, 1, 0, 0, 4035 0, 0, 1, 0, 1, 1, 0, 1 ], 4036 colors: [ 4037 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4038 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4039 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4040 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4041 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4042 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], 4043 indices: [0, 1, 2, 0, 2, 3, 4, 5, 6, 4, 6, 7, 8, 9, 10, 8, 10, 11, 12, 13, 14, 12, 14, 15, 16, 17, 18, 16, 18, 19, 20, 21, 22, 20, 22, 23] 4044 }, options || {})); 4045 }; 4046 4047 /** 4048 * Static method to generate a pyramid Model. 4049 * @static 4050 * @param {Object} [options] Contains geometric model data. 4051 * @param {Number[]} [options.vertices] Contains geometric vertices data. 4052 * @param {Number[]} [options.normals] Contains geometric normals data. 4053 * @param {Number[]} [options.texcoords] Contains texture coordinates data. 4054 * @param {Number[]} [options.colors] Contains colors data. 4055 * @returns {Model} A new instance of Model. 4056 */ 4057 Model.Pyramid = function(options) { 4058 return new Model($.mix({ 4059 vertices: [ 4060 0, 1, 0, -1, -1, 1, 1, -1, 1, 4061 0, 1, 0, 1, -1, -1, -1, -1, -1, 4062 0, 1, 0, 1, -1, 1, 1, -1, -1, 4063 0, 1, 0, -1, -1, -1, -1, -1, 1], 4064 normals: [ 4065 0, 1, 0, -1, -1, 1, 1, -1, 1, 4066 0, 1, 0, 1, -1, -1, -1, -1, -1, 4067 0, 1, 0, 1, -1, 1, 1, -1, -1, 4068 0, 1, 0, -1, -1, -1, -1, -1, 1], 4069 texcoords: [ 4070 1, 1, 0, 0, 1, 0, 4071 1, 1, 0, 0, 1, 0, 4072 1, 1, 0, 0, 1, 0, 4073 1, 1, 0, 0, 1, 0], 4074 colors: [ 4075 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4076 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4077 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4078 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] 4079 }, options || {})); 4080 }; 4081 4082 /** 4083 * Static method to generate a sphere Model. 4084 * @static 4085 * @param {Object} [options] Contains parameters to generate a sphere. 4086 * @param {Number[]} [options.slices] The sphere's approximation slices. 4087 * @param {Number[]} [options.slices] The sphere's approximation slices. 4088 * @param {Number[]} [options.radius] The sphere's radius. 4089 * @returns {Model} A new instance of Model. 4090 */ 4091 Model.Sphere = function(options) { 4092 var n = (options) ? options.slices || 32 : 32, 4093 m = (options) ? options.stacks || 32 : 32, 4094 r = (options) ? options.radius || 1.0 : 1.0, 4095 vertexCoords = [], 4096 normalCoords = [], 4097 textureCoords = [], 4098 indices = [], 4099 pi2 = pi * 2, 4100 i, j, theta, phi, sint, cost, sinp, cosp, 4101 x, y, z, u, v, first, second; 4102 4103 for (i = 0; i <= n; i++) { 4104 theta = pi * i / n; 4105 sint = sin(theta); 4106 cost = cos(theta); 4107 for (j = 0; j <= m; j++) { 4108 phi = pi2 * j / m; 4109 sinp = sin(phi); 4110 cosp = cos(phi); 4111 x = r * sint * cosp; 4112 y = r * cost; 4113 z = r * sint * sinp; 4114 u = 1 - j / m; 4115 v = 1 - i / n; 4116 4117 vertexCoords.push(x); 4118 vertexCoords.push(y); 4119 vertexCoords.push(z); 4120 normalCoords.push(x); 4121 normalCoords.push(y); 4122 normalCoords.push(z); 4123 textureCoords.push(u); 4124 textureCoords.push(v); 4125 } 4126 } 4127 4128 for (i = 0; i < n; i++) { 4129 for (j = 0; j < m; j++) { 4130 first = i * (m + 1) + j; 4131 second = first + m + 1; 4132 4133 indices.push(first); 4134 indices.push(second); 4135 indices.push(first + 1); 4136 indices.push(second); 4137 indices.push(second + 1); 4138 indices.push(first + 1); 4139 } 4140 } 4141 4142 return new Model($.mix({ 4143 vertices: vertexCoords, 4144 normals: normalCoords, 4145 texcoords: textureCoords, 4146 colors: [1.0, 1.0, 1.0, 1.0], 4147 indices: indices 4148 }, options || {})); 4149 }; 4150 4151 /** 4152 * Static method to load a Model from a JSON file. 4153 * @static 4154 * @param {Object} [options] Options for creating the Model. 4155 * @param {String} [options.url] The URL of the JSON file.. 4156 * @param {Object} [options.model] Contains model options. 4157 * @returns {Model} A new instance of Model. 4158 */ 4159 Model.Json = function(options) { 4160 var modelOptions = options.model, 4161 model; 4162 4163 new XHR({ 4164 url: options.url, 4165 async: false, 4166 onSuccess: function(response){ 4167 var json = JSON.parse(response), 4168 options = $.mix({ 4169 vertices: json.vertexPositions, 4170 normals: json.vertexNormals, 4171 texcoords: json.vertexTextureCoords, 4172 indices: json.indices 4173 }, modelOptions || {}); 4174 4175 model = new Model(options); 4176 } 4177 }).send(); 4178 4179 return model; 4180 }; 4181 4182 return Model; 4183 4184 }()); 4185 4186 // renderer.js 4187 // Module drawing: Implements the core of the rendering engine. 4188 4189 BenchGL.namespace('BenchGL.drawing.SinglePassRenderingStrategy'); 4190 4191 BenchGL.drawing.SinglePassRenderingStrategy = (function() { 4192 4193 var SinglePassRenderingStrategy; 4194 4195 /** 4196 * Creates a new SinglePassRenderingStrategy. 4197 * @class Represent the first-pass rendering algorithm. 4198 * @param {Renderer} renderer A reference to the Renderer performing the algorithm. 4199 */ 4200 SinglePassRenderingStrategy = function(renderer) { 4201 this.renderer = renderer; 4202 }; 4203 4204 /** 4205 * Setups camera uniforms in the current shader program. 4206 */ 4207 SinglePassRenderingStrategy.prototype.setupCamera = function() { 4208 var program = this.renderer.program, 4209 camera = this.renderer.camera; 4210 4211 program.bindUniform('u_projectionMatrix', camera.projMatrix()); 4212 program.bindUniform('u_viewMatrix', camera.viewMatrix()); 4213 }; 4214 4215 /** 4216 * Setups lights uniforms in the current shader program. 4217 */ 4218 SinglePassRenderingStrategy.prototype.setupLights = function(){ 4219 var uniforms = {}, 4220 index = 0, l, light; 4221 4222 uniforms.u_enableLighting = this.renderer.useLighting; 4223 uniforms.u_ambientColor = this.renderer.ambientColor.toRGBArray(); 4224 uniforms.u_directionalColor = this.renderer.directionalColor.toRGBArray(); 4225 uniforms.u_lightingDirection = this.renderer.lightingDirection.toArray(); 4226 4227 for (l in this.renderer.lights) { 4228 light = this.renderer.lights[l]; 4229 uniforms['u_enableLight' + (index + 1)] = light.active; 4230 uniforms['u_lightPosition' + (index + 1)] = light.position.toArray(); 4231 uniforms['u_lightColor' + (index + 1)] = light.diffuse.toRGBArray(); 4232 uniforms['u_lightSpecularColor' + (index + 1)] = light.specular.toRGBArray(); 4233 index++; 4234 } 4235 4236 this.renderer.program.bindUniforms(uniforms); 4237 }; 4238 4239 /** 4240 * Setups texture uniforms in the current shader program. 4241 */ 4242 SinglePassRenderingStrategy.prototype.setupTextures = function() { 4243 this.renderer.program.bindUniform('u_enableTexturing', this.renderer.useTexturing); 4244 }; 4245 4246 /** 4247 * Setups special effects uniforms in the current shader program. 4248 */ 4249 SinglePassRenderingStrategy.prototype.setupEffects = function() { 4250 var effects = this.renderer.effects, 4251 uniforms = {}, 4252 e, effect, p, property, value; 4253 4254 for (e in effects) { 4255 effect = effects[e]; 4256 for (p in effect) { 4257 property = p.charAt(0).toUpperCase() + p.slice(1); 4258 value = effect[p]; 4259 uniforms['u_' + e + property] = value; 4260 } 4261 } 4262 4263 this.renderer.program.bindUniforms(uniforms); 4264 }; 4265 4266 /** 4267 * Renders a model. 4268 * @param {Model} model The model to render. 4269 */ 4270 SinglePassRenderingStrategy.prototype.renderModel = function(model) { 4271 var program = this.renderer.program, 4272 camera = this.renderer.camera, 4273 textures = this.renderer.textures, 4274 modelView, i, l, texture; 4275 4276 model.bindVertices(program); 4277 model.bindNormals(program); 4278 model.bindTexcoords(program); 4279 model.bindColors(program); 4280 model.bindIndices(program); 4281 4282 model.bindMaterial(program); 4283 model.bindUniforms(program); 4284 model.bindTextures(program, textures); 4285 4286 // Set modelView and normal matrices 4287 modelView = camera.modelViewMatrix().multiplyMat4(model.matrix()); 4288 program.bindUniform('u_modelViewMatrix', modelView); 4289 program.bindUniform('u_normalMatrix', modelView.invert().$transpose()); 4290 4291 model.draw(); 4292 }; 4293 4294 /** 4295 * Renders all the models registered to the Renderer. 4296 */ 4297 SinglePassRenderingStrategy.prototype.renderAll = function() { 4298 this.setupCamera(); 4299 this.setupLights(); 4300 this.setupTextures(); 4301 this.setupEffects(); 4302 4303 for (var i = 0, l = this.renderer.models.length; i < l; i++) { 4304 this.renderModel(this.renderer.models[i]); 4305 } 4306 }; 4307 4308 return SinglePassRenderingStrategy; 4309 4310 }()); 4311 4312 BenchGL.namespace('BenchGL.drawing.Renderer'); 4313 4314 BenchGL.drawing.Renderer = (function() { 4315 4316 // Dependencies 4317 var Vec3 = BenchGL.math.Vector3, 4318 Mat4 = BenchGL.math.Matrix4, 4319 Color = BenchGL.skin.Color, 4320 Material = BenchGL.skin.Material, 4321 Light = BenchGL.skin.Light, 4322 Texture = BenchGL.skin.Texture, 4323 TextureRequest = BenchGL.io.TextureRequest, 4324 SinglePassRenderingStrategy = BenchGL.drawing.SinglePassRenderingStrategy, 4325 4326 // Private properties and methods 4327 Renderer; 4328 4329 /** 4330 * Creates a new Renderer. 4331 * @class Represents the rendering engine in BenchGL. 4332 * @param {Program} program The current active shader program. 4333 * @param {Camera} camera The camera holding the point of view over the world. 4334 * @param {Object} effects Contains special effects' specifications. 4335 */ 4336 Renderer = function(program, camera, effects) { 4337 this.program = program; 4338 this.camera = camera; 4339 this.effects = effects; 4340 4341 // Rendering strategy 4342 this.strategy = new SinglePassRenderingStrategy(this); 4343 4344 // Background and current color 4345 this.clearColor = new Color(0, 0, 0, 1); 4346 4347 // Textures 4348 this.useTexturing = false; 4349 this.textures = {}; 4350 4351 // Ambient Light 4352 this.ambientColor = new Color(0.2, 0.2, 0.2); 4353 4354 // Lights 4355 this.useLighting = false; 4356 this.directionalColor = new Color(0.8, 0.8, 0.8); 4357 this.lightingDirection = new Vec3(0.0, 0.0, -1.0); 4358 this.lights = {}; 4359 4360 // Saved models 4361 this.models = []; 4362 }; 4363 4364 /** 4365 * Clear the background. 4366 */ 4367 Renderer.prototype.background = function(r, g, b, a) { 4368 var color = this.clearColor; 4369 4370 gl.clearColor(r || color.r, g || color.g, b || color.b, a || color.a); 4371 gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); 4372 gl.enable(gl.DEPTH_TEST); 4373 }; 4374 4375 /** 4376 * Switches lighting. 4377 * @param {Boolean} lighting True to enable lighting, else otherwise. 4378 */ 4379 Renderer.prototype.useLights = function(lighting) { 4380 this.useLighting = lighting; 4381 }; 4382 4383 /** 4384 * Switches texturing. 4385 * @param {Boolean} texturing True to enable texturing, else otherwise. 4386 */ 4387 Renderer.prototype.useTextures = function(texturing) { 4388 this.useTexturing = texturing; 4389 }; 4390 4391 /** 4392 * Switches alpha-blending. 4393 * @param {Boolean} blending True to enable blending, else otherwise. 4394 * @param {Object} [options] Contain info over about blending functions. 4395 */ 4396 Renderer.prototype.useAlphaBlending = function(blending, options) { 4397 options = $.mix({ 4398 src: gl.SRC_ALPHA, 4399 dest: gl.ONE 4400 }, options || {}); 4401 4402 if (blending) { 4403 gl.blendFunc(gl.SRC_ALPHA, gl.ONE); 4404 gl.enable(gl.BLEND); 4405 gl.disable(gl.DEPTH_TEST); 4406 } else { 4407 gl.disable(gl.BLEND); 4408 gl.enable(gl.DEPTH_TEST); 4409 } 4410 }; 4411 4412 /** 4413 * Sets the clear color for the backgrond. 4414 * @param {Number} r The red component of the color. 4415 * @param {Number} g The green component of the color. 4416 * @param {Number} b The blue component of the color. 4417 * @param {Number} a The alpha component of the color. 4418 */ 4419 Renderer.prototype.setClearColor = function(r, g, b, a) { 4420 this.clearColor = new Color(r, g, b, a); 4421 }; 4422 4423 /** 4424 * Sets the world ambient color. 4425 * @param {Number} r The red component of the color. 4426 * @param {Number} g The green component of the color. 4427 * @param {Number} b The blue component of the color. 4428 * @param {Number} a The alpha component of the color. 4429 */ 4430 Renderer.prototype.setAmbientColor = function(r, g, b, a) { 4431 this.ambientColor = new Color(r, g, b, a); 4432 }; 4433 4434 /** 4435 * Sets the world directional light color. 4436 * @param {Number} r The red component of the color. 4437 * @param {Number} g The green component of the color. 4438 * @param {Number} b The blue component of the color. 4439 * @param {Number} a The alpha component of the color. 4440 */ 4441 Renderer.prototype.setDirectionalColor = function(r, g, b, a) { 4442 this.directionalColor = new Color(r, g, b, a); 4443 }; 4444 4445 /** 4446 * Sets the world directional light vector. 4447 * @param {Number} x The x component of the vector. 4448 * @param {Number} y The y component of the vector. 4449 * @param {Number} z The z component of the vector. 4450 */ 4451 Renderer.prototype.setLightingDirection = function(x, y, z) { 4452 this.lightingDirection = new Vec3(x, y, z).$unit(); 4453 }; 4454 4455 /** 4456 * Adds a light. 4457 * @param {String} name The name for the light. 4458 * @param {Object} options Contains information about the light. 4459 */ 4460 Renderer.prototype.addLight = function(name, options) { 4461 this.lights[name] = new Light(options); 4462 }; 4463 4464 /** 4465 * Adds a single texture. 4466 * @param {String} name The name for the texture. 4467 * @param {Object} options Contains information about the texture. 4468 */ 4469 Renderer.prototype.addTexture = function(name, options) { 4470 this.textures[name] = new Texture(options); 4471 }; 4472 4473 /** 4474 * Adds multiple textures. 4475 * @param {Object} options Contains information about the textures to load. 4476 */ 4477 Renderer.prototype.addTextures = function(options) { 4478 var myself = this; 4479 4480 new TextureRequest(options).send(function(name, options) { 4481 myself.addTexture(name, options); 4482 }); 4483 }; 4484 4485 /** 4486 * Adds models to this Renderer. 4487 * @param {Object} arguments The models to load. 4488 */ 4489 Renderer.prototype.addModels = function() { 4490 var models = this.models, 4491 i, l, model; 4492 4493 for (i = 0, l = arguments.length; i < l; i++) { 4494 model = arguments[i]; 4495 models.push(model); 4496 4497 model.bindVertices(this.program, true); 4498 model.bindNormals(this.program, true); 4499 model.bindTexcoords(this.program, true); 4500 model.bindColors(this.program, true); 4501 model.bindIndices(this.program, true); 4502 } 4503 }; 4504 4505 /** 4506 * Setups camera uniforms in the current shader program. 4507 */ 4508 Renderer.prototype.setupCamera = function() { 4509 this.strategy.setupCamera(); 4510 }; 4511 4512 /** 4513 * Setups lights uniforms in the current shader program. 4514 */ 4515 Renderer.prototype.setupLights = function() { 4516 this.strategy.setupLights(); 4517 }; 4518 4519 /** 4520 * Setups lights uniforms in the current shader program. 4521 */ 4522 Renderer.prototype.setupTextures = function() { 4523 this.strategy.setupTextures(); 4524 }; 4525 4526 /** 4527 * Setups special effects uniforms in the current shader program. 4528 */ 4529 Renderer.prototype.setupEffects = function() { 4530 this.strategy.setupTextures(); 4531 }; 4532 4533 /** 4534 * Renders a model. 4535 * @param {Model} model The model to render. 4536 */ 4537 Renderer.prototype.renderModel = function(model) { 4538 this.strategy.renderModel(model); 4539 }; 4540 4541 /** 4542 * Renders all the models registered to the Renderer. 4543 */ 4544 Renderer.prototype.renderAll = function() { 4545 this.strategy.renderAll(); 4546 }; 4547 4548 return Renderer; 4549 4550 }()); 4551 4552 // webgl.js 4553 4554 BenchGL.namespace('BenchGL.webgl.WebGL'); 4555 4556 BenchGL.webgl.WebGL = (function() { 4557 4558 // Private properties and methods 4559 var WebGL; 4560 4561 /** 4562 * The WebGL container. 4563 * @class Represents a container for the static method that retrieves a WebGL context. 4564 */ 4565 WebGL = {}; 4566 4567 /** 4568 * Tries to retrieve a WebGL, if availale. 4569 * @param {String|HTMLCanvasElement} The canvas id or element to leverage. 4570 * @param {Object} Options for creating the context. 4571 * @returns {WebGLRenderingContext} A WebGL rendering context or null if not available. 4572 */ 4573 WebGL.getContext = function(canvas, options) { 4574 var canvas = typeof canvas === "string" ? $(canvas) : canvas, 4575 options = options || {}, 4576 gl = canvas.getContext('experimental-webgl', options); 4577 4578 if (!gl) { 4579 gl = canvas.getContext('webgl', options); 4580 } 4581 4582 return gl; 4583 }; 4584 4585 return WebGL; 4586 4587 }()); 4588 // core.js 4589 // The core module provides the main entry point for the library. 4590 4591 //BenchGL.namespace('BenchGL.core.Engine'); 4592 4593 BenchGL.Engine = (function() { 4594 4595 // Dependencies 4596 var WebGL = BenchGL.webgl.WebGL, 4597 Program = BenchGL.webgl.Program, 4598 Canvas = BenchGL.ui.Canvas, 4599 Camera = BenchGL.ui.Camera, 4600 Renderer = BenchGL.drawing.Renderer, 4601 instance, 4602 4603 // Private properties 4604 Engine, 4605 4606 /** 4607 * @ignore 4608 */ 4609 start = function(program, canvas, camera, effects, callback, debug) { 4610 // Binds the loaded program for rendering 4611 program.bind(); 4612 4613 // Create a renderer 4614 renderer = new Renderer(program, camera, effects); 4615 4616 if (debug) { 4617 gl.setTracing(true); 4618 } 4619 4620 // Call the application with library handlers references 4621 callback({ 4622 gl: gl, 4623 canvas: canvas, 4624 program: program, 4625 camera: camera, 4626 renderer: renderer 4627 }); 4628 4629 if (debug) { 4630 gl.setTracing(false); 4631 } 4632 }; 4633 4634 /** 4635 * Creates an instance of BenchGL. 4636 * @class Provides an entry point for BenchGL library. 4637 * @param {String} canvasId The id of the canvas that WebGL exploits. 4638 * @param {Object} [options] General options for initializing the library. 4639 * @param {Object} [options.context] The options for the WebGL context. 4640 * @param {Object} [options.program] The options for the shader program. 4641 * @param {String} [options.program.type='defaults'] The type of shader program. 4642 * @param {String} [options.program.vertex] The vertex shader source. 4643 * @param {String} [options.program.fragment] The fragmente shader source. 4644 * @param {String} [options.camera] The options for the camera. 4645 * @param {Number} [options.camera.fovy=45] The field of view angle for the camera. 4646 * @param {Number} [options.camera.near=0.1] The near clipping plane for the camera. 4647 * @param {Number} [options.camera.far=100] The far clipping plane for the camera. 4648 * @param {Object} [effects] Special effects for the rendering engine. 4649 * @param {Object} [events] Functions to eventually handle user events. 4650 * @param {Boolean} [debug=false] Is debug active? 4651 * @param {Function} [onError] Callback function to call on errors. 4652 * @param {Function} [onLoad] Callback function to call after loading succesfully. 4653 */ 4654 Engine = function(canvasId, options) { 4655 if (instance) { 4656 return instance; 4657 } 4658 4659 instance = this; 4660 4661 options = $.mix({ 4662 context: {}, 4663 program: { 4664 type: 'defaults' // {defaults|urls|scripts|sources} 4665 }, 4666 camera: { 4667 fovy: 45, 4668 near: 0.1, 4669 far: 100 4670 }, 4671 effects: { 4672 /* 4673 Example: 4674 4675 fog : { 4676 active : true, 4677 color : [0.5, 0.5, 0.5], 4678 near : 10, 4679 far : 100 4680 } 4681 4682 */ 4683 }, 4684 events: { 4685 /* 4686 Example: 4687 4688 onKeyDown : function() { ... }, 4689 onMouseMove : function() { ... }, 4690 4691 */ 4692 }, 4693 debug: false, 4694 onError: $.empty, 4695 onLoad: $.empty 4696 }, options || {}); 4697 4698 var contextOptions = options.context, 4699 eventsOptions = options.events, 4700 cameraOptions = options.camera, 4701 programOptions = options.program, 4702 effectsOptions = options.effects, 4703 canvas, program, camera, renderer; 4704 4705 // Create the WebGL context and store it in a library-shared variable. 4706 gl = WebGL.getContext(canvasId, contextOptions); 4707 4708 if (!gl) { 4709 options.onError(); 4710 return null; 4711 } 4712 4713 // Use webgl-trace.js library to trace webgl calls 4714 if (options.debug && WebGLDebugUtils) { 4715 gl = WebGLDebugUtils.makeDebugContext(gl); 4716 } 4717 4718 // Create a canvas wrapper to handle user events 4719 canvas = new Canvas(gl.canvas, eventsOptions); 4720 4721 // Create a camera 4722 camera = new Camera($.mix(cameraOptions, { 4723 aspect: gl.canvas.width / gl.canvas.height 4724 })); 4725 4726 // Set up the shader program asynchronously 4727 program = Program.factory($.mix({ 4728 onSuccess : function(program) { 4729 start(program, canvas, camera, effectsOptions, function(application) { 4730 options.onLoad(application); 4731 }, options.debug); 4732 }, 4733 onError : function(e) { 4734 options.onError(e); 4735 } 4736 }, programOptions)); 4737 4738 // If the program has loaded correctly, call the onLoad callback 4739 if (program) { 4740 start(program, canvas, camera, effectsOptions, function(application) { 4741 options.onLoad(application); 4742 }, options.debug); 4743 } 4744 }; 4745 4746 return Engine; 4747 4748 }()); 4749 4750 // Framework version 4751 BenchGL.version = '0.1'; 4752 4753 // WebGL context container 4754 var gl; 4755 4756 }()); 4757