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