зеркало из https://github.com/mozilla/gecko-dev.git
1597 строки
46 KiB
JavaScript
1597 строки
46 KiB
JavaScript
/* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=2 et sw=2 tw=80: */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
"use strict";
|
|
|
|
const Cc = Components.classes;
|
|
const Ci = Components.interfaces;
|
|
const Cu = Components.utils;
|
|
|
|
const WEBGL_CONTEXT_NAME = "experimental-webgl";
|
|
|
|
Cu.import("resource://gre/modules/Services.jsm");
|
|
Cu.import("resource:///modules/devtools/TiltMath.jsm");
|
|
Cu.import("resource:///modules/devtools/TiltUtils.jsm");
|
|
|
|
this.EXPORTED_SYMBOLS = ["TiltGL"];
|
|
|
|
/**
|
|
* Module containing thin wrappers around low-level WebGL functions.
|
|
*/
|
|
this.TiltGL = {};
|
|
|
|
/**
|
|
* Contains commonly used helper methods used in any 3D application.
|
|
*
|
|
* @param {HTMLCanvasElement} aCanvas
|
|
* the canvas element used for rendering
|
|
* @param {Function} onError
|
|
* optional, function called if initialization failed
|
|
* @param {Function} onLoad
|
|
* optional, function called if initialization worked
|
|
*/
|
|
TiltGL.Renderer = function TGL_Renderer(aCanvas, onError, onLoad)
|
|
{
|
|
/**
|
|
* The WebGL context obtained from the canvas element, used for drawing.
|
|
*/
|
|
this.context = TiltGL.create3DContext(aCanvas);
|
|
|
|
// check if the context was created successfully
|
|
if (!this.context) {
|
|
TiltUtils.Output.alert("Firefox", TiltUtils.L10n.get("initTilt.error"));
|
|
TiltUtils.Output.error(TiltUtils.L10n.get("initWebGL.error"));
|
|
|
|
if ("function" === typeof onError) {
|
|
onError();
|
|
}
|
|
return;
|
|
}
|
|
|
|
// set the default clear color and depth buffers
|
|
this.context.clearColor(0, 0, 0, 0);
|
|
this.context.clearDepth(1);
|
|
|
|
/**
|
|
* Variables representing the current framebuffer width and height.
|
|
*/
|
|
this.width = aCanvas.width;
|
|
this.height = aCanvas.height;
|
|
this.initialWidth = this.width;
|
|
this.initialHeight = this.height;
|
|
|
|
/**
|
|
* The current model view matrix.
|
|
*/
|
|
this.mvMatrix = mat4.identity(mat4.create());
|
|
|
|
/**
|
|
* The current projection matrix.
|
|
*/
|
|
this.projMatrix = mat4.identity(mat4.create());
|
|
|
|
/**
|
|
* The current fill color applied to any objects which can be filled.
|
|
* These are rectangles, circles, boxes, 2d or 3d primitives in general.
|
|
*/
|
|
this._fillColor = [];
|
|
|
|
/**
|
|
* The current stroke color applied to any objects which can be stroked.
|
|
* This property mostly refers to lines.
|
|
*/
|
|
this._strokeColor = [];
|
|
|
|
/**
|
|
* Variable representing the current stroke weight.
|
|
*/
|
|
this._strokeWeightValue = 0;
|
|
|
|
/**
|
|
* A shader useful for drawing vertices with only a color component.
|
|
*/
|
|
this._colorShader = new TiltGL.Program(this.context, {
|
|
vs: TiltGL.ColorShader.vs,
|
|
fs: TiltGL.ColorShader.fs,
|
|
attributes: ["vertexPosition"],
|
|
uniforms: ["mvMatrix", "projMatrix", "fill"]
|
|
});
|
|
|
|
// create helper functions to create shaders, meshes, buffers and textures
|
|
this.Program =
|
|
TiltGL.Program.bind(TiltGL.Program, this.context);
|
|
this.VertexBuffer =
|
|
TiltGL.VertexBuffer.bind(TiltGL.VertexBuffer, this.context);
|
|
this.IndexBuffer =
|
|
TiltGL.IndexBuffer.bind(TiltGL.IndexBuffer, this.context);
|
|
this.Texture =
|
|
TiltGL.Texture.bind(TiltGL.Texture, this.context);
|
|
|
|
// set the default mvp matrices, tint, fill, stroke and other visual props.
|
|
this.defaults();
|
|
|
|
// the renderer was created successfully
|
|
if ("function" === typeof onLoad) {
|
|
onLoad();
|
|
}
|
|
};
|
|
|
|
TiltGL.Renderer.prototype = {
|
|
|
|
/**
|
|
* Clears the color and depth buffers.
|
|
*/
|
|
clear: function TGLR_clear()
|
|
{
|
|
let gl = this.context;
|
|
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
|
},
|
|
|
|
/**
|
|
* Sets if depth testing should be enabled or not.
|
|
* Disabling could be useful when handling transparency (for example).
|
|
*
|
|
* @param {Boolean} aEnabledFlag
|
|
* true if depth testing should be enabled
|
|
*/
|
|
depthTest: function TGLR_depthTest(aEnabledFlag)
|
|
{
|
|
let gl = this.context;
|
|
|
|
if (aEnabledFlag) {
|
|
gl.enable(gl.DEPTH_TEST);
|
|
} else {
|
|
gl.disable(gl.DEPTH_TEST);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Sets if stencil testing should be enabled or not.
|
|
*
|
|
* @param {Boolean} aEnabledFlag
|
|
* true if stencil testing should be enabled
|
|
*/
|
|
stencilTest: function TGLR_stencilTest(aEnabledFlag)
|
|
{
|
|
let gl = this.context;
|
|
|
|
if (aEnabledFlag) {
|
|
gl.enable(gl.STENCIL_TEST);
|
|
} else {
|
|
gl.disable(gl.STENCIL_TEST);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Sets cull face, either "front", "back" or disabled.
|
|
*
|
|
* @param {String} aModeFlag
|
|
* blending mode, either "front", "back", "both" or falsy
|
|
*/
|
|
cullFace: function TGLR_cullFace(aModeFlag)
|
|
{
|
|
let gl = this.context;
|
|
|
|
switch (aModeFlag) {
|
|
case "front":
|
|
gl.enable(gl.CULL_FACE);
|
|
gl.cullFace(gl.FRONT);
|
|
break;
|
|
case "back":
|
|
gl.enable(gl.CULL_FACE);
|
|
gl.cullFace(gl.BACK);
|
|
break;
|
|
case "both":
|
|
gl.enable(gl.CULL_FACE);
|
|
gl.cullFace(gl.FRONT_AND_BACK);
|
|
break;
|
|
default:
|
|
gl.disable(gl.CULL_FACE);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Specifies the orientation of front-facing polygons.
|
|
*
|
|
* @param {String} aModeFlag
|
|
* either "cw" or "ccw"
|
|
*/
|
|
frontFace: function TGLR_frontFace(aModeFlag)
|
|
{
|
|
let gl = this.context;
|
|
|
|
switch (aModeFlag) {
|
|
case "cw":
|
|
gl.frontFace(gl.CW);
|
|
break;
|
|
case "ccw":
|
|
gl.frontFace(gl.CCW);
|
|
break;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Sets blending, either "alpha" or "add" (additive blending).
|
|
* Anything else disables blending.
|
|
*
|
|
* @param {String} aModeFlag
|
|
* blending mode, either "alpha", "add" or falsy
|
|
*/
|
|
blendMode: function TGLR_blendMode(aModeFlag)
|
|
{
|
|
let gl = this.context;
|
|
|
|
switch (aModeFlag) {
|
|
case "alpha":
|
|
gl.enable(gl.BLEND);
|
|
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
|
|
break;
|
|
case "add":
|
|
gl.enable(gl.BLEND);
|
|
gl.blendFunc(gl.SRC_ALPHA, gl.ONE);
|
|
break;
|
|
default:
|
|
gl.disable(gl.BLEND);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Helper function to activate the color shader.
|
|
*
|
|
* @param {TiltGL.VertexBuffer} aVerticesBuffer
|
|
* a buffer of vertices positions
|
|
* @param {Array} aColor
|
|
* the color fill to be used as [r, g, b, a] with 0..1 range
|
|
* @param {Array} aMvMatrix
|
|
* the model view matrix
|
|
* @param {Array} aProjMatrix
|
|
* the projection matrix
|
|
*/
|
|
useColorShader: function TGLR_useColorShader(
|
|
aVerticesBuffer, aColor, aMvMatrix, aProjMatrix)
|
|
{
|
|
let program = this._colorShader;
|
|
|
|
// use this program
|
|
program.use();
|
|
|
|
// bind the attributes and uniforms as necessary
|
|
program.bindVertexBuffer("vertexPosition", aVerticesBuffer);
|
|
program.bindUniformMatrix("mvMatrix", aMvMatrix || this.mvMatrix);
|
|
program.bindUniformMatrix("projMatrix", aProjMatrix || this.projMatrix);
|
|
program.bindUniformVec4("fill", aColor || this._fillColor);
|
|
},
|
|
|
|
/**
|
|
* Draws bound vertex buffers using the specified parameters.
|
|
*
|
|
* @param {Number} aDrawMode
|
|
* WebGL enum, like TRIANGLES
|
|
* @param {Number} aCount
|
|
* the number of indices to be rendered
|
|
*/
|
|
drawVertices: function TGLR_drawVertices(aDrawMode, aCount)
|
|
{
|
|
this.context.drawArrays(aDrawMode, 0, aCount);
|
|
},
|
|
|
|
/**
|
|
* Draws bound vertex buffers using the specified parameters.
|
|
* This function also makes use of an index buffer.
|
|
*
|
|
* @param {Number} aDrawMode
|
|
* WebGL enum, like TRIANGLES
|
|
* @param {TiltGL.IndexBuffer} aIndicesBuffer
|
|
* indices for the vertices buffer
|
|
*/
|
|
drawIndexedVertices: function TGLR_drawIndexedVertices(
|
|
aDrawMode, aIndicesBuffer)
|
|
{
|
|
let gl = this.context;
|
|
|
|
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, aIndicesBuffer._ref);
|
|
gl.drawElements(aDrawMode, aIndicesBuffer.numItems, gl.UNSIGNED_SHORT, 0);
|
|
},
|
|
|
|
/**
|
|
* Sets the current fill color.
|
|
*
|
|
* @param {Array} aColor
|
|
* the color fill to be used as [r, g, b, a] with 0..1 range
|
|
* @param {Number} aMultiplyAlpha
|
|
* optional, scalar to multiply the alpha element with
|
|
*/
|
|
fill: function TGLR_fill(aColor, aMultiplyAlpha)
|
|
{
|
|
let fill = this._fillColor;
|
|
|
|
fill[0] = aColor[0];
|
|
fill[1] = aColor[1];
|
|
fill[2] = aColor[2];
|
|
fill[3] = aColor[3] * (aMultiplyAlpha || 1);
|
|
},
|
|
|
|
/**
|
|
* Sets the current stroke color.
|
|
*
|
|
* @param {Array} aColor
|
|
* the color stroke to be used as [r, g, b, a] with 0..1 range
|
|
* @param {Number} aMultiplyAlpha
|
|
* optional, scalar to multiply the alpha element with
|
|
*/
|
|
stroke: function TGLR_stroke(aColor, aMultiplyAlpha)
|
|
{
|
|
let stroke = this._strokeColor;
|
|
|
|
stroke[0] = aColor[0];
|
|
stroke[1] = aColor[1];
|
|
stroke[2] = aColor[2];
|
|
stroke[3] = aColor[3] * (aMultiplyAlpha || 1);
|
|
},
|
|
|
|
/**
|
|
* Sets the current stroke weight (line width).
|
|
*
|
|
* @param {Number} aWeight
|
|
* the stroke weight
|
|
*/
|
|
strokeWeight: function TGLR_strokeWeight(aWeight)
|
|
{
|
|
if (this._strokeWeightValue !== aWeight) {
|
|
this._strokeWeightValue = aWeight;
|
|
this.context.lineWidth(aWeight);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Sets a default perspective projection, with the near frustum rectangle
|
|
* mapped to the canvas width and height bounds.
|
|
*/
|
|
perspective: function TGLR_perspective()
|
|
{
|
|
let fov = 45;
|
|
let w = this.width;
|
|
let h = this.height;
|
|
let x = w / 2;
|
|
let y = h / 2;
|
|
let z = y / Math.tan(TiltMath.radians(fov) / 2);
|
|
let aspect = w / h;
|
|
let znear = z / 10;
|
|
let zfar = z * 10;
|
|
|
|
mat4.perspective(fov, aspect, znear, zfar, this.projMatrix, -1);
|
|
mat4.translate(this.projMatrix, [-x, -y, -z]);
|
|
mat4.identity(this.mvMatrix);
|
|
},
|
|
|
|
/**
|
|
* Sets a default orthographic projection (recommended for 2d rendering).
|
|
*/
|
|
ortho: function TGLR_ortho()
|
|
{
|
|
mat4.ortho(0, this.width, this.height, 0, -1, 1, this.projMatrix);
|
|
mat4.identity(this.mvMatrix);
|
|
},
|
|
|
|
/**
|
|
* Sets a custom projection matrix.
|
|
* @param {Array} matrix: the custom projection matrix to be used
|
|
*/
|
|
projection: function TGLR_projection(aMatrix)
|
|
{
|
|
mat4.set(aMatrix, this.projMatrix);
|
|
mat4.identity(this.mvMatrix);
|
|
},
|
|
|
|
/**
|
|
* Resets the model view matrix to identity.
|
|
* This is a default matrix with no rotation, no scaling, at (0, 0, 0);
|
|
*/
|
|
origin: function TGLR_origin()
|
|
{
|
|
mat4.identity(this.mvMatrix);
|
|
},
|
|
|
|
/**
|
|
* Transforms the model view matrix with a new matrix.
|
|
* Useful for creating custom transformations.
|
|
*
|
|
* @param {Array} matrix: the matrix to be multiply the model view with
|
|
*/
|
|
transform: function TGLR_transform(aMatrix)
|
|
{
|
|
mat4.multiply(this.mvMatrix, aMatrix);
|
|
},
|
|
|
|
/**
|
|
* Translates the model view by the x, y and z coordinates.
|
|
*
|
|
* @param {Number} x
|
|
* the x amount of translation
|
|
* @param {Number} y
|
|
* the y amount of translation
|
|
* @param {Number} z
|
|
* optional, the z amount of translation
|
|
*/
|
|
translate: function TGLR_translate(x, y, z)
|
|
{
|
|
mat4.translate(this.mvMatrix, [x, y, z || 0]);
|
|
},
|
|
|
|
/**
|
|
* Rotates the model view by a specified angle on the x, y and z axis.
|
|
*
|
|
* @param {Number} angle
|
|
* the angle expressed in radians
|
|
* @param {Number} x
|
|
* the x axis of the rotation
|
|
* @param {Number} y
|
|
* the y axis of the rotation
|
|
* @param {Number} z
|
|
* the z axis of the rotation
|
|
*/
|
|
rotate: function TGLR_rotate(angle, x, y, z)
|
|
{
|
|
mat4.rotate(this.mvMatrix, angle, [x, y, z]);
|
|
},
|
|
|
|
/**
|
|
* Rotates the model view by a specified angle on the x axis.
|
|
*
|
|
* @param {Number} aAngle
|
|
* the angle expressed in radians
|
|
*/
|
|
rotateX: function TGLR_rotateX(aAngle)
|
|
{
|
|
mat4.rotateX(this.mvMatrix, aAngle);
|
|
},
|
|
|
|
/**
|
|
* Rotates the model view by a specified angle on the y axis.
|
|
*
|
|
* @param {Number} aAngle
|
|
* the angle expressed in radians
|
|
*/
|
|
rotateY: function TGLR_rotateY(aAngle)
|
|
{
|
|
mat4.rotateY(this.mvMatrix, aAngle);
|
|
},
|
|
|
|
/**
|
|
* Rotates the model view by a specified angle on the z axis.
|
|
*
|
|
* @param {Number} aAngle
|
|
* the angle expressed in radians
|
|
*/
|
|
rotateZ: function TGLR_rotateZ(aAngle)
|
|
{
|
|
mat4.rotateZ(this.mvMatrix, aAngle);
|
|
},
|
|
|
|
/**
|
|
* Scales the model view by the x, y and z coordinates.
|
|
*
|
|
* @param {Number} x
|
|
* the x amount of scaling
|
|
* @param {Number} y
|
|
* the y amount of scaling
|
|
* @param {Number} z
|
|
* optional, the z amount of scaling
|
|
*/
|
|
scale: function TGLR_scale(x, y, z)
|
|
{
|
|
mat4.scale(this.mvMatrix, [x, y, z || 1]);
|
|
},
|
|
|
|
/**
|
|
* Performs a custom interpolation between two matrices.
|
|
* The result is saved in the first operand.
|
|
*
|
|
* @param {Array} aMat
|
|
* the first matrix
|
|
* @param {Array} aMat2
|
|
* the second matrix
|
|
* @param {Number} aLerp
|
|
* interpolation amount between the two inputs
|
|
* @param {Number} aDamping
|
|
* optional, scalar adjusting the interpolation amortization
|
|
* @param {Number} aBalance
|
|
* optional, scalar adjusting the interpolation shift ammount
|
|
*/
|
|
lerp: function TGLR_lerp(aMat, aMat2, aLerp, aDamping, aBalance)
|
|
{
|
|
if (aLerp < 0 || aLerp > 1) {
|
|
return;
|
|
}
|
|
|
|
// calculate the interpolation factor based on the damping and step
|
|
let f = Math.pow(1 - Math.pow(aLerp, aDamping || 1), 1 / aBalance || 1);
|
|
|
|
// interpolate each element from the two matrices
|
|
for (let i = 0, len = this.projMatrix.length; i < len; i++) {
|
|
aMat[i] = aMat[i] + f * (aMat2[i] - aMat[i]);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Resets the drawing style to default.
|
|
*/
|
|
defaults: function TGLR_defaults()
|
|
{
|
|
this.depthTest(true);
|
|
this.stencilTest(false);
|
|
this.cullFace(false);
|
|
this.frontFace("ccw");
|
|
this.blendMode("alpha");
|
|
this.fill([1, 1, 1, 1]);
|
|
this.stroke([0, 0, 0, 1]);
|
|
this.strokeWeight(1);
|
|
this.perspective();
|
|
this.origin();
|
|
},
|
|
|
|
/**
|
|
* Draws a quad composed of four vertices.
|
|
* Vertices must be in clockwise order, or else drawing will be distorted.
|
|
* Do not abuse this function, it is quite slow.
|
|
*
|
|
* @param {Array} aV0
|
|
* the [x, y, z] position of the first triangle point
|
|
* @param {Array} aV1
|
|
* the [x, y, z] position of the second triangle point
|
|
* @param {Array} aV2
|
|
* the [x, y, z] position of the third triangle point
|
|
* @param {Array} aV3
|
|
* the [x, y, z] position of the fourth triangle point
|
|
*/
|
|
quad: function TGLR_quad(aV0, aV1, aV2, aV3)
|
|
{
|
|
let gl = this.context;
|
|
let fill = this._fillColor;
|
|
let stroke = this._strokeColor;
|
|
let vert = new TiltGL.VertexBuffer(gl, [aV0[0], aV0[1], aV0[2] || 0,
|
|
aV1[0], aV1[1], aV1[2] || 0,
|
|
aV2[0], aV2[1], aV2[2] || 0,
|
|
aV3[0], aV3[1], aV3[2] || 0], 3);
|
|
|
|
// use the necessary shader and draw the vertices
|
|
this.useColorShader(vert, fill);
|
|
this.drawVertices(gl.TRIANGLE_FAN, vert.numItems);
|
|
|
|
this.useColorShader(vert, stroke);
|
|
this.drawVertices(gl.LINE_LOOP, vert.numItems);
|
|
|
|
TiltUtils.destroyObject(vert);
|
|
},
|
|
|
|
/**
|
|
* Function called when this object is destroyed.
|
|
*/
|
|
finalize: function TGLR_finalize()
|
|
{
|
|
if (this.context) {
|
|
TiltUtils.destroyObject(this._colorShader);
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Creates a vertex buffer containing an array of elements.
|
|
*
|
|
* @param {Object} aContext
|
|
* a WebGL context
|
|
* @param {Array} aElementsArray
|
|
* an array of numbers (floats)
|
|
* @param {Number} aItemSize
|
|
* how many items create a block
|
|
* @param {Number} aNumItems
|
|
* optional, how many items to use from the array
|
|
*/
|
|
TiltGL.VertexBuffer = function TGL_VertexBuffer(
|
|
aContext, aElementsArray, aItemSize, aNumItems)
|
|
{
|
|
/**
|
|
* The parent WebGL context.
|
|
*/
|
|
this._context = aContext;
|
|
|
|
/**
|
|
* The array buffer.
|
|
*/
|
|
this._ref = null;
|
|
|
|
/**
|
|
* Array of number components contained in the buffer.
|
|
*/
|
|
this.components = null;
|
|
|
|
/**
|
|
* Variables defining the internal structure of the buffer.
|
|
*/
|
|
this.itemSize = 0;
|
|
this.numItems = 0;
|
|
|
|
// if the array is specified in the constructor, initialize directly
|
|
if (aElementsArray) {
|
|
this.initBuffer(aElementsArray, aItemSize, aNumItems);
|
|
}
|
|
};
|
|
|
|
TiltGL.VertexBuffer.prototype = {
|
|
|
|
/**
|
|
* Initializes buffer data to be used for drawing, using an array of floats.
|
|
* The "aNumItems" param can be specified to use only a portion of the array.
|
|
*
|
|
* @param {Array} aElementsArray
|
|
* an array of floats
|
|
* @param {Number} aItemSize
|
|
* how many items create a block
|
|
* @param {Number} aNumItems
|
|
* optional, how many items to use from the array
|
|
*/
|
|
initBuffer: function TGLVB_initBuffer(aElementsArray, aItemSize, aNumItems)
|
|
{
|
|
let gl = this._context;
|
|
|
|
// the aNumItems parameter is optional, we can compute it if not specified
|
|
aNumItems = aNumItems || aElementsArray.length / aItemSize;
|
|
|
|
// create the Float32Array using the elements array
|
|
this.components = new Float32Array(aElementsArray);
|
|
|
|
// create an array buffer and bind the elements as a Float32Array
|
|
this._ref = gl.createBuffer();
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, this._ref);
|
|
gl.bufferData(gl.ARRAY_BUFFER, this.components, gl.STATIC_DRAW);
|
|
|
|
// remember some properties, useful when binding the buffer to a shader
|
|
this.itemSize = aItemSize;
|
|
this.numItems = aNumItems;
|
|
},
|
|
|
|
/**
|
|
* Function called when this object is destroyed.
|
|
*/
|
|
finalize: function TGLVB_finalize()
|
|
{
|
|
if (this._context) {
|
|
this._context.deleteBuffer(this._ref);
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Creates an index buffer containing an array of indices.
|
|
*
|
|
* @param {Object} aContext
|
|
* a WebGL context
|
|
* @param {Array} aElementsArray
|
|
* an array of unsigned integers
|
|
* @param {Number} aNumItems
|
|
* optional, how many items to use from the array
|
|
*/
|
|
TiltGL.IndexBuffer = function TGL_IndexBuffer(
|
|
aContext, aElementsArray, aNumItems)
|
|
{
|
|
/**
|
|
* The parent WebGL context.
|
|
*/
|
|
this._context = aContext;
|
|
|
|
/**
|
|
* The element array buffer.
|
|
*/
|
|
this._ref = null;
|
|
|
|
/**
|
|
* Array of number components contained in the buffer.
|
|
*/
|
|
this.components = null;
|
|
|
|
/**
|
|
* Variables defining the internal structure of the buffer.
|
|
*/
|
|
this.itemSize = 0;
|
|
this.numItems = 0;
|
|
|
|
// if the array is specified in the constructor, initialize directly
|
|
if (aElementsArray) {
|
|
this.initBuffer(aElementsArray, aNumItems);
|
|
}
|
|
};
|
|
|
|
TiltGL.IndexBuffer.prototype = {
|
|
|
|
/**
|
|
* Initializes a buffer of vertex indices, using an array of unsigned ints.
|
|
* The item size will automatically default to 1, and the "numItems" will be
|
|
* equal to the number of items in the array if not specified.
|
|
*
|
|
* @param {Array} aElementsArray
|
|
* an array of numbers (unsigned integers)
|
|
* @param {Number} aNumItems
|
|
* optional, how many items to use from the array
|
|
*/
|
|
initBuffer: function TGLIB_initBuffer(aElementsArray, aNumItems)
|
|
{
|
|
let gl = this._context;
|
|
|
|
// the aNumItems parameter is optional, we can compute it if not specified
|
|
aNumItems = aNumItems || aElementsArray.length;
|
|
|
|
// create the Uint16Array using the elements array
|
|
this.components = new Uint16Array(aElementsArray);
|
|
|
|
// create an array buffer and bind the elements as a Uint16Array
|
|
this._ref = gl.createBuffer();
|
|
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this._ref);
|
|
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.components, gl.STATIC_DRAW);
|
|
|
|
// remember some properties, useful when binding the buffer to a shader
|
|
this.itemSize = 1;
|
|
this.numItems = aNumItems;
|
|
},
|
|
|
|
/**
|
|
* Function called when this object is destroyed.
|
|
*/
|
|
finalize: function TGLIB_finalize()
|
|
{
|
|
if (this._context) {
|
|
this._context.deleteBuffer(this._ref);
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* A program is composed of a vertex and a fragment shader.
|
|
*
|
|
* @param {Object} aProperties
|
|
* optional, an object containing the following properties:
|
|
* {String} vs: the vertex shader source code
|
|
* {String} fs: the fragment shader source code
|
|
* {Array} attributes: an array of attributes as strings
|
|
* {Array} uniforms: an array of uniforms as strings
|
|
*/
|
|
TiltGL.Program = function(aContext, aProperties)
|
|
{
|
|
// make sure the properties parameter is a valid object
|
|
aProperties = aProperties || {};
|
|
|
|
/**
|
|
* The parent WebGL context.
|
|
*/
|
|
this._context = aContext;
|
|
|
|
/**
|
|
* A reference to the actual GLSL program.
|
|
*/
|
|
this._ref = null;
|
|
|
|
/**
|
|
* Each program has an unique id assigned.
|
|
*/
|
|
this._id = -1;
|
|
|
|
/**
|
|
* Two arrays: an attributes array, containing all the cached attributes
|
|
* and a uniforms array, containing all the cached uniforms.
|
|
*/
|
|
this._attributes = null;
|
|
this._uniforms = null;
|
|
|
|
// if the sources are specified in the constructor, initialize directly
|
|
if (aProperties.vs && aProperties.fs) {
|
|
this.initProgram(aProperties);
|
|
}
|
|
};
|
|
|
|
TiltGL.Program.prototype = {
|
|
|
|
/**
|
|
* Initializes a shader program, using specified source code as strings.
|
|
*
|
|
* @param {Object} aProperties
|
|
* an object containing the following properties:
|
|
* {String} vs: the vertex shader source code
|
|
* {String} fs: the fragment shader source code
|
|
* {Array} attributes: an array of attributes as strings
|
|
* {Array} uniforms: an array of uniforms as strings
|
|
*/
|
|
initProgram: function TGLP_initProgram(aProperties)
|
|
{
|
|
this._ref = TiltGL.ProgramUtils.create(this._context, aProperties);
|
|
|
|
// cache for faster access
|
|
this._id = this._ref.id;
|
|
this._attributes = this._ref.attributes;
|
|
this._uniforms = this._ref.uniforms;
|
|
|
|
// cleanup
|
|
delete this._ref.id;
|
|
delete this._ref.attributes;
|
|
delete this._ref.uniforms;
|
|
},
|
|
|
|
/**
|
|
* Uses the shader program as current one for the WebGL context; it also
|
|
* enables vertex attributes necessary to enable when using this program.
|
|
* This method also does some useful caching, as the function "useProgram"
|
|
* could take quite a lot of time.
|
|
*/
|
|
use: function TGLP_use()
|
|
{
|
|
let id = this._id;
|
|
let utils = TiltGL.ProgramUtils;
|
|
|
|
// check if the program wasn't already active
|
|
if (utils._activeProgram !== id) {
|
|
utils._activeProgram = id;
|
|
|
|
// use the the program if it wasn't already set
|
|
this._context.useProgram(this._ref);
|
|
this.cleanupVertexAttrib();
|
|
|
|
// enable any necessary vertex attributes using the cache
|
|
for each (let attribute in this._attributes) {
|
|
this._context.enableVertexAttribArray(attribute);
|
|
utils._enabledAttributes.push(attribute);
|
|
}
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Disables all currently enabled vertex attribute arrays.
|
|
*/
|
|
cleanupVertexAttrib: function TGLP_cleanupVertexAttrib()
|
|
{
|
|
let utils = TiltGL.ProgramUtils;
|
|
|
|
for each (let attribute in utils._enabledAttributes) {
|
|
this._context.disableVertexAttribArray(attribute);
|
|
}
|
|
utils._enabledAttributes = [];
|
|
},
|
|
|
|
/**
|
|
* Binds a vertex buffer as an array buffer for a specific shader attribute.
|
|
*
|
|
* @param {String} aAtribute
|
|
* the attribute name obtained from the shader
|
|
* @param {Float32Array} aBuffer
|
|
* the buffer to be bound
|
|
*/
|
|
bindVertexBuffer: function TGLP_bindVertexBuffer(aAtribute, aBuffer)
|
|
{
|
|
// get the cached attribute value from the shader
|
|
let gl = this._context;
|
|
let attr = this._attributes[aAtribute];
|
|
let size = aBuffer.itemSize;
|
|
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, aBuffer._ref);
|
|
gl.vertexAttribPointer(attr, size, gl.FLOAT, false, 0, 0);
|
|
},
|
|
|
|
/**
|
|
* Binds a uniform matrix to the current shader.
|
|
*
|
|
* @param {String} aUniform
|
|
* the uniform name to bind the variable to
|
|
* @param {Float32Array} m
|
|
* the matrix to be bound
|
|
*/
|
|
bindUniformMatrix: function TGLP_bindUniformMatrix(aUniform, m)
|
|
{
|
|
this._context.uniformMatrix4fv(this._uniforms[aUniform], false, m);
|
|
},
|
|
|
|
/**
|
|
* Binds a uniform vector of 4 elements to the current shader.
|
|
*
|
|
* @param {String} aUniform
|
|
* the uniform name to bind the variable to
|
|
* @param {Float32Array} v
|
|
* the vector to be bound
|
|
*/
|
|
bindUniformVec4: function TGLP_bindUniformVec4(aUniform, v)
|
|
{
|
|
this._context.uniform4fv(this._uniforms[aUniform], v);
|
|
},
|
|
|
|
/**
|
|
* Binds a simple float element to the current shader.
|
|
*
|
|
* @param {String} aUniform
|
|
* the uniform name to bind the variable to
|
|
* @param {Number} v
|
|
* the variable to be bound
|
|
*/
|
|
bindUniformFloat: function TGLP_bindUniformFloat(aUniform, f)
|
|
{
|
|
this._context.uniform1f(this._uniforms[aUniform], f);
|
|
},
|
|
|
|
/**
|
|
* Binds a uniform texture for a sampler to the current shader.
|
|
*
|
|
* @param {String} aSampler
|
|
* the sampler name to bind the texture to
|
|
* @param {TiltGL.Texture} aTexture
|
|
* the texture to be bound
|
|
*/
|
|
bindTexture: function TGLP_bindTexture(aSampler, aTexture)
|
|
{
|
|
let gl = this._context;
|
|
|
|
gl.activeTexture(gl.TEXTURE0);
|
|
gl.bindTexture(gl.TEXTURE_2D, aTexture._ref);
|
|
gl.uniform1i(this._uniforms[aSampler], 0);
|
|
},
|
|
|
|
/**
|
|
* Function called when this object is destroyed.
|
|
*/
|
|
finalize: function TGLP_finalize()
|
|
{
|
|
if (this._context) {
|
|
this._context.useProgram(null);
|
|
this._context.deleteProgram(this._ref);
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Utility functions for handling GLSL shaders and programs.
|
|
*/
|
|
TiltGL.ProgramUtils = {
|
|
|
|
/**
|
|
* Initializes a shader program, using specified source code as strings,
|
|
* returning the newly created shader program, by compiling and linking the
|
|
* vertex and fragment shader.
|
|
*
|
|
* @param {Object} aContext
|
|
* a WebGL context
|
|
* @param {Object} aProperties
|
|
* an object containing the following properties:
|
|
* {String} vs: the vertex shader source code
|
|
* {String} fs: the fragment shader source code
|
|
* {Array} attributes: an array of attributes as strings
|
|
* {Array} uniforms: an array of uniforms as strings
|
|
*/
|
|
create: function TGLPU_create(aContext, aProperties)
|
|
{
|
|
// make sure the properties parameter is a valid object
|
|
aProperties = aProperties || {};
|
|
|
|
// compile the two shaders
|
|
let vertShader = this.compile(aContext, aProperties.vs, "vertex");
|
|
let fragShader = this.compile(aContext, aProperties.fs, "fragment");
|
|
let program = this.link(aContext, vertShader, fragShader);
|
|
|
|
aContext.deleteShader(vertShader);
|
|
aContext.deleteShader(fragShader);
|
|
|
|
return this.cache(aContext, aProperties, program);
|
|
},
|
|
|
|
/**
|
|
* Compiles a shader source of a specific type, either vertex or fragment.
|
|
*
|
|
* @param {Object} aContext
|
|
* a WebGL context
|
|
* @param {String} aShaderSource
|
|
* the source code for the shader
|
|
* @param {String} aShaderType
|
|
* the shader type ("vertex" or "fragment")
|
|
*
|
|
* @return {WebGLShader} the compiled shader
|
|
*/
|
|
compile: function TGLPU_compile(aContext, aShaderSource, aShaderType)
|
|
{
|
|
let gl = aContext, shader, status;
|
|
|
|
// make sure the shader source is valid
|
|
if ("string" !== typeof aShaderSource || aShaderSource.length < 1) {
|
|
TiltUtils.Output.error(
|
|
TiltUtils.L10n.get("compileShader.source.error"));
|
|
return null;
|
|
}
|
|
|
|
// also make sure the necessary shader mime type is valid
|
|
if (aShaderType === "vertex") {
|
|
shader = gl.createShader(gl.VERTEX_SHADER);
|
|
} else if (aShaderType === "fragment") {
|
|
shader = gl.createShader(gl.FRAGMENT_SHADER);
|
|
} else {
|
|
TiltUtils.Output.error(
|
|
TiltUtils.L10n.format("compileShader.type.error", [aShaderSource]));
|
|
return null;
|
|
}
|
|
|
|
// set the shader source and compile it
|
|
gl.shaderSource(shader, aShaderSource);
|
|
gl.compileShader(shader);
|
|
|
|
// remember the shader source (useful for debugging and caching)
|
|
shader.src = aShaderSource;
|
|
|
|
// verify the compile status; if something went wrong, log the error
|
|
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
|
status = gl.getShaderInfoLog(shader);
|
|
|
|
TiltUtils.Output.error(
|
|
TiltUtils.L10n.format("compileShader.compile.error", [status]));
|
|
return null;
|
|
}
|
|
|
|
// return the newly compiled shader from the specified source
|
|
return shader;
|
|
},
|
|
|
|
/**
|
|
* Links two compiled vertex or fragment shaders together to form a program.
|
|
*
|
|
* @param {Object} aContext
|
|
* a WebGL context
|
|
* @param {WebGLShader} aVertShader
|
|
* the compiled vertex shader
|
|
* @param {WebGLShader} aFragShader
|
|
* the compiled fragment shader
|
|
*
|
|
* @return {WebGLProgram} the newly created and linked shader program
|
|
*/
|
|
link: function TGLPU_link(aContext, aVertShader, aFragShader)
|
|
{
|
|
let gl = aContext, program, status;
|
|
|
|
// create a program and attach the compiled vertex and fragment shaders
|
|
program = gl.createProgram();
|
|
|
|
// attach the vertex and fragment shaders to the program
|
|
gl.attachShader(program, aVertShader);
|
|
gl.attachShader(program, aFragShader);
|
|
gl.linkProgram(program);
|
|
|
|
// verify the link status; if something went wrong, log the error
|
|
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
|
|
status = gl.getProgramInfoLog(program);
|
|
|
|
TiltUtils.Output.error(
|
|
TiltUtils.L10n.format("linkProgram.error", [status]));
|
|
return null;
|
|
}
|
|
|
|
// generate an id for the program
|
|
program.id = this._count++;
|
|
|
|
return program;
|
|
},
|
|
|
|
/**
|
|
* Caches shader attributes and uniforms as properties for a program object.
|
|
*
|
|
* @param {Object} aContext
|
|
* a WebGL context
|
|
* @param {Object} aProperties
|
|
* an object containing the following properties:
|
|
* {Array} attributes: optional, an array of attributes as strings
|
|
* {Array} uniforms: optional, an array of uniforms as strings
|
|
* @param {WebGLProgram} aProgram
|
|
* the shader program used for caching
|
|
*
|
|
* @return {WebGLProgram} the same program
|
|
*/
|
|
cache: function TGLPU_cache(aContext, aProperties, aProgram)
|
|
{
|
|
// make sure the properties parameter is a valid object
|
|
aProperties = aProperties || {};
|
|
|
|
// make sure the attributes and uniforms cache objects are created
|
|
aProgram.attributes = {};
|
|
aProgram.uniforms = {};
|
|
|
|
Object.defineProperty(aProgram.attributes, "length",
|
|
{ value: 0, writable: true, enumerable: false, configurable: true });
|
|
|
|
Object.defineProperty(aProgram.uniforms, "length",
|
|
{ value: 0, writable: true, enumerable: false, configurable: true });
|
|
|
|
|
|
let attr = aProperties.attributes;
|
|
let unif = aProperties.uniforms;
|
|
|
|
if (attr) {
|
|
for (let i = 0, len = attr.length; i < len; i++) {
|
|
// try to get a shader attribute from the program
|
|
let param = attr[i];
|
|
let loc = aContext.getAttribLocation(aProgram, param);
|
|
|
|
if ("number" === typeof loc && loc > -1) {
|
|
// if we get an attribute location, store it
|
|
// bind the new parameter only if it was not already defined
|
|
if (aProgram.attributes[param] === undefined) {
|
|
aProgram.attributes[param] = loc;
|
|
aProgram.attributes.length++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (unif) {
|
|
for (let i = 0, len = unif.length; i < len; i++) {
|
|
// try to get a shader uniform from the program
|
|
let param = unif[i];
|
|
let loc = aContext.getUniformLocation(aProgram, param);
|
|
|
|
if ("object" === typeof loc && loc) {
|
|
// if we get a uniform object, store it
|
|
// bind the new parameter only if it was not already defined
|
|
if (aProgram.uniforms[param] === undefined) {
|
|
aProgram.uniforms[param] = loc;
|
|
aProgram.uniforms.length++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return aProgram;
|
|
},
|
|
|
|
/**
|
|
* The total number of programs created.
|
|
*/
|
|
_count: 0,
|
|
|
|
/**
|
|
* Represents the current active shader, identified by an id.
|
|
*/
|
|
_activeProgram: -1,
|
|
|
|
/**
|
|
* Represents the current enabled attributes.
|
|
*/
|
|
_enabledAttributes: []
|
|
};
|
|
|
|
/**
|
|
* This constructor creates a texture from an Image.
|
|
*
|
|
* @param {Object} aContext
|
|
* a WebGL context
|
|
* @param {Object} aProperties
|
|
* optional, an object containing the following properties:
|
|
* {Image} source: the source image for the texture
|
|
* {String} format: the format of the texture ("RGB" or "RGBA")
|
|
* {String} fill: optional, color to fill the transparent bits
|
|
* {String} stroke: optional, color to draw an outline
|
|
* {Number} strokeWeight: optional, the width of the outline
|
|
* {String} minFilter: either "nearest" or "linear"
|
|
* {String} magFilter: either "nearest" or "linear"
|
|
* {String} wrapS: either "repeat" or "clamp"
|
|
* {String} wrapT: either "repeat" or "clamp"
|
|
* {Boolean} mipmap: true if should generate mipmap
|
|
*/
|
|
TiltGL.Texture = function(aContext, aProperties)
|
|
{
|
|
// make sure the properties parameter is a valid object
|
|
aProperties = aProperties || {};
|
|
|
|
/**
|
|
* The parent WebGL context.
|
|
*/
|
|
this._context = aContext;
|
|
|
|
/**
|
|
* A reference to the WebGL texture object.
|
|
*/
|
|
this._ref = null;
|
|
|
|
/**
|
|
* Each texture has an unique id assigned.
|
|
*/
|
|
this._id = -1;
|
|
|
|
/**
|
|
* Variables specifying the width and height of the texture.
|
|
* If these values are less than 0, the texture hasn't loaded yet.
|
|
*/
|
|
this.width = -1;
|
|
this.height = -1;
|
|
|
|
/**
|
|
* Specifies if the texture has loaded or not.
|
|
*/
|
|
this.loaded = false;
|
|
|
|
// if the image is specified in the constructor, initialize directly
|
|
if ("object" === typeof aProperties.source) {
|
|
this.initTexture(aProperties);
|
|
} else {
|
|
TiltUtils.Output.error(
|
|
TiltUtils.L10n.get("initTexture.source.error"));
|
|
}
|
|
};
|
|
|
|
TiltGL.Texture.prototype = {
|
|
|
|
/**
|
|
* Initializes a texture from a pre-existing image or canvas.
|
|
*
|
|
* @param {Image} aImage
|
|
* the source image or canvas
|
|
* @param {Object} aProperties
|
|
* an object containing the following properties:
|
|
* {Image} source: the source image for the texture
|
|
* {String} format: the format of the texture ("RGB" or "RGBA")
|
|
* {String} fill: optional, color to fill the transparent bits
|
|
* {String} stroke: optional, color to draw an outline
|
|
* {Number} strokeWeight: optional, the width of the outline
|
|
* {String} minFilter: either "nearest" or "linear"
|
|
* {String} magFilter: either "nearest" or "linear"
|
|
* {String} wrapS: either "repeat" or "clamp"
|
|
* {String} wrapT: either "repeat" or "clamp"
|
|
* {Boolean} mipmap: true if should generate mipmap
|
|
*/
|
|
initTexture: function TGLT_initTexture(aProperties)
|
|
{
|
|
this._ref = TiltGL.TextureUtils.create(this._context, aProperties);
|
|
|
|
// cache for faster access
|
|
this._id = this._ref.id;
|
|
this.width = this._ref.width;
|
|
this.height = this._ref.height;
|
|
this.loaded = true;
|
|
|
|
// cleanup
|
|
delete this._ref.id;
|
|
delete this._ref.width;
|
|
delete this._ref.height;
|
|
delete this.onload;
|
|
},
|
|
|
|
/**
|
|
* Function called when this object is destroyed.
|
|
*/
|
|
finalize: function TGLT_finalize()
|
|
{
|
|
if (this._context) {
|
|
this._context.deleteTexture(this._ref);
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Utility functions for creating and manipulating textures.
|
|
*/
|
|
TiltGL.TextureUtils = {
|
|
|
|
/**
|
|
* Initializes a texture from a pre-existing image or canvas.
|
|
*
|
|
* @param {Object} aContext
|
|
* a WebGL context
|
|
* @param {Image} aImage
|
|
* the source image or canvas
|
|
* @param {Object} aProperties
|
|
* an object containing some of the following properties:
|
|
* {Image} source: the source image for the texture
|
|
* {String} format: the format of the texture ("RGB" or "RGBA")
|
|
* {String} fill: optional, color to fill the transparent bits
|
|
* {String} stroke: optional, color to draw an outline
|
|
* {Number} strokeWeight: optional, the width of the outline
|
|
* {String} minFilter: either "nearest" or "linear"
|
|
* {String} magFilter: either "nearest" or "linear"
|
|
* {String} wrapS: either "repeat" or "clamp"
|
|
* {String} wrapT: either "repeat" or "clamp"
|
|
* {Boolean} mipmap: true if should generate mipmap
|
|
*
|
|
* @return {WebGLTexture} the created texture
|
|
*/
|
|
create: function TGLTU_create(aContext, aProperties)
|
|
{
|
|
// make sure the properties argument is an object
|
|
aProperties = aProperties || {};
|
|
|
|
if (!aProperties.source) {
|
|
return null;
|
|
}
|
|
|
|
let gl = aContext;
|
|
let width = aProperties.source.width;
|
|
let height = aProperties.source.height;
|
|
let format = gl[aProperties.format || "RGB"];
|
|
|
|
// make sure the image is power of two before binding to a texture
|
|
let source = this.resizeImageToPowerOfTwo(aProperties);
|
|
|
|
// first, create the texture to hold the image data
|
|
let texture = gl.createTexture();
|
|
|
|
// attach the image data to the newly create texture
|
|
gl.bindTexture(gl.TEXTURE_2D, texture);
|
|
gl.texImage2D(gl.TEXTURE_2D, 0, format, format, gl.UNSIGNED_BYTE, source);
|
|
this.setTextureParams(gl, aProperties);
|
|
|
|
// do some cleanup
|
|
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
|
|
// remember the width and the height
|
|
texture.width = width;
|
|
texture.height = height;
|
|
|
|
// generate an id for the texture
|
|
texture.id = this._count++;
|
|
|
|
return texture;
|
|
},
|
|
|
|
/**
|
|
* Sets texture parameters for the current texture binding.
|
|
* Optionally, you can also (re)set the current texture binding manually.
|
|
*
|
|
* @param {Object} aContext
|
|
* a WebGL context
|
|
* @param {Object} aProperties
|
|
* an object containing the texture properties
|
|
*/
|
|
setTextureParams: function TGLTU_setTextureParams(aContext, aProperties)
|
|
{
|
|
// make sure the properties argument is an object
|
|
aProperties = aProperties || {};
|
|
|
|
let gl = aContext;
|
|
let minFilter = gl.TEXTURE_MIN_FILTER;
|
|
let magFilter = gl.TEXTURE_MAG_FILTER;
|
|
let wrapS = gl.TEXTURE_WRAP_S;
|
|
let wrapT = gl.TEXTURE_WRAP_T;
|
|
|
|
// bind a new texture if necessary
|
|
if (aProperties.texture) {
|
|
gl.bindTexture(gl.TEXTURE_2D, aProperties.texture.ref);
|
|
}
|
|
|
|
// set the minification filter
|
|
if ("nearest" === aProperties.minFilter) {
|
|
gl.texParameteri(gl.TEXTURE_2D, minFilter, gl.NEAREST);
|
|
} else if ("linear" === aProperties.minFilter && aProperties.mipmap) {
|
|
gl.texParameteri(gl.TEXTURE_2D, minFilter, gl.LINEAR_MIPMAP_LINEAR);
|
|
} else {
|
|
gl.texParameteri(gl.TEXTURE_2D, minFilter, gl.LINEAR);
|
|
}
|
|
|
|
// set the magnification filter
|
|
if ("nearest" === aProperties.magFilter) {
|
|
gl.texParameteri(gl.TEXTURE_2D, magFilter, gl.NEAREST);
|
|
} else {
|
|
gl.texParameteri(gl.TEXTURE_2D, magFilter, gl.LINEAR);
|
|
}
|
|
|
|
// set the wrapping on the x-axis for the texture
|
|
if ("repeat" === aProperties.wrapS) {
|
|
gl.texParameteri(gl.TEXTURE_2D, wrapS, gl.REPEAT);
|
|
} else {
|
|
gl.texParameteri(gl.TEXTURE_2D, wrapS, gl.CLAMP_TO_EDGE);
|
|
}
|
|
|
|
// set the wrapping on the y-axis for the texture
|
|
if ("repeat" === aProperties.wrapT) {
|
|
gl.texParameteri(gl.TEXTURE_2D, wrapT, gl.REPEAT);
|
|
} else {
|
|
gl.texParameteri(gl.TEXTURE_2D, wrapT, gl.CLAMP_TO_EDGE);
|
|
}
|
|
|
|
// generate mipmap if necessary
|
|
if (aProperties.mipmap) {
|
|
gl.generateMipmap(gl.TEXTURE_2D);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* This shim renders a content window to a canvas element, but clamps the
|
|
* maximum width and height of the canvas to the WebGL MAX_TEXTURE_SIZE.
|
|
*
|
|
* @param {Window} aContentWindow
|
|
* the content window to get a texture from
|
|
* @param {Number} aMaxImageSize
|
|
* the maximum image size to be used
|
|
*
|
|
* @return {Image} the new content window image
|
|
*/
|
|
createContentImage: function TGLTU_createContentImage(
|
|
aContentWindow, aMaxImageSize)
|
|
{
|
|
// calculate the total width and height of the content page
|
|
let size = TiltUtils.DOM.getContentWindowDimensions(aContentWindow);
|
|
|
|
// use a custom canvas element and a 2d context to draw the window
|
|
let canvas = TiltUtils.DOM.initCanvas(null);
|
|
canvas.width = TiltMath.clamp(size.width, 0, aMaxImageSize);
|
|
canvas.height = TiltMath.clamp(size.height, 0, aMaxImageSize);
|
|
|
|
// use the 2d context.drawWindow() magic
|
|
let ctx = canvas.getContext("2d");
|
|
ctx.drawWindow(aContentWindow, 0, 0, canvas.width, canvas.height, "#fff");
|
|
|
|
return canvas;
|
|
},
|
|
|
|
/**
|
|
* Scales an image's width and height to next power of two.
|
|
* If the image already has power of two sizes, it is immediately returned,
|
|
* otherwise, a new image is created.
|
|
*
|
|
* @param {Image} aImage
|
|
* the image to be scaled
|
|
* @param {Object} aProperties
|
|
* an object containing the following properties:
|
|
* {Image} source: the source image to resize
|
|
* {Boolean} resize: true to resize the image if it has npot dimensions
|
|
* {String} fill: optional, color to fill the transparent bits
|
|
* {String} stroke: optional, color to draw an image outline
|
|
* {Number} strokeWeight: optional, the width of the outline
|
|
*
|
|
* @return {Image} the resized image
|
|
*/
|
|
resizeImageToPowerOfTwo: function TGLTU_resizeImageToPowerOfTwo(aProperties)
|
|
{
|
|
// make sure the properties argument is an object
|
|
aProperties = aProperties || {};
|
|
|
|
if (!aProperties.source) {
|
|
return null;
|
|
}
|
|
|
|
let isPowerOfTwoWidth = TiltMath.isPowerOfTwo(aProperties.source.width);
|
|
let isPowerOfTwoHeight = TiltMath.isPowerOfTwo(aProperties.source.height);
|
|
|
|
// first check if the image is not already power of two
|
|
if (!aProperties.resize || (isPowerOfTwoWidth && isPowerOfTwoHeight)) {
|
|
return aProperties.source;
|
|
}
|
|
|
|
// calculate the power of two dimensions for the npot image
|
|
let width = TiltMath.nextPowerOfTwo(aProperties.source.width);
|
|
let height = TiltMath.nextPowerOfTwo(aProperties.source.height);
|
|
|
|
// create a canvas, then we will use a 2d context to scale the image
|
|
let canvas = TiltUtils.DOM.initCanvas(null, {
|
|
width: width,
|
|
height: height
|
|
});
|
|
|
|
let ctx = canvas.getContext("2d");
|
|
|
|
// optional fill (useful when handling transparent images)
|
|
if (aProperties.fill) {
|
|
ctx.fillStyle = aProperties.fill;
|
|
ctx.fillRect(0, 0, width, height);
|
|
}
|
|
|
|
// draw the image with power of two dimensions
|
|
ctx.drawImage(aProperties.source, 0, 0, width, height);
|
|
|
|
// optional stroke (useful when creating textures for edges)
|
|
if (aProperties.stroke) {
|
|
ctx.strokeStyle = aProperties.stroke;
|
|
ctx.lineWidth = aProperties.strokeWeight;
|
|
ctx.strokeRect(0, 0, width, height);
|
|
}
|
|
|
|
return canvas;
|
|
},
|
|
|
|
/**
|
|
* The total number of textures created.
|
|
*/
|
|
_count: 0
|
|
};
|
|
|
|
/**
|
|
* A color shader. The only useful thing it does is set the gl_FragColor.
|
|
*
|
|
* @param {Attribute} vertexPosition: the vertex position
|
|
* @param {Uniform} mvMatrix: the model view matrix
|
|
* @param {Uniform} projMatrix: the projection matrix
|
|
* @param {Uniform} color: the color to set the gl_FragColor to
|
|
*/
|
|
TiltGL.ColorShader = {
|
|
|
|
/**
|
|
* Vertex shader.
|
|
*/
|
|
vs: [
|
|
"attribute vec3 vertexPosition;",
|
|
|
|
"uniform mat4 mvMatrix;",
|
|
"uniform mat4 projMatrix;",
|
|
|
|
"void main() {",
|
|
" gl_Position = projMatrix * mvMatrix * vec4(vertexPosition, 1.0);",
|
|
"}"
|
|
].join("\n"),
|
|
|
|
/**
|
|
* Fragment shader.
|
|
*/
|
|
fs: [
|
|
"#ifdef GL_ES",
|
|
"precision lowp float;",
|
|
"#endif",
|
|
|
|
"uniform vec4 fill;",
|
|
|
|
"void main() {",
|
|
" gl_FragColor = fill;",
|
|
"}"
|
|
].join("\n")
|
|
};
|
|
|
|
TiltGL.isWebGLForceEnabled = function TGL_isWebGLForceEnabled()
|
|
{
|
|
return Services.prefs.getBoolPref("webgl.force-enabled");
|
|
};
|
|
|
|
/**
|
|
* Tests if the WebGL OpenGL or Angle renderer is available using the
|
|
* GfxInfo service.
|
|
*
|
|
* @return {Boolean} true if WebGL is available
|
|
*/
|
|
TiltGL.isWebGLSupported = function TGL_isWebGLSupported()
|
|
{
|
|
let supported = false;
|
|
|
|
try {
|
|
let gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo);
|
|
let angle = gfxInfo.FEATURE_WEBGL_ANGLE;
|
|
let opengl = gfxInfo.FEATURE_WEBGL_OPENGL;
|
|
|
|
// if either the Angle or OpenGL renderers are available, WebGL should work
|
|
supported = gfxInfo.getFeatureStatus(angle) === gfxInfo.FEATURE_NO_INFO ||
|
|
gfxInfo.getFeatureStatus(opengl) === gfxInfo.FEATURE_NO_INFO;
|
|
} catch(e) {
|
|
if (e && e.message) { TiltUtils.Output.error(e.message); }
|
|
return false;
|
|
}
|
|
return supported;
|
|
};
|
|
|
|
/**
|
|
* Helper function to create a 3D context.
|
|
*
|
|
* @param {HTMLCanvasElement} aCanvas
|
|
* the canvas to get the WebGL context from
|
|
* @param {Object} aFlags
|
|
* optional, flags used for initialization
|
|
*
|
|
* @return {Object} the WebGL context, or null if anything failed
|
|
*/
|
|
TiltGL.create3DContext = function TGL_create3DContext(aCanvas, aFlags)
|
|
{
|
|
TiltGL.clearCache();
|
|
|
|
// try to get a valid context from an existing canvas
|
|
let context = null;
|
|
|
|
try {
|
|
context = aCanvas.getContext(WEBGL_CONTEXT_NAME, aFlags);
|
|
} catch(e) {
|
|
if (e && e.message) { TiltUtils.Output.error(e.message); }
|
|
return null;
|
|
}
|
|
return context;
|
|
};
|
|
|
|
/**
|
|
* Clears the cache and sets all the variables to default.
|
|
*/
|
|
TiltGL.clearCache = function TGL_clearCache()
|
|
{
|
|
TiltGL.ProgramUtils._activeProgram = -1;
|
|
TiltGL.ProgramUtils._enabledAttributes = [];
|
|
};
|