browser/devtools/tilt/tilt-gl.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* vim: set ts=2 et sw=2 tw=80: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6 "use strict";
michael@0 7
michael@0 8 const {Cc, Ci, Cu} = require("chrome");
michael@0 9
michael@0 10 let TiltUtils = require("devtools/tilt/tilt-utils");
michael@0 11 let {TiltMath, mat4} = require("devtools/tilt/tilt-math");
michael@0 12
michael@0 13 Cu.import("resource://gre/modules/Services.jsm");
michael@0 14
michael@0 15 const WEBGL_CONTEXT_NAME = "experimental-webgl";
michael@0 16
michael@0 17
michael@0 18 /**
michael@0 19 * Module containing thin wrappers around low-level WebGL functions.
michael@0 20 */
michael@0 21 let TiltGL = {};
michael@0 22 module.exports = TiltGL;
michael@0 23
michael@0 24 /**
michael@0 25 * Contains commonly used helper methods used in any 3D application.
michael@0 26 *
michael@0 27 * @param {HTMLCanvasElement} aCanvas
michael@0 28 * the canvas element used for rendering
michael@0 29 * @param {Function} onError
michael@0 30 * optional, function called if initialization failed
michael@0 31 * @param {Function} onLoad
michael@0 32 * optional, function called if initialization worked
michael@0 33 */
michael@0 34 TiltGL.Renderer = function TGL_Renderer(aCanvas, onError, onLoad)
michael@0 35 {
michael@0 36 /**
michael@0 37 * The WebGL context obtained from the canvas element, used for drawing.
michael@0 38 */
michael@0 39 this.context = TiltGL.create3DContext(aCanvas);
michael@0 40
michael@0 41 // check if the context was created successfully
michael@0 42 if (!this.context) {
michael@0 43 TiltUtils.Output.alert("Firefox", TiltUtils.L10n.get("initTilt.error"));
michael@0 44 TiltUtils.Output.error(TiltUtils.L10n.get("initWebGL.error"));
michael@0 45
michael@0 46 if ("function" === typeof onError) {
michael@0 47 onError();
michael@0 48 }
michael@0 49 return;
michael@0 50 }
michael@0 51
michael@0 52 // set the default clear color and depth buffers
michael@0 53 this.context.clearColor(0, 0, 0, 0);
michael@0 54 this.context.clearDepth(1);
michael@0 55
michael@0 56 /**
michael@0 57 * Variables representing the current framebuffer width and height.
michael@0 58 */
michael@0 59 this.width = aCanvas.width;
michael@0 60 this.height = aCanvas.height;
michael@0 61 this.initialWidth = this.width;
michael@0 62 this.initialHeight = this.height;
michael@0 63
michael@0 64 /**
michael@0 65 * The current model view matrix.
michael@0 66 */
michael@0 67 this.mvMatrix = mat4.identity(mat4.create());
michael@0 68
michael@0 69 /**
michael@0 70 * The current projection matrix.
michael@0 71 */
michael@0 72 this.projMatrix = mat4.identity(mat4.create());
michael@0 73
michael@0 74 /**
michael@0 75 * The current fill color applied to any objects which can be filled.
michael@0 76 * These are rectangles, circles, boxes, 2d or 3d primitives in general.
michael@0 77 */
michael@0 78 this._fillColor = [];
michael@0 79
michael@0 80 /**
michael@0 81 * The current stroke color applied to any objects which can be stroked.
michael@0 82 * This property mostly refers to lines.
michael@0 83 */
michael@0 84 this._strokeColor = [];
michael@0 85
michael@0 86 /**
michael@0 87 * Variable representing the current stroke weight.
michael@0 88 */
michael@0 89 this._strokeWeightValue = 0;
michael@0 90
michael@0 91 /**
michael@0 92 * A shader useful for drawing vertices with only a color component.
michael@0 93 */
michael@0 94 this._colorShader = new TiltGL.Program(this.context, {
michael@0 95 vs: TiltGL.ColorShader.vs,
michael@0 96 fs: TiltGL.ColorShader.fs,
michael@0 97 attributes: ["vertexPosition"],
michael@0 98 uniforms: ["mvMatrix", "projMatrix", "fill"]
michael@0 99 });
michael@0 100
michael@0 101 // create helper functions to create shaders, meshes, buffers and textures
michael@0 102 this.Program =
michael@0 103 TiltGL.Program.bind(TiltGL.Program, this.context);
michael@0 104 this.VertexBuffer =
michael@0 105 TiltGL.VertexBuffer.bind(TiltGL.VertexBuffer, this.context);
michael@0 106 this.IndexBuffer =
michael@0 107 TiltGL.IndexBuffer.bind(TiltGL.IndexBuffer, this.context);
michael@0 108 this.Texture =
michael@0 109 TiltGL.Texture.bind(TiltGL.Texture, this.context);
michael@0 110
michael@0 111 // set the default mvp matrices, tint, fill, stroke and other visual props.
michael@0 112 this.defaults();
michael@0 113
michael@0 114 // the renderer was created successfully
michael@0 115 if ("function" === typeof onLoad) {
michael@0 116 onLoad();
michael@0 117 }
michael@0 118 };
michael@0 119
michael@0 120 TiltGL.Renderer.prototype = {
michael@0 121
michael@0 122 /**
michael@0 123 * Clears the color and depth buffers.
michael@0 124 */
michael@0 125 clear: function TGLR_clear()
michael@0 126 {
michael@0 127 let gl = this.context;
michael@0 128 gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
michael@0 129 },
michael@0 130
michael@0 131 /**
michael@0 132 * Sets if depth testing should be enabled or not.
michael@0 133 * Disabling could be useful when handling transparency (for example).
michael@0 134 *
michael@0 135 * @param {Boolean} aEnabledFlag
michael@0 136 * true if depth testing should be enabled
michael@0 137 */
michael@0 138 depthTest: function TGLR_depthTest(aEnabledFlag)
michael@0 139 {
michael@0 140 let gl = this.context;
michael@0 141
michael@0 142 if (aEnabledFlag) {
michael@0 143 gl.enable(gl.DEPTH_TEST);
michael@0 144 } else {
michael@0 145 gl.disable(gl.DEPTH_TEST);
michael@0 146 }
michael@0 147 },
michael@0 148
michael@0 149 /**
michael@0 150 * Sets if stencil testing should be enabled or not.
michael@0 151 *
michael@0 152 * @param {Boolean} aEnabledFlag
michael@0 153 * true if stencil testing should be enabled
michael@0 154 */
michael@0 155 stencilTest: function TGLR_stencilTest(aEnabledFlag)
michael@0 156 {
michael@0 157 let gl = this.context;
michael@0 158
michael@0 159 if (aEnabledFlag) {
michael@0 160 gl.enable(gl.STENCIL_TEST);
michael@0 161 } else {
michael@0 162 gl.disable(gl.STENCIL_TEST);
michael@0 163 }
michael@0 164 },
michael@0 165
michael@0 166 /**
michael@0 167 * Sets cull face, either "front", "back" or disabled.
michael@0 168 *
michael@0 169 * @param {String} aModeFlag
michael@0 170 * blending mode, either "front", "back", "both" or falsy
michael@0 171 */
michael@0 172 cullFace: function TGLR_cullFace(aModeFlag)
michael@0 173 {
michael@0 174 let gl = this.context;
michael@0 175
michael@0 176 switch (aModeFlag) {
michael@0 177 case "front":
michael@0 178 gl.enable(gl.CULL_FACE);
michael@0 179 gl.cullFace(gl.FRONT);
michael@0 180 break;
michael@0 181 case "back":
michael@0 182 gl.enable(gl.CULL_FACE);
michael@0 183 gl.cullFace(gl.BACK);
michael@0 184 break;
michael@0 185 case "both":
michael@0 186 gl.enable(gl.CULL_FACE);
michael@0 187 gl.cullFace(gl.FRONT_AND_BACK);
michael@0 188 break;
michael@0 189 default:
michael@0 190 gl.disable(gl.CULL_FACE);
michael@0 191 }
michael@0 192 },
michael@0 193
michael@0 194 /**
michael@0 195 * Specifies the orientation of front-facing polygons.
michael@0 196 *
michael@0 197 * @param {String} aModeFlag
michael@0 198 * either "cw" or "ccw"
michael@0 199 */
michael@0 200 frontFace: function TGLR_frontFace(aModeFlag)
michael@0 201 {
michael@0 202 let gl = this.context;
michael@0 203
michael@0 204 switch (aModeFlag) {
michael@0 205 case "cw":
michael@0 206 gl.frontFace(gl.CW);
michael@0 207 break;
michael@0 208 case "ccw":
michael@0 209 gl.frontFace(gl.CCW);
michael@0 210 break;
michael@0 211 }
michael@0 212 },
michael@0 213
michael@0 214 /**
michael@0 215 * Sets blending, either "alpha" or "add" (additive blending).
michael@0 216 * Anything else disables blending.
michael@0 217 *
michael@0 218 * @param {String} aModeFlag
michael@0 219 * blending mode, either "alpha", "add" or falsy
michael@0 220 */
michael@0 221 blendMode: function TGLR_blendMode(aModeFlag)
michael@0 222 {
michael@0 223 let gl = this.context;
michael@0 224
michael@0 225 switch (aModeFlag) {
michael@0 226 case "alpha":
michael@0 227 gl.enable(gl.BLEND);
michael@0 228 gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
michael@0 229 break;
michael@0 230 case "add":
michael@0 231 gl.enable(gl.BLEND);
michael@0 232 gl.blendFunc(gl.SRC_ALPHA, gl.ONE);
michael@0 233 break;
michael@0 234 default:
michael@0 235 gl.disable(gl.BLEND);
michael@0 236 }
michael@0 237 },
michael@0 238
michael@0 239 /**
michael@0 240 * Helper function to activate the color shader.
michael@0 241 *
michael@0 242 * @param {TiltGL.VertexBuffer} aVerticesBuffer
michael@0 243 * a buffer of vertices positions
michael@0 244 * @param {Array} aColor
michael@0 245 * the color fill to be used as [r, g, b, a] with 0..1 range
michael@0 246 * @param {Array} aMvMatrix
michael@0 247 * the model view matrix
michael@0 248 * @param {Array} aProjMatrix
michael@0 249 * the projection matrix
michael@0 250 */
michael@0 251 useColorShader: function TGLR_useColorShader(
michael@0 252 aVerticesBuffer, aColor, aMvMatrix, aProjMatrix)
michael@0 253 {
michael@0 254 let program = this._colorShader;
michael@0 255
michael@0 256 // use this program
michael@0 257 program.use();
michael@0 258
michael@0 259 // bind the attributes and uniforms as necessary
michael@0 260 program.bindVertexBuffer("vertexPosition", aVerticesBuffer);
michael@0 261 program.bindUniformMatrix("mvMatrix", aMvMatrix || this.mvMatrix);
michael@0 262 program.bindUniformMatrix("projMatrix", aProjMatrix || this.projMatrix);
michael@0 263 program.bindUniformVec4("fill", aColor || this._fillColor);
michael@0 264 },
michael@0 265
michael@0 266 /**
michael@0 267 * Draws bound vertex buffers using the specified parameters.
michael@0 268 *
michael@0 269 * @param {Number} aDrawMode
michael@0 270 * WebGL enum, like TRIANGLES
michael@0 271 * @param {Number} aCount
michael@0 272 * the number of indices to be rendered
michael@0 273 */
michael@0 274 drawVertices: function TGLR_drawVertices(aDrawMode, aCount)
michael@0 275 {
michael@0 276 this.context.drawArrays(aDrawMode, 0, aCount);
michael@0 277 },
michael@0 278
michael@0 279 /**
michael@0 280 * Draws bound vertex buffers using the specified parameters.
michael@0 281 * This function also makes use of an index buffer.
michael@0 282 *
michael@0 283 * @param {Number} aDrawMode
michael@0 284 * WebGL enum, like TRIANGLES
michael@0 285 * @param {TiltGL.IndexBuffer} aIndicesBuffer
michael@0 286 * indices for the vertices buffer
michael@0 287 */
michael@0 288 drawIndexedVertices: function TGLR_drawIndexedVertices(
michael@0 289 aDrawMode, aIndicesBuffer)
michael@0 290 {
michael@0 291 let gl = this.context;
michael@0 292
michael@0 293 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, aIndicesBuffer._ref);
michael@0 294 gl.drawElements(aDrawMode, aIndicesBuffer.numItems, gl.UNSIGNED_SHORT, 0);
michael@0 295 },
michael@0 296
michael@0 297 /**
michael@0 298 * Sets the current fill color.
michael@0 299 *
michael@0 300 * @param {Array} aColor
michael@0 301 * the color fill to be used as [r, g, b, a] with 0..1 range
michael@0 302 * @param {Number} aMultiplyAlpha
michael@0 303 * optional, scalar to multiply the alpha element with
michael@0 304 */
michael@0 305 fill: function TGLR_fill(aColor, aMultiplyAlpha)
michael@0 306 {
michael@0 307 let fill = this._fillColor;
michael@0 308
michael@0 309 fill[0] = aColor[0];
michael@0 310 fill[1] = aColor[1];
michael@0 311 fill[2] = aColor[2];
michael@0 312 fill[3] = aColor[3] * (aMultiplyAlpha || 1);
michael@0 313 },
michael@0 314
michael@0 315 /**
michael@0 316 * Sets the current stroke color.
michael@0 317 *
michael@0 318 * @param {Array} aColor
michael@0 319 * the color stroke to be used as [r, g, b, a] with 0..1 range
michael@0 320 * @param {Number} aMultiplyAlpha
michael@0 321 * optional, scalar to multiply the alpha element with
michael@0 322 */
michael@0 323 stroke: function TGLR_stroke(aColor, aMultiplyAlpha)
michael@0 324 {
michael@0 325 let stroke = this._strokeColor;
michael@0 326
michael@0 327 stroke[0] = aColor[0];
michael@0 328 stroke[1] = aColor[1];
michael@0 329 stroke[2] = aColor[2];
michael@0 330 stroke[3] = aColor[3] * (aMultiplyAlpha || 1);
michael@0 331 },
michael@0 332
michael@0 333 /**
michael@0 334 * Sets the current stroke weight (line width).
michael@0 335 *
michael@0 336 * @param {Number} aWeight
michael@0 337 * the stroke weight
michael@0 338 */
michael@0 339 strokeWeight: function TGLR_strokeWeight(aWeight)
michael@0 340 {
michael@0 341 if (this._strokeWeightValue !== aWeight) {
michael@0 342 this._strokeWeightValue = aWeight;
michael@0 343 this.context.lineWidth(aWeight);
michael@0 344 }
michael@0 345 },
michael@0 346
michael@0 347 /**
michael@0 348 * Sets a default perspective projection, with the near frustum rectangle
michael@0 349 * mapped to the canvas width and height bounds.
michael@0 350 */
michael@0 351 perspective: function TGLR_perspective()
michael@0 352 {
michael@0 353 let fov = 45;
michael@0 354 let w = this.width;
michael@0 355 let h = this.height;
michael@0 356 let x = w / 2;
michael@0 357 let y = h / 2;
michael@0 358 let z = y / Math.tan(TiltMath.radians(fov) / 2);
michael@0 359 let aspect = w / h;
michael@0 360 let znear = z / 10;
michael@0 361 let zfar = z * 10;
michael@0 362
michael@0 363 mat4.perspective(fov, aspect, znear, zfar, this.projMatrix, -1);
michael@0 364 mat4.translate(this.projMatrix, [-x, -y, -z]);
michael@0 365 mat4.identity(this.mvMatrix);
michael@0 366 },
michael@0 367
michael@0 368 /**
michael@0 369 * Sets a default orthographic projection (recommended for 2d rendering).
michael@0 370 */
michael@0 371 ortho: function TGLR_ortho()
michael@0 372 {
michael@0 373 mat4.ortho(0, this.width, this.height, 0, -1, 1, this.projMatrix);
michael@0 374 mat4.identity(this.mvMatrix);
michael@0 375 },
michael@0 376
michael@0 377 /**
michael@0 378 * Sets a custom projection matrix.
michael@0 379 * @param {Array} matrix: the custom projection matrix to be used
michael@0 380 */
michael@0 381 projection: function TGLR_projection(aMatrix)
michael@0 382 {
michael@0 383 mat4.set(aMatrix, this.projMatrix);
michael@0 384 mat4.identity(this.mvMatrix);
michael@0 385 },
michael@0 386
michael@0 387 /**
michael@0 388 * Resets the model view matrix to identity.
michael@0 389 * This is a default matrix with no rotation, no scaling, at (0, 0, 0);
michael@0 390 */
michael@0 391 origin: function TGLR_origin()
michael@0 392 {
michael@0 393 mat4.identity(this.mvMatrix);
michael@0 394 },
michael@0 395
michael@0 396 /**
michael@0 397 * Transforms the model view matrix with a new matrix.
michael@0 398 * Useful for creating custom transformations.
michael@0 399 *
michael@0 400 * @param {Array} matrix: the matrix to be multiply the model view with
michael@0 401 */
michael@0 402 transform: function TGLR_transform(aMatrix)
michael@0 403 {
michael@0 404 mat4.multiply(this.mvMatrix, aMatrix);
michael@0 405 },
michael@0 406
michael@0 407 /**
michael@0 408 * Translates the model view by the x, y and z coordinates.
michael@0 409 *
michael@0 410 * @param {Number} x
michael@0 411 * the x amount of translation
michael@0 412 * @param {Number} y
michael@0 413 * the y amount of translation
michael@0 414 * @param {Number} z
michael@0 415 * optional, the z amount of translation
michael@0 416 */
michael@0 417 translate: function TGLR_translate(x, y, z)
michael@0 418 {
michael@0 419 mat4.translate(this.mvMatrix, [x, y, z || 0]);
michael@0 420 },
michael@0 421
michael@0 422 /**
michael@0 423 * Rotates the model view by a specified angle on the x, y and z axis.
michael@0 424 *
michael@0 425 * @param {Number} angle
michael@0 426 * the angle expressed in radians
michael@0 427 * @param {Number} x
michael@0 428 * the x axis of the rotation
michael@0 429 * @param {Number} y
michael@0 430 * the y axis of the rotation
michael@0 431 * @param {Number} z
michael@0 432 * the z axis of the rotation
michael@0 433 */
michael@0 434 rotate: function TGLR_rotate(angle, x, y, z)
michael@0 435 {
michael@0 436 mat4.rotate(this.mvMatrix, angle, [x, y, z]);
michael@0 437 },
michael@0 438
michael@0 439 /**
michael@0 440 * Rotates the model view by a specified angle on the x axis.
michael@0 441 *
michael@0 442 * @param {Number} aAngle
michael@0 443 * the angle expressed in radians
michael@0 444 */
michael@0 445 rotateX: function TGLR_rotateX(aAngle)
michael@0 446 {
michael@0 447 mat4.rotateX(this.mvMatrix, aAngle);
michael@0 448 },
michael@0 449
michael@0 450 /**
michael@0 451 * Rotates the model view by a specified angle on the y axis.
michael@0 452 *
michael@0 453 * @param {Number} aAngle
michael@0 454 * the angle expressed in radians
michael@0 455 */
michael@0 456 rotateY: function TGLR_rotateY(aAngle)
michael@0 457 {
michael@0 458 mat4.rotateY(this.mvMatrix, aAngle);
michael@0 459 },
michael@0 460
michael@0 461 /**
michael@0 462 * Rotates the model view by a specified angle on the z axis.
michael@0 463 *
michael@0 464 * @param {Number} aAngle
michael@0 465 * the angle expressed in radians
michael@0 466 */
michael@0 467 rotateZ: function TGLR_rotateZ(aAngle)
michael@0 468 {
michael@0 469 mat4.rotateZ(this.mvMatrix, aAngle);
michael@0 470 },
michael@0 471
michael@0 472 /**
michael@0 473 * Scales the model view by the x, y and z coordinates.
michael@0 474 *
michael@0 475 * @param {Number} x
michael@0 476 * the x amount of scaling
michael@0 477 * @param {Number} y
michael@0 478 * the y amount of scaling
michael@0 479 * @param {Number} z
michael@0 480 * optional, the z amount of scaling
michael@0 481 */
michael@0 482 scale: function TGLR_scale(x, y, z)
michael@0 483 {
michael@0 484 mat4.scale(this.mvMatrix, [x, y, z || 1]);
michael@0 485 },
michael@0 486
michael@0 487 /**
michael@0 488 * Performs a custom interpolation between two matrices.
michael@0 489 * The result is saved in the first operand.
michael@0 490 *
michael@0 491 * @param {Array} aMat
michael@0 492 * the first matrix
michael@0 493 * @param {Array} aMat2
michael@0 494 * the second matrix
michael@0 495 * @param {Number} aLerp
michael@0 496 * interpolation amount between the two inputs
michael@0 497 * @param {Number} aDamping
michael@0 498 * optional, scalar adjusting the interpolation amortization
michael@0 499 * @param {Number} aBalance
michael@0 500 * optional, scalar adjusting the interpolation shift ammount
michael@0 501 */
michael@0 502 lerp: function TGLR_lerp(aMat, aMat2, aLerp, aDamping, aBalance)
michael@0 503 {
michael@0 504 if (aLerp < 0 || aLerp > 1) {
michael@0 505 return;
michael@0 506 }
michael@0 507
michael@0 508 // calculate the interpolation factor based on the damping and step
michael@0 509 let f = Math.pow(1 - Math.pow(aLerp, aDamping || 1), 1 / aBalance || 1);
michael@0 510
michael@0 511 // interpolate each element from the two matrices
michael@0 512 for (let i = 0, len = this.projMatrix.length; i < len; i++) {
michael@0 513 aMat[i] = aMat[i] + f * (aMat2[i] - aMat[i]);
michael@0 514 }
michael@0 515 },
michael@0 516
michael@0 517 /**
michael@0 518 * Resets the drawing style to default.
michael@0 519 */
michael@0 520 defaults: function TGLR_defaults()
michael@0 521 {
michael@0 522 this.depthTest(true);
michael@0 523 this.stencilTest(false);
michael@0 524 this.cullFace(false);
michael@0 525 this.frontFace("ccw");
michael@0 526 this.blendMode("alpha");
michael@0 527 this.fill([1, 1, 1, 1]);
michael@0 528 this.stroke([0, 0, 0, 1]);
michael@0 529 this.strokeWeight(1);
michael@0 530 this.perspective();
michael@0 531 this.origin();
michael@0 532 },
michael@0 533
michael@0 534 /**
michael@0 535 * Draws a quad composed of four vertices.
michael@0 536 * Vertices must be in clockwise order, or else drawing will be distorted.
michael@0 537 * Do not abuse this function, it is quite slow.
michael@0 538 *
michael@0 539 * @param {Array} aV0
michael@0 540 * the [x, y, z] position of the first triangle point
michael@0 541 * @param {Array} aV1
michael@0 542 * the [x, y, z] position of the second triangle point
michael@0 543 * @param {Array} aV2
michael@0 544 * the [x, y, z] position of the third triangle point
michael@0 545 * @param {Array} aV3
michael@0 546 * the [x, y, z] position of the fourth triangle point
michael@0 547 */
michael@0 548 quad: function TGLR_quad(aV0, aV1, aV2, aV3)
michael@0 549 {
michael@0 550 let gl = this.context;
michael@0 551 let fill = this._fillColor;
michael@0 552 let stroke = this._strokeColor;
michael@0 553 let vert = new TiltGL.VertexBuffer(gl, [aV0[0], aV0[1], aV0[2] || 0,
michael@0 554 aV1[0], aV1[1], aV1[2] || 0,
michael@0 555 aV2[0], aV2[1], aV2[2] || 0,
michael@0 556 aV3[0], aV3[1], aV3[2] || 0], 3);
michael@0 557
michael@0 558 // use the necessary shader and draw the vertices
michael@0 559 this.useColorShader(vert, fill);
michael@0 560 this.drawVertices(gl.TRIANGLE_FAN, vert.numItems);
michael@0 561
michael@0 562 this.useColorShader(vert, stroke);
michael@0 563 this.drawVertices(gl.LINE_LOOP, vert.numItems);
michael@0 564
michael@0 565 TiltUtils.destroyObject(vert);
michael@0 566 },
michael@0 567
michael@0 568 /**
michael@0 569 * Function called when this object is destroyed.
michael@0 570 */
michael@0 571 finalize: function TGLR_finalize()
michael@0 572 {
michael@0 573 if (this.context) {
michael@0 574 TiltUtils.destroyObject(this._colorShader);
michael@0 575 }
michael@0 576 }
michael@0 577 };
michael@0 578
michael@0 579 /**
michael@0 580 * Creates a vertex buffer containing an array of elements.
michael@0 581 *
michael@0 582 * @param {Object} aContext
michael@0 583 * a WebGL context
michael@0 584 * @param {Array} aElementsArray
michael@0 585 * an array of numbers (floats)
michael@0 586 * @param {Number} aItemSize
michael@0 587 * how many items create a block
michael@0 588 * @param {Number} aNumItems
michael@0 589 * optional, how many items to use from the array
michael@0 590 */
michael@0 591 TiltGL.VertexBuffer = function TGL_VertexBuffer(
michael@0 592 aContext, aElementsArray, aItemSize, aNumItems)
michael@0 593 {
michael@0 594 /**
michael@0 595 * The parent WebGL context.
michael@0 596 */
michael@0 597 this._context = aContext;
michael@0 598
michael@0 599 /**
michael@0 600 * The array buffer.
michael@0 601 */
michael@0 602 this._ref = null;
michael@0 603
michael@0 604 /**
michael@0 605 * Array of number components contained in the buffer.
michael@0 606 */
michael@0 607 this.components = null;
michael@0 608
michael@0 609 /**
michael@0 610 * Variables defining the internal structure of the buffer.
michael@0 611 */
michael@0 612 this.itemSize = 0;
michael@0 613 this.numItems = 0;
michael@0 614
michael@0 615 // if the array is specified in the constructor, initialize directly
michael@0 616 if (aElementsArray) {
michael@0 617 this.initBuffer(aElementsArray, aItemSize, aNumItems);
michael@0 618 }
michael@0 619 };
michael@0 620
michael@0 621 TiltGL.VertexBuffer.prototype = {
michael@0 622
michael@0 623 /**
michael@0 624 * Initializes buffer data to be used for drawing, using an array of floats.
michael@0 625 * The "aNumItems" param can be specified to use only a portion of the array.
michael@0 626 *
michael@0 627 * @param {Array} aElementsArray
michael@0 628 * an array of floats
michael@0 629 * @param {Number} aItemSize
michael@0 630 * how many items create a block
michael@0 631 * @param {Number} aNumItems
michael@0 632 * optional, how many items to use from the array
michael@0 633 */
michael@0 634 initBuffer: function TGLVB_initBuffer(aElementsArray, aItemSize, aNumItems)
michael@0 635 {
michael@0 636 let gl = this._context;
michael@0 637
michael@0 638 // the aNumItems parameter is optional, we can compute it if not specified
michael@0 639 aNumItems = aNumItems || aElementsArray.length / aItemSize;
michael@0 640
michael@0 641 // create the Float32Array using the elements array
michael@0 642 this.components = new Float32Array(aElementsArray);
michael@0 643
michael@0 644 // create an array buffer and bind the elements as a Float32Array
michael@0 645 this._ref = gl.createBuffer();
michael@0 646 gl.bindBuffer(gl.ARRAY_BUFFER, this._ref);
michael@0 647 gl.bufferData(gl.ARRAY_BUFFER, this.components, gl.STATIC_DRAW);
michael@0 648
michael@0 649 // remember some properties, useful when binding the buffer to a shader
michael@0 650 this.itemSize = aItemSize;
michael@0 651 this.numItems = aNumItems;
michael@0 652 },
michael@0 653
michael@0 654 /**
michael@0 655 * Function called when this object is destroyed.
michael@0 656 */
michael@0 657 finalize: function TGLVB_finalize()
michael@0 658 {
michael@0 659 if (this._context) {
michael@0 660 this._context.deleteBuffer(this._ref);
michael@0 661 }
michael@0 662 }
michael@0 663 };
michael@0 664
michael@0 665 /**
michael@0 666 * Creates an index buffer containing an array of indices.
michael@0 667 *
michael@0 668 * @param {Object} aContext
michael@0 669 * a WebGL context
michael@0 670 * @param {Array} aElementsArray
michael@0 671 * an array of unsigned integers
michael@0 672 * @param {Number} aNumItems
michael@0 673 * optional, how many items to use from the array
michael@0 674 */
michael@0 675 TiltGL.IndexBuffer = function TGL_IndexBuffer(
michael@0 676 aContext, aElementsArray, aNumItems)
michael@0 677 {
michael@0 678 /**
michael@0 679 * The parent WebGL context.
michael@0 680 */
michael@0 681 this._context = aContext;
michael@0 682
michael@0 683 /**
michael@0 684 * The element array buffer.
michael@0 685 */
michael@0 686 this._ref = null;
michael@0 687
michael@0 688 /**
michael@0 689 * Array of number components contained in the buffer.
michael@0 690 */
michael@0 691 this.components = null;
michael@0 692
michael@0 693 /**
michael@0 694 * Variables defining the internal structure of the buffer.
michael@0 695 */
michael@0 696 this.itemSize = 0;
michael@0 697 this.numItems = 0;
michael@0 698
michael@0 699 // if the array is specified in the constructor, initialize directly
michael@0 700 if (aElementsArray) {
michael@0 701 this.initBuffer(aElementsArray, aNumItems);
michael@0 702 }
michael@0 703 };
michael@0 704
michael@0 705 TiltGL.IndexBuffer.prototype = {
michael@0 706
michael@0 707 /**
michael@0 708 * Initializes a buffer of vertex indices, using an array of unsigned ints.
michael@0 709 * The item size will automatically default to 1, and the "numItems" will be
michael@0 710 * equal to the number of items in the array if not specified.
michael@0 711 *
michael@0 712 * @param {Array} aElementsArray
michael@0 713 * an array of numbers (unsigned integers)
michael@0 714 * @param {Number} aNumItems
michael@0 715 * optional, how many items to use from the array
michael@0 716 */
michael@0 717 initBuffer: function TGLIB_initBuffer(aElementsArray, aNumItems)
michael@0 718 {
michael@0 719 let gl = this._context;
michael@0 720
michael@0 721 // the aNumItems parameter is optional, we can compute it if not specified
michael@0 722 aNumItems = aNumItems || aElementsArray.length;
michael@0 723
michael@0 724 // create the Uint16Array using the elements array
michael@0 725 this.components = new Uint16Array(aElementsArray);
michael@0 726
michael@0 727 // create an array buffer and bind the elements as a Uint16Array
michael@0 728 this._ref = gl.createBuffer();
michael@0 729 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this._ref);
michael@0 730 gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.components, gl.STATIC_DRAW);
michael@0 731
michael@0 732 // remember some properties, useful when binding the buffer to a shader
michael@0 733 this.itemSize = 1;
michael@0 734 this.numItems = aNumItems;
michael@0 735 },
michael@0 736
michael@0 737 /**
michael@0 738 * Function called when this object is destroyed.
michael@0 739 */
michael@0 740 finalize: function TGLIB_finalize()
michael@0 741 {
michael@0 742 if (this._context) {
michael@0 743 this._context.deleteBuffer(this._ref);
michael@0 744 }
michael@0 745 }
michael@0 746 };
michael@0 747
michael@0 748 /**
michael@0 749 * A program is composed of a vertex and a fragment shader.
michael@0 750 *
michael@0 751 * @param {Object} aProperties
michael@0 752 * optional, an object containing the following properties:
michael@0 753 * {String} vs: the vertex shader source code
michael@0 754 * {String} fs: the fragment shader source code
michael@0 755 * {Array} attributes: an array of attributes as strings
michael@0 756 * {Array} uniforms: an array of uniforms as strings
michael@0 757 */
michael@0 758 TiltGL.Program = function(aContext, aProperties)
michael@0 759 {
michael@0 760 // make sure the properties parameter is a valid object
michael@0 761 aProperties = aProperties || {};
michael@0 762
michael@0 763 /**
michael@0 764 * The parent WebGL context.
michael@0 765 */
michael@0 766 this._context = aContext;
michael@0 767
michael@0 768 /**
michael@0 769 * A reference to the actual GLSL program.
michael@0 770 */
michael@0 771 this._ref = null;
michael@0 772
michael@0 773 /**
michael@0 774 * Each program has an unique id assigned.
michael@0 775 */
michael@0 776 this._id = -1;
michael@0 777
michael@0 778 /**
michael@0 779 * Two arrays: an attributes array, containing all the cached attributes
michael@0 780 * and a uniforms array, containing all the cached uniforms.
michael@0 781 */
michael@0 782 this._attributes = null;
michael@0 783 this._uniforms = null;
michael@0 784
michael@0 785 // if the sources are specified in the constructor, initialize directly
michael@0 786 if (aProperties.vs && aProperties.fs) {
michael@0 787 this.initProgram(aProperties);
michael@0 788 }
michael@0 789 };
michael@0 790
michael@0 791 TiltGL.Program.prototype = {
michael@0 792
michael@0 793 /**
michael@0 794 * Initializes a shader program, using specified source code as strings.
michael@0 795 *
michael@0 796 * @param {Object} aProperties
michael@0 797 * an object containing the following properties:
michael@0 798 * {String} vs: the vertex shader source code
michael@0 799 * {String} fs: the fragment shader source code
michael@0 800 * {Array} attributes: an array of attributes as strings
michael@0 801 * {Array} uniforms: an array of uniforms as strings
michael@0 802 */
michael@0 803 initProgram: function TGLP_initProgram(aProperties)
michael@0 804 {
michael@0 805 this._ref = TiltGL.ProgramUtils.create(this._context, aProperties);
michael@0 806
michael@0 807 // cache for faster access
michael@0 808 this._id = this._ref.id;
michael@0 809 this._attributes = this._ref.attributes;
michael@0 810 this._uniforms = this._ref.uniforms;
michael@0 811
michael@0 812 // cleanup
michael@0 813 delete this._ref.id;
michael@0 814 delete this._ref.attributes;
michael@0 815 delete this._ref.uniforms;
michael@0 816 },
michael@0 817
michael@0 818 /**
michael@0 819 * Uses the shader program as current one for the WebGL context; it also
michael@0 820 * enables vertex attributes necessary to enable when using this program.
michael@0 821 * This method also does some useful caching, as the function "useProgram"
michael@0 822 * could take quite a lot of time.
michael@0 823 */
michael@0 824 use: function TGLP_use()
michael@0 825 {
michael@0 826 let id = this._id;
michael@0 827 let utils = TiltGL.ProgramUtils;
michael@0 828
michael@0 829 // check if the program wasn't already active
michael@0 830 if (utils._activeProgram !== id) {
michael@0 831 utils._activeProgram = id;
michael@0 832
michael@0 833 // use the the program if it wasn't already set
michael@0 834 this._context.useProgram(this._ref);
michael@0 835 this.cleanupVertexAttrib();
michael@0 836
michael@0 837 // enable any necessary vertex attributes using the cache
michael@0 838 for each (let attribute in this._attributes) {
michael@0 839 this._context.enableVertexAttribArray(attribute);
michael@0 840 utils._enabledAttributes.push(attribute);
michael@0 841 }
michael@0 842 }
michael@0 843 },
michael@0 844
michael@0 845 /**
michael@0 846 * Disables all currently enabled vertex attribute arrays.
michael@0 847 */
michael@0 848 cleanupVertexAttrib: function TGLP_cleanupVertexAttrib()
michael@0 849 {
michael@0 850 let utils = TiltGL.ProgramUtils;
michael@0 851
michael@0 852 for each (let attribute in utils._enabledAttributes) {
michael@0 853 this._context.disableVertexAttribArray(attribute);
michael@0 854 }
michael@0 855 utils._enabledAttributes = [];
michael@0 856 },
michael@0 857
michael@0 858 /**
michael@0 859 * Binds a vertex buffer as an array buffer for a specific shader attribute.
michael@0 860 *
michael@0 861 * @param {String} aAtribute
michael@0 862 * the attribute name obtained from the shader
michael@0 863 * @param {Float32Array} aBuffer
michael@0 864 * the buffer to be bound
michael@0 865 */
michael@0 866 bindVertexBuffer: function TGLP_bindVertexBuffer(aAtribute, aBuffer)
michael@0 867 {
michael@0 868 // get the cached attribute value from the shader
michael@0 869 let gl = this._context;
michael@0 870 let attr = this._attributes[aAtribute];
michael@0 871 let size = aBuffer.itemSize;
michael@0 872
michael@0 873 gl.bindBuffer(gl.ARRAY_BUFFER, aBuffer._ref);
michael@0 874 gl.vertexAttribPointer(attr, size, gl.FLOAT, false, 0, 0);
michael@0 875 },
michael@0 876
michael@0 877 /**
michael@0 878 * Binds a uniform matrix to the current shader.
michael@0 879 *
michael@0 880 * @param {String} aUniform
michael@0 881 * the uniform name to bind the variable to
michael@0 882 * @param {Float32Array} m
michael@0 883 * the matrix to be bound
michael@0 884 */
michael@0 885 bindUniformMatrix: function TGLP_bindUniformMatrix(aUniform, m)
michael@0 886 {
michael@0 887 this._context.uniformMatrix4fv(this._uniforms[aUniform], false, m);
michael@0 888 },
michael@0 889
michael@0 890 /**
michael@0 891 * Binds a uniform vector of 4 elements to the current shader.
michael@0 892 *
michael@0 893 * @param {String} aUniform
michael@0 894 * the uniform name to bind the variable to
michael@0 895 * @param {Float32Array} v
michael@0 896 * the vector to be bound
michael@0 897 */
michael@0 898 bindUniformVec4: function TGLP_bindUniformVec4(aUniform, v)
michael@0 899 {
michael@0 900 this._context.uniform4fv(this._uniforms[aUniform], v);
michael@0 901 },
michael@0 902
michael@0 903 /**
michael@0 904 * Binds a simple float element to the current shader.
michael@0 905 *
michael@0 906 * @param {String} aUniform
michael@0 907 * the uniform name to bind the variable to
michael@0 908 * @param {Number} v
michael@0 909 * the variable to be bound
michael@0 910 */
michael@0 911 bindUniformFloat: function TGLP_bindUniformFloat(aUniform, f)
michael@0 912 {
michael@0 913 this._context.uniform1f(this._uniforms[aUniform], f);
michael@0 914 },
michael@0 915
michael@0 916 /**
michael@0 917 * Binds a uniform texture for a sampler to the current shader.
michael@0 918 *
michael@0 919 * @param {String} aSampler
michael@0 920 * the sampler name to bind the texture to
michael@0 921 * @param {TiltGL.Texture} aTexture
michael@0 922 * the texture to be bound
michael@0 923 */
michael@0 924 bindTexture: function TGLP_bindTexture(aSampler, aTexture)
michael@0 925 {
michael@0 926 let gl = this._context;
michael@0 927
michael@0 928 gl.activeTexture(gl.TEXTURE0);
michael@0 929 gl.bindTexture(gl.TEXTURE_2D, aTexture._ref);
michael@0 930 gl.uniform1i(this._uniforms[aSampler], 0);
michael@0 931 },
michael@0 932
michael@0 933 /**
michael@0 934 * Function called when this object is destroyed.
michael@0 935 */
michael@0 936 finalize: function TGLP_finalize()
michael@0 937 {
michael@0 938 if (this._context) {
michael@0 939 this._context.useProgram(null);
michael@0 940 this._context.deleteProgram(this._ref);
michael@0 941 }
michael@0 942 }
michael@0 943 };
michael@0 944
michael@0 945 /**
michael@0 946 * Utility functions for handling GLSL shaders and programs.
michael@0 947 */
michael@0 948 TiltGL.ProgramUtils = {
michael@0 949
michael@0 950 /**
michael@0 951 * Initializes a shader program, using specified source code as strings,
michael@0 952 * returning the newly created shader program, by compiling and linking the
michael@0 953 * vertex and fragment shader.
michael@0 954 *
michael@0 955 * @param {Object} aContext
michael@0 956 * a WebGL context
michael@0 957 * @param {Object} aProperties
michael@0 958 * an object containing the following properties:
michael@0 959 * {String} vs: the vertex shader source code
michael@0 960 * {String} fs: the fragment shader source code
michael@0 961 * {Array} attributes: an array of attributes as strings
michael@0 962 * {Array} uniforms: an array of uniforms as strings
michael@0 963 */
michael@0 964 create: function TGLPU_create(aContext, aProperties)
michael@0 965 {
michael@0 966 // make sure the properties parameter is a valid object
michael@0 967 aProperties = aProperties || {};
michael@0 968
michael@0 969 // compile the two shaders
michael@0 970 let vertShader = this.compile(aContext, aProperties.vs, "vertex");
michael@0 971 let fragShader = this.compile(aContext, aProperties.fs, "fragment");
michael@0 972 let program = this.link(aContext, vertShader, fragShader);
michael@0 973
michael@0 974 aContext.deleteShader(vertShader);
michael@0 975 aContext.deleteShader(fragShader);
michael@0 976
michael@0 977 return this.cache(aContext, aProperties, program);
michael@0 978 },
michael@0 979
michael@0 980 /**
michael@0 981 * Compiles a shader source of a specific type, either vertex or fragment.
michael@0 982 *
michael@0 983 * @param {Object} aContext
michael@0 984 * a WebGL context
michael@0 985 * @param {String} aShaderSource
michael@0 986 * the source code for the shader
michael@0 987 * @param {String} aShaderType
michael@0 988 * the shader type ("vertex" or "fragment")
michael@0 989 *
michael@0 990 * @return {WebGLShader} the compiled shader
michael@0 991 */
michael@0 992 compile: function TGLPU_compile(aContext, aShaderSource, aShaderType)
michael@0 993 {
michael@0 994 let gl = aContext, shader, status;
michael@0 995
michael@0 996 // make sure the shader source is valid
michael@0 997 if ("string" !== typeof aShaderSource || aShaderSource.length < 1) {
michael@0 998 TiltUtils.Output.error(
michael@0 999 TiltUtils.L10n.get("compileShader.source.error"));
michael@0 1000 return null;
michael@0 1001 }
michael@0 1002
michael@0 1003 // also make sure the necessary shader mime type is valid
michael@0 1004 if (aShaderType === "vertex") {
michael@0 1005 shader = gl.createShader(gl.VERTEX_SHADER);
michael@0 1006 } else if (aShaderType === "fragment") {
michael@0 1007 shader = gl.createShader(gl.FRAGMENT_SHADER);
michael@0 1008 } else {
michael@0 1009 TiltUtils.Output.error(
michael@0 1010 TiltUtils.L10n.format("compileShader.type.error", [aShaderSource]));
michael@0 1011 return null;
michael@0 1012 }
michael@0 1013
michael@0 1014 // set the shader source and compile it
michael@0 1015 gl.shaderSource(shader, aShaderSource);
michael@0 1016 gl.compileShader(shader);
michael@0 1017
michael@0 1018 // remember the shader source (useful for debugging and caching)
michael@0 1019 shader.src = aShaderSource;
michael@0 1020
michael@0 1021 // verify the compile status; if something went wrong, log the error
michael@0 1022 if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
michael@0 1023 status = gl.getShaderInfoLog(shader);
michael@0 1024
michael@0 1025 TiltUtils.Output.error(
michael@0 1026 TiltUtils.L10n.format("compileShader.compile.error", [status]));
michael@0 1027 return null;
michael@0 1028 }
michael@0 1029
michael@0 1030 // return the newly compiled shader from the specified source
michael@0 1031 return shader;
michael@0 1032 },
michael@0 1033
michael@0 1034 /**
michael@0 1035 * Links two compiled vertex or fragment shaders together to form a program.
michael@0 1036 *
michael@0 1037 * @param {Object} aContext
michael@0 1038 * a WebGL context
michael@0 1039 * @param {WebGLShader} aVertShader
michael@0 1040 * the compiled vertex shader
michael@0 1041 * @param {WebGLShader} aFragShader
michael@0 1042 * the compiled fragment shader
michael@0 1043 *
michael@0 1044 * @return {WebGLProgram} the newly created and linked shader program
michael@0 1045 */
michael@0 1046 link: function TGLPU_link(aContext, aVertShader, aFragShader)
michael@0 1047 {
michael@0 1048 let gl = aContext, program, status;
michael@0 1049
michael@0 1050 // create a program and attach the compiled vertex and fragment shaders
michael@0 1051 program = gl.createProgram();
michael@0 1052
michael@0 1053 // attach the vertex and fragment shaders to the program
michael@0 1054 gl.attachShader(program, aVertShader);
michael@0 1055 gl.attachShader(program, aFragShader);
michael@0 1056 gl.linkProgram(program);
michael@0 1057
michael@0 1058 // verify the link status; if something went wrong, log the error
michael@0 1059 if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
michael@0 1060 status = gl.getProgramInfoLog(program);
michael@0 1061
michael@0 1062 TiltUtils.Output.error(
michael@0 1063 TiltUtils.L10n.format("linkProgram.error", [status]));
michael@0 1064 return null;
michael@0 1065 }
michael@0 1066
michael@0 1067 // generate an id for the program
michael@0 1068 program.id = this._count++;
michael@0 1069
michael@0 1070 return program;
michael@0 1071 },
michael@0 1072
michael@0 1073 /**
michael@0 1074 * Caches shader attributes and uniforms as properties for a program object.
michael@0 1075 *
michael@0 1076 * @param {Object} aContext
michael@0 1077 * a WebGL context
michael@0 1078 * @param {Object} aProperties
michael@0 1079 * an object containing the following properties:
michael@0 1080 * {Array} attributes: optional, an array of attributes as strings
michael@0 1081 * {Array} uniforms: optional, an array of uniforms as strings
michael@0 1082 * @param {WebGLProgram} aProgram
michael@0 1083 * the shader program used for caching
michael@0 1084 *
michael@0 1085 * @return {WebGLProgram} the same program
michael@0 1086 */
michael@0 1087 cache: function TGLPU_cache(aContext, aProperties, aProgram)
michael@0 1088 {
michael@0 1089 // make sure the properties parameter is a valid object
michael@0 1090 aProperties = aProperties || {};
michael@0 1091
michael@0 1092 // make sure the attributes and uniforms cache objects are created
michael@0 1093 aProgram.attributes = {};
michael@0 1094 aProgram.uniforms = {};
michael@0 1095
michael@0 1096 Object.defineProperty(aProgram.attributes, "length",
michael@0 1097 { value: 0, writable: true, enumerable: false, configurable: true });
michael@0 1098
michael@0 1099 Object.defineProperty(aProgram.uniforms, "length",
michael@0 1100 { value: 0, writable: true, enumerable: false, configurable: true });
michael@0 1101
michael@0 1102
michael@0 1103 let attr = aProperties.attributes;
michael@0 1104 let unif = aProperties.uniforms;
michael@0 1105
michael@0 1106 if (attr) {
michael@0 1107 for (let i = 0, len = attr.length; i < len; i++) {
michael@0 1108 // try to get a shader attribute from the program
michael@0 1109 let param = attr[i];
michael@0 1110 let loc = aContext.getAttribLocation(aProgram, param);
michael@0 1111
michael@0 1112 if ("number" === typeof loc && loc > -1) {
michael@0 1113 // if we get an attribute location, store it
michael@0 1114 // bind the new parameter only if it was not already defined
michael@0 1115 if (aProgram.attributes[param] === undefined) {
michael@0 1116 aProgram.attributes[param] = loc;
michael@0 1117 aProgram.attributes.length++;
michael@0 1118 }
michael@0 1119 }
michael@0 1120 }
michael@0 1121 }
michael@0 1122
michael@0 1123 if (unif) {
michael@0 1124 for (let i = 0, len = unif.length; i < len; i++) {
michael@0 1125 // try to get a shader uniform from the program
michael@0 1126 let param = unif[i];
michael@0 1127 let loc = aContext.getUniformLocation(aProgram, param);
michael@0 1128
michael@0 1129 if ("object" === typeof loc && loc) {
michael@0 1130 // if we get a uniform object, store it
michael@0 1131 // bind the new parameter only if it was not already defined
michael@0 1132 if (aProgram.uniforms[param] === undefined) {
michael@0 1133 aProgram.uniforms[param] = loc;
michael@0 1134 aProgram.uniforms.length++;
michael@0 1135 }
michael@0 1136 }
michael@0 1137 }
michael@0 1138 }
michael@0 1139
michael@0 1140 return aProgram;
michael@0 1141 },
michael@0 1142
michael@0 1143 /**
michael@0 1144 * The total number of programs created.
michael@0 1145 */
michael@0 1146 _count: 0,
michael@0 1147
michael@0 1148 /**
michael@0 1149 * Represents the current active shader, identified by an id.
michael@0 1150 */
michael@0 1151 _activeProgram: -1,
michael@0 1152
michael@0 1153 /**
michael@0 1154 * Represents the current enabled attributes.
michael@0 1155 */
michael@0 1156 _enabledAttributes: []
michael@0 1157 };
michael@0 1158
michael@0 1159 /**
michael@0 1160 * This constructor creates a texture from an Image.
michael@0 1161 *
michael@0 1162 * @param {Object} aContext
michael@0 1163 * a WebGL context
michael@0 1164 * @param {Object} aProperties
michael@0 1165 * optional, an object containing the following properties:
michael@0 1166 * {Image} source: the source image for the texture
michael@0 1167 * {String} format: the format of the texture ("RGB" or "RGBA")
michael@0 1168 * {String} fill: optional, color to fill the transparent bits
michael@0 1169 * {String} stroke: optional, color to draw an outline
michael@0 1170 * {Number} strokeWeight: optional, the width of the outline
michael@0 1171 * {String} minFilter: either "nearest" or "linear"
michael@0 1172 * {String} magFilter: either "nearest" or "linear"
michael@0 1173 * {String} wrapS: either "repeat" or "clamp"
michael@0 1174 * {String} wrapT: either "repeat" or "clamp"
michael@0 1175 * {Boolean} mipmap: true if should generate mipmap
michael@0 1176 */
michael@0 1177 TiltGL.Texture = function(aContext, aProperties)
michael@0 1178 {
michael@0 1179 // make sure the properties parameter is a valid object
michael@0 1180 aProperties = aProperties || {};
michael@0 1181
michael@0 1182 /**
michael@0 1183 * The parent WebGL context.
michael@0 1184 */
michael@0 1185 this._context = aContext;
michael@0 1186
michael@0 1187 /**
michael@0 1188 * A reference to the WebGL texture object.
michael@0 1189 */
michael@0 1190 this._ref = null;
michael@0 1191
michael@0 1192 /**
michael@0 1193 * Each texture has an unique id assigned.
michael@0 1194 */
michael@0 1195 this._id = -1;
michael@0 1196
michael@0 1197 /**
michael@0 1198 * Variables specifying the width and height of the texture.
michael@0 1199 * If these values are less than 0, the texture hasn't loaded yet.
michael@0 1200 */
michael@0 1201 this.width = -1;
michael@0 1202 this.height = -1;
michael@0 1203
michael@0 1204 /**
michael@0 1205 * Specifies if the texture has loaded or not.
michael@0 1206 */
michael@0 1207 this.loaded = false;
michael@0 1208
michael@0 1209 // if the image is specified in the constructor, initialize directly
michael@0 1210 if ("object" === typeof aProperties.source) {
michael@0 1211 this.initTexture(aProperties);
michael@0 1212 } else {
michael@0 1213 TiltUtils.Output.error(
michael@0 1214 TiltUtils.L10n.get("initTexture.source.error"));
michael@0 1215 }
michael@0 1216 };
michael@0 1217
michael@0 1218 TiltGL.Texture.prototype = {
michael@0 1219
michael@0 1220 /**
michael@0 1221 * Initializes a texture from a pre-existing image or canvas.
michael@0 1222 *
michael@0 1223 * @param {Image} aImage
michael@0 1224 * the source image or canvas
michael@0 1225 * @param {Object} aProperties
michael@0 1226 * an object containing the following properties:
michael@0 1227 * {Image} source: the source image for the texture
michael@0 1228 * {String} format: the format of the texture ("RGB" or "RGBA")
michael@0 1229 * {String} fill: optional, color to fill the transparent bits
michael@0 1230 * {String} stroke: optional, color to draw an outline
michael@0 1231 * {Number} strokeWeight: optional, the width of the outline
michael@0 1232 * {String} minFilter: either "nearest" or "linear"
michael@0 1233 * {String} magFilter: either "nearest" or "linear"
michael@0 1234 * {String} wrapS: either "repeat" or "clamp"
michael@0 1235 * {String} wrapT: either "repeat" or "clamp"
michael@0 1236 * {Boolean} mipmap: true if should generate mipmap
michael@0 1237 */
michael@0 1238 initTexture: function TGLT_initTexture(aProperties)
michael@0 1239 {
michael@0 1240 this._ref = TiltGL.TextureUtils.create(this._context, aProperties);
michael@0 1241
michael@0 1242 // cache for faster access
michael@0 1243 this._id = this._ref.id;
michael@0 1244 this.width = this._ref.width;
michael@0 1245 this.height = this._ref.height;
michael@0 1246 this.loaded = true;
michael@0 1247
michael@0 1248 // cleanup
michael@0 1249 delete this._ref.id;
michael@0 1250 delete this._ref.width;
michael@0 1251 delete this._ref.height;
michael@0 1252 delete this.onload;
michael@0 1253 },
michael@0 1254
michael@0 1255 /**
michael@0 1256 * Function called when this object is destroyed.
michael@0 1257 */
michael@0 1258 finalize: function TGLT_finalize()
michael@0 1259 {
michael@0 1260 if (this._context) {
michael@0 1261 this._context.deleteTexture(this._ref);
michael@0 1262 }
michael@0 1263 }
michael@0 1264 };
michael@0 1265
michael@0 1266 /**
michael@0 1267 * Utility functions for creating and manipulating textures.
michael@0 1268 */
michael@0 1269 TiltGL.TextureUtils = {
michael@0 1270
michael@0 1271 /**
michael@0 1272 * Initializes a texture from a pre-existing image or canvas.
michael@0 1273 *
michael@0 1274 * @param {Object} aContext
michael@0 1275 * a WebGL context
michael@0 1276 * @param {Image} aImage
michael@0 1277 * the source image or canvas
michael@0 1278 * @param {Object} aProperties
michael@0 1279 * an object containing some of the following properties:
michael@0 1280 * {Image} source: the source image for the texture
michael@0 1281 * {String} format: the format of the texture ("RGB" or "RGBA")
michael@0 1282 * {String} fill: optional, color to fill the transparent bits
michael@0 1283 * {String} stroke: optional, color to draw an outline
michael@0 1284 * {Number} strokeWeight: optional, the width of the outline
michael@0 1285 * {String} minFilter: either "nearest" or "linear"
michael@0 1286 * {String} magFilter: either "nearest" or "linear"
michael@0 1287 * {String} wrapS: either "repeat" or "clamp"
michael@0 1288 * {String} wrapT: either "repeat" or "clamp"
michael@0 1289 * {Boolean} mipmap: true if should generate mipmap
michael@0 1290 *
michael@0 1291 * @return {WebGLTexture} the created texture
michael@0 1292 */
michael@0 1293 create: function TGLTU_create(aContext, aProperties)
michael@0 1294 {
michael@0 1295 // make sure the properties argument is an object
michael@0 1296 aProperties = aProperties || {};
michael@0 1297
michael@0 1298 if (!aProperties.source) {
michael@0 1299 return null;
michael@0 1300 }
michael@0 1301
michael@0 1302 let gl = aContext;
michael@0 1303 let width = aProperties.source.width;
michael@0 1304 let height = aProperties.source.height;
michael@0 1305 let format = gl[aProperties.format || "RGB"];
michael@0 1306
michael@0 1307 // make sure the image is power of two before binding to a texture
michael@0 1308 let source = this.resizeImageToPowerOfTwo(aProperties);
michael@0 1309
michael@0 1310 // first, create the texture to hold the image data
michael@0 1311 let texture = gl.createTexture();
michael@0 1312
michael@0 1313 // attach the image data to the newly create texture
michael@0 1314 gl.bindTexture(gl.TEXTURE_2D, texture);
michael@0 1315 gl.texImage2D(gl.TEXTURE_2D, 0, format, format, gl.UNSIGNED_BYTE, source);
michael@0 1316 this.setTextureParams(gl, aProperties);
michael@0 1317
michael@0 1318 // do some cleanup
michael@0 1319 gl.bindTexture(gl.TEXTURE_2D, null);
michael@0 1320
michael@0 1321 // remember the width and the height
michael@0 1322 texture.width = width;
michael@0 1323 texture.height = height;
michael@0 1324
michael@0 1325 // generate an id for the texture
michael@0 1326 texture.id = this._count++;
michael@0 1327
michael@0 1328 return texture;
michael@0 1329 },
michael@0 1330
michael@0 1331 /**
michael@0 1332 * Sets texture parameters for the current texture binding.
michael@0 1333 * Optionally, you can also (re)set the current texture binding manually.
michael@0 1334 *
michael@0 1335 * @param {Object} aContext
michael@0 1336 * a WebGL context
michael@0 1337 * @param {Object} aProperties
michael@0 1338 * an object containing the texture properties
michael@0 1339 */
michael@0 1340 setTextureParams: function TGLTU_setTextureParams(aContext, aProperties)
michael@0 1341 {
michael@0 1342 // make sure the properties argument is an object
michael@0 1343 aProperties = aProperties || {};
michael@0 1344
michael@0 1345 let gl = aContext;
michael@0 1346 let minFilter = gl.TEXTURE_MIN_FILTER;
michael@0 1347 let magFilter = gl.TEXTURE_MAG_FILTER;
michael@0 1348 let wrapS = gl.TEXTURE_WRAP_S;
michael@0 1349 let wrapT = gl.TEXTURE_WRAP_T;
michael@0 1350
michael@0 1351 // bind a new texture if necessary
michael@0 1352 if (aProperties.texture) {
michael@0 1353 gl.bindTexture(gl.TEXTURE_2D, aProperties.texture.ref);
michael@0 1354 }
michael@0 1355
michael@0 1356 // set the minification filter
michael@0 1357 if ("nearest" === aProperties.minFilter) {
michael@0 1358 gl.texParameteri(gl.TEXTURE_2D, minFilter, gl.NEAREST);
michael@0 1359 } else if ("linear" === aProperties.minFilter && aProperties.mipmap) {
michael@0 1360 gl.texParameteri(gl.TEXTURE_2D, minFilter, gl.LINEAR_MIPMAP_LINEAR);
michael@0 1361 } else {
michael@0 1362 gl.texParameteri(gl.TEXTURE_2D, minFilter, gl.LINEAR);
michael@0 1363 }
michael@0 1364
michael@0 1365 // set the magnification filter
michael@0 1366 if ("nearest" === aProperties.magFilter) {
michael@0 1367 gl.texParameteri(gl.TEXTURE_2D, magFilter, gl.NEAREST);
michael@0 1368 } else {
michael@0 1369 gl.texParameteri(gl.TEXTURE_2D, magFilter, gl.LINEAR);
michael@0 1370 }
michael@0 1371
michael@0 1372 // set the wrapping on the x-axis for the texture
michael@0 1373 if ("repeat" === aProperties.wrapS) {
michael@0 1374 gl.texParameteri(gl.TEXTURE_2D, wrapS, gl.REPEAT);
michael@0 1375 } else {
michael@0 1376 gl.texParameteri(gl.TEXTURE_2D, wrapS, gl.CLAMP_TO_EDGE);
michael@0 1377 }
michael@0 1378
michael@0 1379 // set the wrapping on the y-axis for the texture
michael@0 1380 if ("repeat" === aProperties.wrapT) {
michael@0 1381 gl.texParameteri(gl.TEXTURE_2D, wrapT, gl.REPEAT);
michael@0 1382 } else {
michael@0 1383 gl.texParameteri(gl.TEXTURE_2D, wrapT, gl.CLAMP_TO_EDGE);
michael@0 1384 }
michael@0 1385
michael@0 1386 // generate mipmap if necessary
michael@0 1387 if (aProperties.mipmap) {
michael@0 1388 gl.generateMipmap(gl.TEXTURE_2D);
michael@0 1389 }
michael@0 1390 },
michael@0 1391
michael@0 1392 /**
michael@0 1393 * This shim renders a content window to a canvas element, but clamps the
michael@0 1394 * maximum width and height of the canvas to the WebGL MAX_TEXTURE_SIZE.
michael@0 1395 *
michael@0 1396 * @param {Window} aContentWindow
michael@0 1397 * the content window to get a texture from
michael@0 1398 * @param {Number} aMaxImageSize
michael@0 1399 * the maximum image size to be used
michael@0 1400 *
michael@0 1401 * @return {Image} the new content window image
michael@0 1402 */
michael@0 1403 createContentImage: function TGLTU_createContentImage(
michael@0 1404 aContentWindow, aMaxImageSize)
michael@0 1405 {
michael@0 1406 // calculate the total width and height of the content page
michael@0 1407 let size = TiltUtils.DOM.getContentWindowDimensions(aContentWindow);
michael@0 1408
michael@0 1409 // use a custom canvas element and a 2d context to draw the window
michael@0 1410 let canvas = TiltUtils.DOM.initCanvas(null);
michael@0 1411 canvas.width = TiltMath.clamp(size.width, 0, aMaxImageSize);
michael@0 1412 canvas.height = TiltMath.clamp(size.height, 0, aMaxImageSize);
michael@0 1413
michael@0 1414 // use the 2d context.drawWindow() magic
michael@0 1415 let ctx = canvas.getContext("2d");
michael@0 1416 ctx.drawWindow(aContentWindow, 0, 0, canvas.width, canvas.height, "#fff");
michael@0 1417
michael@0 1418 return canvas;
michael@0 1419 },
michael@0 1420
michael@0 1421 /**
michael@0 1422 * Scales an image's width and height to next power of two.
michael@0 1423 * If the image already has power of two sizes, it is immediately returned,
michael@0 1424 * otherwise, a new image is created.
michael@0 1425 *
michael@0 1426 * @param {Image} aImage
michael@0 1427 * the image to be scaled
michael@0 1428 * @param {Object} aProperties
michael@0 1429 * an object containing the following properties:
michael@0 1430 * {Image} source: the source image to resize
michael@0 1431 * {Boolean} resize: true to resize the image if it has npot dimensions
michael@0 1432 * {String} fill: optional, color to fill the transparent bits
michael@0 1433 * {String} stroke: optional, color to draw an image outline
michael@0 1434 * {Number} strokeWeight: optional, the width of the outline
michael@0 1435 *
michael@0 1436 * @return {Image} the resized image
michael@0 1437 */
michael@0 1438 resizeImageToPowerOfTwo: function TGLTU_resizeImageToPowerOfTwo(aProperties)
michael@0 1439 {
michael@0 1440 // make sure the properties argument is an object
michael@0 1441 aProperties = aProperties || {};
michael@0 1442
michael@0 1443 if (!aProperties.source) {
michael@0 1444 return null;
michael@0 1445 }
michael@0 1446
michael@0 1447 let isPowerOfTwoWidth = TiltMath.isPowerOfTwo(aProperties.source.width);
michael@0 1448 let isPowerOfTwoHeight = TiltMath.isPowerOfTwo(aProperties.source.height);
michael@0 1449
michael@0 1450 // first check if the image is not already power of two
michael@0 1451 if (!aProperties.resize || (isPowerOfTwoWidth && isPowerOfTwoHeight)) {
michael@0 1452 return aProperties.source;
michael@0 1453 }
michael@0 1454
michael@0 1455 // calculate the power of two dimensions for the npot image
michael@0 1456 let width = TiltMath.nextPowerOfTwo(aProperties.source.width);
michael@0 1457 let height = TiltMath.nextPowerOfTwo(aProperties.source.height);
michael@0 1458
michael@0 1459 // create a canvas, then we will use a 2d context to scale the image
michael@0 1460 let canvas = TiltUtils.DOM.initCanvas(null, {
michael@0 1461 width: width,
michael@0 1462 height: height
michael@0 1463 });
michael@0 1464
michael@0 1465 let ctx = canvas.getContext("2d");
michael@0 1466
michael@0 1467 // optional fill (useful when handling transparent images)
michael@0 1468 if (aProperties.fill) {
michael@0 1469 ctx.fillStyle = aProperties.fill;
michael@0 1470 ctx.fillRect(0, 0, width, height);
michael@0 1471 }
michael@0 1472
michael@0 1473 // draw the image with power of two dimensions
michael@0 1474 ctx.drawImage(aProperties.source, 0, 0, width, height);
michael@0 1475
michael@0 1476 // optional stroke (useful when creating textures for edges)
michael@0 1477 if (aProperties.stroke) {
michael@0 1478 ctx.strokeStyle = aProperties.stroke;
michael@0 1479 ctx.lineWidth = aProperties.strokeWeight;
michael@0 1480 ctx.strokeRect(0, 0, width, height);
michael@0 1481 }
michael@0 1482
michael@0 1483 return canvas;
michael@0 1484 },
michael@0 1485
michael@0 1486 /**
michael@0 1487 * The total number of textures created.
michael@0 1488 */
michael@0 1489 _count: 0
michael@0 1490 };
michael@0 1491
michael@0 1492 /**
michael@0 1493 * A color shader. The only useful thing it does is set the gl_FragColor.
michael@0 1494 *
michael@0 1495 * @param {Attribute} vertexPosition: the vertex position
michael@0 1496 * @param {Uniform} mvMatrix: the model view matrix
michael@0 1497 * @param {Uniform} projMatrix: the projection matrix
michael@0 1498 * @param {Uniform} color: the color to set the gl_FragColor to
michael@0 1499 */
michael@0 1500 TiltGL.ColorShader = {
michael@0 1501
michael@0 1502 /**
michael@0 1503 * Vertex shader.
michael@0 1504 */
michael@0 1505 vs: [
michael@0 1506 "attribute vec3 vertexPosition;",
michael@0 1507
michael@0 1508 "uniform mat4 mvMatrix;",
michael@0 1509 "uniform mat4 projMatrix;",
michael@0 1510
michael@0 1511 "void main() {",
michael@0 1512 " gl_Position = projMatrix * mvMatrix * vec4(vertexPosition, 1.0);",
michael@0 1513 "}"
michael@0 1514 ].join("\n"),
michael@0 1515
michael@0 1516 /**
michael@0 1517 * Fragment shader.
michael@0 1518 */
michael@0 1519 fs: [
michael@0 1520 "#ifdef GL_ES",
michael@0 1521 "precision lowp float;",
michael@0 1522 "#endif",
michael@0 1523
michael@0 1524 "uniform vec4 fill;",
michael@0 1525
michael@0 1526 "void main() {",
michael@0 1527 " gl_FragColor = fill;",
michael@0 1528 "}"
michael@0 1529 ].join("\n")
michael@0 1530 };
michael@0 1531
michael@0 1532 TiltGL.isWebGLForceEnabled = function TGL_isWebGLForceEnabled()
michael@0 1533 {
michael@0 1534 return Services.prefs.getBoolPref("webgl.force-enabled");
michael@0 1535 };
michael@0 1536
michael@0 1537 /**
michael@0 1538 * Tests if the WebGL OpenGL or Angle renderer is available using the
michael@0 1539 * GfxInfo service.
michael@0 1540 *
michael@0 1541 * @return {Boolean} true if WebGL is available
michael@0 1542 */
michael@0 1543 TiltGL.isWebGLSupported = function TGL_isWebGLSupported()
michael@0 1544 {
michael@0 1545 let supported = false;
michael@0 1546
michael@0 1547 try {
michael@0 1548 let gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo);
michael@0 1549 let angle = gfxInfo.FEATURE_WEBGL_ANGLE;
michael@0 1550 let opengl = gfxInfo.FEATURE_WEBGL_OPENGL;
michael@0 1551
michael@0 1552 // if either the Angle or OpenGL renderers are available, WebGL should work
michael@0 1553 supported = gfxInfo.getFeatureStatus(angle) === gfxInfo.FEATURE_NO_INFO ||
michael@0 1554 gfxInfo.getFeatureStatus(opengl) === gfxInfo.FEATURE_NO_INFO;
michael@0 1555 } catch(e) {
michael@0 1556 if (e && e.message) { TiltUtils.Output.error(e.message); }
michael@0 1557 return false;
michael@0 1558 }
michael@0 1559 return supported;
michael@0 1560 };
michael@0 1561
michael@0 1562 /**
michael@0 1563 * Helper function to create a 3D context.
michael@0 1564 *
michael@0 1565 * @param {HTMLCanvasElement} aCanvas
michael@0 1566 * the canvas to get the WebGL context from
michael@0 1567 * @param {Object} aFlags
michael@0 1568 * optional, flags used for initialization
michael@0 1569 *
michael@0 1570 * @return {Object} the WebGL context, or null if anything failed
michael@0 1571 */
michael@0 1572 TiltGL.create3DContext = function TGL_create3DContext(aCanvas, aFlags)
michael@0 1573 {
michael@0 1574 TiltGL.clearCache();
michael@0 1575
michael@0 1576 // try to get a valid context from an existing canvas
michael@0 1577 let context = null;
michael@0 1578
michael@0 1579 try {
michael@0 1580 context = aCanvas.getContext(WEBGL_CONTEXT_NAME, aFlags);
michael@0 1581 } catch(e) {
michael@0 1582 if (e && e.message) { TiltUtils.Output.error(e.message); }
michael@0 1583 return null;
michael@0 1584 }
michael@0 1585 return context;
michael@0 1586 };
michael@0 1587
michael@0 1588 /**
michael@0 1589 * Clears the cache and sets all the variables to default.
michael@0 1590 */
michael@0 1591 TiltGL.clearCache = function TGL_clearCache()
michael@0 1592 {
michael@0 1593 TiltGL.ProgramUtils._activeProgram = -1;
michael@0 1594 TiltGL.ProgramUtils._enabledAttributes = [];
michael@0 1595 };

mercurial