commit
063ceb5293
7 changed files with 3059 additions and 0 deletions
@ -0,0 +1,603 @@ |
|||
/* |
|||
* Copyright (C) 2009 Apple Inc. All Rights Reserved. |
|||
* |
|||
* Redistribution and use in source and binary forms, with or without |
|||
* modification, are permitted provided that the following conditions |
|||
* are met: |
|||
* 1. Redistributions of source code must retain the above copyright |
|||
* notice, this list of conditions and the following disclaimer. |
|||
* 2. Redistributions in binary form must reproduce the above copyright |
|||
* notice, this list of conditions and the following disclaimer in the |
|||
* documentation and/or other materials provided with the distribution. |
|||
* |
|||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY |
|||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR |
|||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
|||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
|||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
|||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
|||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|||
*/ |
|||
|
|||
//
|
|||
// initWebGL
|
|||
//
|
|||
// Initialize the Canvas element with the passed name as a WebGL object and return the
|
|||
// WebGLRenderingContext.
|
|||
function initWebGL(canvasName, vshader, fshader, attribs, clearColor, clearDepth) |
|||
{ |
|||
var canvas = document.getElementById(canvasName); |
|||
return gl = WebGLUtils.setupWebGL(canvas); |
|||
} |
|||
|
|||
function log(msg) { |
|||
if (window.console && window.console.log) { |
|||
window.console.log(msg); |
|||
} |
|||
} |
|||
|
|||
// Load shaders with the passed names and create a program with them. Return this program
|
|||
// in the 'program' property of the returned context.
|
|||
//
|
|||
// For each string in the passed attribs array, bind an attrib with that name at that index.
|
|||
// Once the attribs are bound, link the program and then use it.
|
|||
//
|
|||
// Set the clear color to the passed array (4 values) and set the clear depth to the passed value.
|
|||
// Enable depth testing and blending with a blend func of (SRC_ALPHA, ONE_MINUS_SRC_ALPHA)
|
|||
//
|
|||
// A console function is added to the context: console(string). This can be replaced
|
|||
// by the caller. By default, it maps to the window.console() function on WebKit and to
|
|||
// an empty function on other browsers.
|
|||
//
|
|||
function simpleSetup(gl, vshader, fshader, attribs, clearColor, clearDepth) |
|||
{ |
|||
// create our shaders
|
|||
var vertexShader = loadShader(gl, vshader); |
|||
var fragmentShader = loadShader(gl, fshader); |
|||
|
|||
// Create the program object
|
|||
var program = gl.createProgram(); |
|||
|
|||
// Attach our two shaders to the program
|
|||
gl.attachShader (program, vertexShader); |
|||
gl.attachShader (program, fragmentShader); |
|||
|
|||
// Bind attributes
|
|||
for (var i = 0; i < attribs.length; ++i) |
|||
gl.bindAttribLocation (program, i, attribs[i]); |
|||
|
|||
// Link the program
|
|||
gl.linkProgram(program); |
|||
|
|||
// Check the link status
|
|||
var linked = gl.getProgramParameter(program, gl.LINK_STATUS); |
|||
if (!linked && !gl.isContextLost()) { |
|||
// something went wrong with the link
|
|||
var error = gl.getProgramInfoLog (program); |
|||
log("Error in program linking:"+error); |
|||
|
|||
gl.deleteProgram(program); |
|||
gl.deleteProgram(fragmentShader); |
|||
gl.deleteProgram(vertexShader); |
|||
|
|||
return null; |
|||
} |
|||
|
|||
gl.useProgram(program); |
|||
|
|||
gl.clearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]); |
|||
gl.clearDepth(clearDepth); |
|||
|
|||
gl.enable(gl.DEPTH_TEST); |
|||
gl.enable(gl.BLEND); |
|||
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); |
|||
|
|||
return program; |
|||
} |
|||
|
|||
//
|
|||
// loadShader
|
|||
//
|
|||
// 'shaderId' is the id of a <script> element containing the shader source string.
|
|||
// Load this shader and return the WebGLShader object corresponding to it.
|
|||
//
|
|||
function loadShader(ctx, shaderId) |
|||
{ |
|||
var shaderScript = document.getElementById(shaderId); |
|||
if (!shaderScript) { |
|||
log("*** Error: shader script '"+shaderId+"' not found"); |
|||
return null; |
|||
} |
|||
|
|||
if (shaderScript.type == "x-shader/x-vertex") |
|||
var shaderType = ctx.VERTEX_SHADER; |
|||
else if (shaderScript.type == "x-shader/x-fragment") |
|||
var shaderType = ctx.FRAGMENT_SHADER; |
|||
else { |
|||
log("*** Error: shader script '"+shaderId+"' of undefined type '"+shaderScript.type+"'"); |
|||
return null; |
|||
} |
|||
|
|||
// Create the shader object
|
|||
var shader = ctx.createShader(shaderType); |
|||
|
|||
// Load the shader source
|
|||
ctx.shaderSource(shader, shaderScript.text); |
|||
|
|||
// Compile the shader
|
|||
ctx.compileShader(shader); |
|||
|
|||
// Check the compile status
|
|||
var compiled = ctx.getShaderParameter(shader, ctx.COMPILE_STATUS); |
|||
if (!compiled && !ctx.isContextLost()) { |
|||
// Something went wrong during compilation; get the error
|
|||
var error = ctx.getShaderInfoLog(shader); |
|||
log("*** Error compiling shader '"+shaderId+"':"+error); |
|||
ctx.deleteShader(shader); |
|||
return null; |
|||
} |
|||
|
|||
return shader; |
|||
} |
|||
|
|||
//
|
|||
// makeBox
|
|||
//
|
|||
// Create a box with vertices, normals and texCoords. Create VBOs for each as well as the index array.
|
|||
// Return an object with the following properties:
|
|||
//
|
|||
// normalObject WebGLBuffer object for normals
|
|||
// texCoordObject WebGLBuffer object for texCoords
|
|||
// vertexObject WebGLBuffer object for vertices
|
|||
// indexObject WebGLBuffer object for indices
|
|||
// numIndices The number of indices in the indexObject
|
|||
//
|
|||
function makeBox(ctx) |
|||
{ |
|||
// box
|
|||
// v6----- v5
|
|||
// /| /|
|
|||
// v1------v0|
|
|||
// | | | |
|
|||
// | |v7---|-|v4
|
|||
// |/ |/
|
|||
// v2------v3
|
|||
//
|
|||
// vertex coords array
|
|||
var vertices = new Float32Array( |
|||
[ 1, 1, 1, -1, 1, 1, -1,-1, 1, 1,-1, 1, // v0-v1-v2-v3 front
|
|||
1, 1, 1, 1,-1, 1, 1,-1,-1, 1, 1,-1, // v0-v3-v4-v5 right
|
|||
1, 1, 1, 1, 1,-1, -1, 1,-1, -1, 1, 1, // v0-v5-v6-v1 top
|
|||
-1, 1, 1, -1, 1,-1, -1,-1,-1, -1,-1, 1, // v1-v6-v7-v2 left
|
|||
-1,-1,-1, 1,-1,-1, 1,-1, 1, -1,-1, 1, // v7-v4-v3-v2 bottom
|
|||
1,-1,-1, -1,-1,-1, -1, 1,-1, 1, 1,-1 ] // v4-v7-v6-v5 back
|
|||
); |
|||
|
|||
// normal array
|
|||
var normals = new Float32Array( |
|||
[ 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, // v0-v1-v2-v3 front
|
|||
1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, // v0-v3-v4-v5 right
|
|||
0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, // v0-v5-v6-v1 top
|
|||
-1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, // v1-v6-v7-v2 left
|
|||
0,-1, 0, 0,-1, 0, 0,-1, 0, 0,-1, 0, // v7-v4-v3-v2 bottom
|
|||
0, 0,-1, 0, 0,-1, 0, 0,-1, 0, 0,-1 ] // v4-v7-v6-v5 back
|
|||
); |
|||
|
|||
|
|||
// texCoord array
|
|||
var texCoords = new Float32Array( |
|||
[ 1, 1, 0, 1, 0, 0, 1, 0, // v0-v1-v2-v3 front
|
|||
0, 1, 0, 0, 1, 0, 1, 1, // v0-v3-v4-v5 right
|
|||
1, 0, 1, 1, 0, 1, 0, 0, // v0-v5-v6-v1 top
|
|||
1, 1, 0, 1, 0, 0, 1, 0, // v1-v6-v7-v2 left
|
|||
0, 0, 1, 0, 1, 1, 0, 1, // v7-v4-v3-v2 bottom
|
|||
0, 0, 1, 0, 1, 1, 0, 1 ] // v4-v7-v6-v5 back
|
|||
); |
|||
|
|||
// index array
|
|||
var indices = new Uint8Array( |
|||
[ 0, 1, 2, 0, 2, 3, // front
|
|||
4, 5, 6, 4, 6, 7, // right
|
|||
8, 9,10, 8,10,11, // top
|
|||
12,13,14, 12,14,15, // left
|
|||
16,17,18, 16,18,19, // bottom
|
|||
20,21,22, 20,22,23 ] // back
|
|||
); |
|||
|
|||
var retval = { }; |
|||
|
|||
retval.normalObject = ctx.createBuffer(); |
|||
ctx.bindBuffer(ctx.ARRAY_BUFFER, retval.normalObject); |
|||
ctx.bufferData(ctx.ARRAY_BUFFER, normals, ctx.STATIC_DRAW); |
|||
|
|||
retval.texCoordObject = ctx.createBuffer(); |
|||
ctx.bindBuffer(ctx.ARRAY_BUFFER, retval.texCoordObject); |
|||
ctx.bufferData(ctx.ARRAY_BUFFER, texCoords, ctx.STATIC_DRAW); |
|||
|
|||
retval.vertexObject = ctx.createBuffer(); |
|||
ctx.bindBuffer(ctx.ARRAY_BUFFER, retval.vertexObject); |
|||
ctx.bufferData(ctx.ARRAY_BUFFER, vertices, ctx.STATIC_DRAW); |
|||
|
|||
ctx.bindBuffer(ctx.ARRAY_BUFFER, null); |
|||
|
|||
retval.indexObject = ctx.createBuffer(); |
|||
ctx.bindBuffer(ctx.ELEMENT_ARRAY_BUFFER, retval.indexObject); |
|||
ctx.bufferData(ctx.ELEMENT_ARRAY_BUFFER, indices, ctx.STATIC_DRAW); |
|||
ctx.bindBuffer(ctx.ELEMENT_ARRAY_BUFFER, null); |
|||
|
|||
retval.numIndices = indices.length; |
|||
|
|||
return retval; |
|||
} |
|||
|
|||
//
|
|||
// makeSphere
|
|||
//
|
|||
// Create a sphere with the passed number of latitude and longitude bands and the passed radius.
|
|||
// Sphere has vertices, normals and texCoords. Create VBOs for each as well as the index array.
|
|||
// Return an object with the following properties:
|
|||
//
|
|||
// normalObject WebGLBuffer object for normals
|
|||
// texCoordObject WebGLBuffer object for texCoords
|
|||
// vertexObject WebGLBuffer object for vertices
|
|||
// indexObject WebGLBuffer object for indices
|
|||
// numIndices The number of indices in the indexObject
|
|||
//
|
|||
function makeSphere(ctx, radius, lats, longs) |
|||
{ |
|||
var geometryData = [ ]; |
|||
var normalData = [ ]; |
|||
var texCoordData = [ ]; |
|||
var indexData = [ ]; |
|||
|
|||
for (var latNumber = 0; latNumber <= lats; ++latNumber) { |
|||
for (var longNumber = 0; longNumber <= longs; ++longNumber) { |
|||
var theta = latNumber * Math.PI / lats; |
|||
var phi = longNumber * 2 * Math.PI / longs; |
|||
var sinTheta = Math.sin(theta); |
|||
var sinPhi = Math.sin(phi); |
|||
var cosTheta = Math.cos(theta); |
|||
var cosPhi = Math.cos(phi); |
|||
|
|||
var x = cosPhi * sinTheta; |
|||
var y = cosTheta; |
|||
var z = sinPhi * sinTheta; |
|||
var u = 1-(longNumber/longs); |
|||
var v = latNumber/lats; |
|||
|
|||
normalData.push(x); |
|||
normalData.push(y); |
|||
normalData.push(z); |
|||
texCoordData.push(u); |
|||
texCoordData.push(v); |
|||
geometryData.push(radius * x); |
|||
geometryData.push(radius * y); |
|||
geometryData.push(radius * z); |
|||
} |
|||
} |
|||
|
|||
for (var latNumber = 0; latNumber < lats; ++latNumber) { |
|||
for (var longNumber = 0; longNumber < longs; ++longNumber) { |
|||
var first = (latNumber * (longs+1)) + longNumber; |
|||
var second = first + longs + 1; |
|||
indexData.push(first); |
|||
indexData.push(second); |
|||
indexData.push(first+1); |
|||
|
|||
indexData.push(second); |
|||
indexData.push(second+1); |
|||
indexData.push(first+1); |
|||
} |
|||
} |
|||
|
|||
var retval = { }; |
|||
|
|||
retval.normalObject = ctx.createBuffer(); |
|||
ctx.bindBuffer(ctx.ARRAY_BUFFER, retval.normalObject); |
|||
ctx.bufferData(ctx.ARRAY_BUFFER, new Float32Array(normalData), ctx.STATIC_DRAW); |
|||
|
|||
retval.texCoordObject = ctx.createBuffer(); |
|||
ctx.bindBuffer(ctx.ARRAY_BUFFER, retval.texCoordObject); |
|||
ctx.bufferData(ctx.ARRAY_BUFFER, new Float32Array(texCoordData), ctx.STATIC_DRAW); |
|||
|
|||
retval.vertexObject = ctx.createBuffer(); |
|||
ctx.bindBuffer(ctx.ARRAY_BUFFER, retval.vertexObject); |
|||
ctx.bufferData(ctx.ARRAY_BUFFER, new Float32Array(geometryData), ctx.STATIC_DRAW); |
|||
|
|||
retval.numIndices = indexData.length; |
|||
retval.indexObject = ctx.createBuffer(); |
|||
ctx.bindBuffer(ctx.ELEMENT_ARRAY_BUFFER, retval.indexObject); |
|||
ctx.bufferData(ctx.ELEMENT_ARRAY_BUFFER, new Uint16Array(indexData), ctx.STREAM_DRAW); |
|||
|
|||
return retval; |
|||
} |
|||
|
|||
// Array of Objects curently loading
|
|||
var g_loadingObjects = []; |
|||
|
|||
// Clears all the Objects currently loading.
|
|||
// This is used to handle context lost events.
|
|||
function clearLoadingObjects() { |
|||
for (var ii = 0; ii < g_loadingObjects.length; ++ii) { |
|||
g_loadingObjects[ii].onreadystatechange = undefined; |
|||
} |
|||
g_loadingObjects = []; |
|||
} |
|||
|
|||
//
|
|||
// loadObj
|
|||
//
|
|||
// Load a .obj file from the passed URL. Return an object with a 'loaded' property set to false.
|
|||
// When the object load is complete, the 'loaded' property becomes true and the following
|
|||
// properties are set:
|
|||
//
|
|||
// normalObject WebGLBuffer object for normals
|
|||
// texCoordObject WebGLBuffer object for texCoords
|
|||
// vertexObject WebGLBuffer object for vertices
|
|||
// indexObject WebGLBuffer object for indices
|
|||
// numIndices The number of indices in the indexObject
|
|||
//
|
|||
function loadObj(ctx, url) |
|||
{ |
|||
var obj = { loaded : false }; |
|||
obj.ctx = ctx; |
|||
var req = new XMLHttpRequest(); |
|||
req.obj = obj; |
|||
g_loadingObjects.push(req); |
|||
req.onreadystatechange = function () { processLoadObj(req) }; |
|||
req.open("GET", url, true); |
|||
req.send(null); |
|||
return obj; |
|||
} |
|||
|
|||
function processLoadObj(req) |
|||
{ |
|||
log("req="+req) |
|||
// only if req shows "complete"
|
|||
if (req.readyState == 4) { |
|||
g_loadingObjects.splice(g_loadingObjects.indexOf(req), 1); |
|||
doLoadObj(req.obj, req.responseText); |
|||
} |
|||
} |
|||
|
|||
function doLoadObj(obj, text) |
|||
{ |
|||
vertexArray = [ ]; |
|||
normalArray = [ ]; |
|||
textureArray = [ ]; |
|||
indexArray = [ ]; |
|||
|
|||
var vertex = [ ]; |
|||
var normal = [ ]; |
|||
var texture = [ ]; |
|||
var facemap = { }; |
|||
var index = 0; |
|||
|
|||
// This is a map which associates a range of indices with a name
|
|||
// The name comes from the 'g' tag (of the form "g NAME"). Indices
|
|||
// are part of one group until another 'g' tag is seen. If any indices
|
|||
// come before a 'g' tag, it is given the group name "_unnamed"
|
|||
// 'group' is an object whose property names are the group name and
|
|||
// whose value is a 2 element array with [<first index>, <num indices>]
|
|||
var groups = { }; |
|||
var currentGroup = [-1, 0]; |
|||
groups["_unnamed"] = currentGroup; |
|||
|
|||
var lines = text.split("\n"); |
|||
for (var lineIndex in lines) { |
|||
var line = lines[lineIndex].replace(/[ \t]+/g, " ").replace(/\s\s*$/, ""); |
|||
|
|||
// ignore comments
|
|||
if (line[0] == "#") |
|||
continue; |
|||
|
|||
var array = line.split(" "); |
|||
if (array[0] == "g") { |
|||
// new group
|
|||
currentGroup = [indexArray.length, 0]; |
|||
groups[array[1]] = currentGroup; |
|||
} |
|||
else if (array[0] == "v") { |
|||
// vertex
|
|||
vertex.push(parseFloat(array[1])); |
|||
vertex.push(parseFloat(array[2])); |
|||
vertex.push(parseFloat(array[3])); |
|||
} |
|||
else if (array[0] == "vt") { |
|||
// normal
|
|||
texture.push(parseFloat(array[1])); |
|||
texture.push(parseFloat(array[2])); |
|||
} |
|||
else if (array[0] == "vn") { |
|||
// normal
|
|||
normal.push(parseFloat(array[1])); |
|||
normal.push(parseFloat(array[2])); |
|||
normal.push(parseFloat(array[3])); |
|||
} |
|||
else if (array[0] == "f") { |
|||
// face
|
|||
if (array.length != 4) { |
|||
log("*** Error: face '"+line+"' not handled"); |
|||
continue; |
|||
} |
|||
|
|||
for (var i = 1; i < 4; ++i) { |
|||
if (!(array[i] in facemap)) { |
|||
// add a new entry to the map and arrays
|
|||
var f = array[i].split("/"); |
|||
var vtx, nor, tex; |
|||
|
|||
if (f.length == 1) { |
|||
vtx = parseInt(f[0]) - 1; |
|||
nor = vtx; |
|||
tex = vtx; |
|||
} |
|||
else if (f.length = 3) { |
|||
vtx = parseInt(f[0]) - 1; |
|||
tex = parseInt(f[1]) - 1; |
|||
nor = parseInt(f[2]) - 1; |
|||
} |
|||
else { |
|||
obj.ctx.console.log("*** Error: did not understand face '"+array[i]+"'"); |
|||
return null; |
|||
} |
|||
|
|||
// do the vertices
|
|||
var x = 0; |
|||
var y = 0; |
|||
var z = 0; |
|||
if (vtx * 3 + 2 < vertex.length) { |
|||
x = vertex[vtx*3]; |
|||
y = vertex[vtx*3+1]; |
|||
z = vertex[vtx*3+2]; |
|||
} |
|||
vertexArray.push(x); |
|||
vertexArray.push(y); |
|||
vertexArray.push(z); |
|||
|
|||
// do the textures
|
|||
x = 0; |
|||
y = 0; |
|||
if (tex * 2 + 1 < texture.length) { |
|||
x = texture[tex*2]; |
|||
y = texture[tex*2+1]; |
|||
} |
|||
textureArray.push(x); |
|||
textureArray.push(y); |
|||
|
|||
// do the normals
|
|||
x = 0; |
|||
y = 0; |
|||
z = 1; |
|||
if (nor * 3 + 2 < normal.length) { |
|||
x = normal[nor*3]; |
|||
y = normal[nor*3+1]; |
|||
z = normal[nor*3+2]; |
|||
} |
|||
normalArray.push(x); |
|||
normalArray.push(y); |
|||
normalArray.push(z); |
|||
|
|||
facemap[array[i]] = index++; |
|||
} |
|||
|
|||
indexArray.push(facemap[array[i]]); |
|||
currentGroup[1]++; |
|||
} |
|||
} |
|||
} |
|||
|
|||
// set the VBOs
|
|||
obj.normalObject = obj.ctx.createBuffer(); |
|||
obj.ctx.bindBuffer(obj.ctx.ARRAY_BUFFER, obj.normalObject); |
|||
obj.ctx.bufferData(obj.ctx.ARRAY_BUFFER, new Float32Array(normalArray), obj.ctx.STATIC_DRAW); |
|||
|
|||
obj.texCoordObject = obj.ctx.createBuffer(); |
|||
obj.ctx.bindBuffer(obj.ctx.ARRAY_BUFFER, obj.texCoordObject); |
|||
obj.ctx.bufferData(obj.ctx.ARRAY_BUFFER, new Float32Array(textureArray), obj.ctx.STATIC_DRAW); |
|||
|
|||
obj.vertexObject = obj.ctx.createBuffer(); |
|||
obj.ctx.bindBuffer(obj.ctx.ARRAY_BUFFER, obj.vertexObject); |
|||
obj.ctx.bufferData(obj.ctx.ARRAY_BUFFER, new Float32Array(vertexArray), obj.ctx.STATIC_DRAW); |
|||
|
|||
obj.numIndices = indexArray.length; |
|||
obj.indexObject = obj.ctx.createBuffer(); |
|||
obj.ctx.bindBuffer(obj.ctx.ELEMENT_ARRAY_BUFFER, obj.indexObject); |
|||
obj.ctx.bufferData(obj.ctx.ELEMENT_ARRAY_BUFFER, new Uint16Array(indexArray), obj.ctx.STREAM_DRAW); |
|||
|
|||
obj.groups = groups; |
|||
|
|||
obj.loaded = true; |
|||
} |
|||
|
|||
// Array of images curently loading
|
|||
var g_loadingImages = []; |
|||
|
|||
// Clears all the images currently loading.
|
|||
// This is used to handle context lost events.
|
|||
function clearLoadingImages() { |
|||
for (var ii = 0; ii < g_loadingImages.length; ++ii) { |
|||
g_loadingImages[ii].onload = undefined; |
|||
} |
|||
g_loadingImages = []; |
|||
} |
|||
|
|||
//
|
|||
// loadImageTexture
|
|||
//
|
|||
// Load the image at the passed url, place it in a new WebGLTexture object and return the WebGLTexture.
|
|||
//
|
|||
function loadImageTexture(ctx, url) |
|||
{ |
|||
var texture = ctx.createTexture(); |
|||
var image = new Image(); |
|||
g_loadingImages.push(image); |
|||
image.onload = function() { doLoadImageTexture(ctx, image, texture) } |
|||
image.src = url; |
|||
return texture; |
|||
} |
|||
|
|||
function doLoadImageTexture(ctx, image, texture) |
|||
{ |
|||
g_loadingImages.splice(g_loadingImages.indexOf(image), 1); |
|||
ctx.bindTexture(ctx.TEXTURE_2D, texture); |
|||
ctx.texImage2D(ctx.TEXTURE_2D, 0, ctx.RGBA, ctx.RGBA, ctx.UNSIGNED_BYTE, image); |
|||
ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_MAG_FILTER, ctx.LINEAR); |
|||
ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_MIN_FILTER, ctx.LINEAR); |
|||
ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_WRAP_S, ctx.CLAMP_TO_EDGE); |
|||
ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_WRAP_T, ctx.CLAMP_TO_EDGE); |
|||
//ctx.generateMipmap(ctx.TEXTURE_2D)
|
|||
ctx.bindTexture(ctx.TEXTURE_2D, null); |
|||
} |
|||
|
|||
//
|
|||
// Framerate object
|
|||
//
|
|||
// This object keeps track of framerate and displays it as the innerHTML text of the
|
|||
// HTML element with the passed id. Once created you call snapshot at the end
|
|||
// of every rendering cycle. Every 500ms the framerate is updated in the HTML element.
|
|||
//
|
|||
Framerate = function(id) |
|||
{ |
|||
this.numFramerates = 10; |
|||
this.framerateUpdateInterval = 500; |
|||
this.id = id; |
|||
|
|||
this.renderTime = -1; |
|||
this.framerates = [ ]; |
|||
self = this; |
|||
var fr = function() { self.updateFramerate() } |
|||
setInterval(fr, this.framerateUpdateInterval); |
|||
} |
|||
|
|||
Framerate.prototype.updateFramerate = function() |
|||
{ |
|||
var tot = 0; |
|||
for (var i = 0; i < this.framerates.length; ++i) |
|||
tot += this.framerates[i]; |
|||
|
|||
var framerate = tot / this.framerates.length; |
|||
framerate = Math.round(framerate); |
|||
document.getElementById(this.id).innerHTML = "Framerate:"+framerate+"fps"; |
|||
} |
|||
|
|||
Framerate.prototype.snapshot = function() |
|||
{ |
|||
if (this.renderTime < 0) |
|||
this.renderTime = new Date().getTime(); |
|||
else { |
|||
var newTime = new Date().getTime(); |
|||
var t = newTime - this.renderTime; |
|||
if (t == 0) |
|||
return; |
|||
var framerate = 1000/t; |
|||
this.framerates.push(framerate); |
|||
while (this.framerates.length > this.numFramerates) |
|||
this.framerates.shift(); |
|||
this.renderTime = newTime; |
|||
} |
|||
} |
|||
File diff suppressed because it is too large
@ -0,0 +1,70 @@ |
|||
<!DOCTYPE html> |
|||
<html> |
|||
<head> |
|||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> |
|||
<title>TP - WebGL</title> |
|||
<link rel="stylesheet" href="tp.css" type="text/css" media="all"> |
|||
|
|||
<script type="text/javascript" src="webgl-utils.js"></script> |
|||
<script type="text/javascript" src="webgl-debug.js"></script> |
|||
<script type="text/javascript" src="J3DI.js"></script> |
|||
<script type="text/javascript" src="J3DIMath.js"></script> |
|||
<script type="text/javascript" src="main.js"></script> |
|||
|
|||
<script id="nuanceurSommets" type="x-shader/x-vertex"> |
|||
uniform mat4 u_modelViewProjMatrix; |
|||
uniform mat4 u_normalMatrix; |
|||
uniform vec3 lightDir; |
|||
|
|||
attribute vec4 vPosition; |
|||
attribute vec4 vColor; |
|||
|
|||
varying float v_NdotL; |
|||
varying vec4 v_color; |
|||
|
|||
void main() |
|||
{ |
|||
// assigner la position du sommet |
|||
gl_Position = u_modelViewProjMatrix * vPosition; |
|||
|
|||
// assigner les coordonnées de texture |
|||
v_color = vColor; |
|||
} |
|||
</script> |
|||
|
|||
<script id="nuanceurFragments" type="x-shader/x-fragment"> |
|||
// informer du degré de précision qu'on veut dans les calculs |
|||
// (Plus à ce sujet: http://stackoverflow.com/questions/5366416/in-opengl-es-2-0-glsl-where-do-you-need-precision-specifiers/6336285#6336285 ) |
|||
precision mediump float; |
|||
|
|||
uniform sampler2D laTexture; |
|||
uniform vec2 positionSouris; |
|||
|
|||
varying float v_NdotL; |
|||
varying vec4 v_color; |
|||
|
|||
void main() |
|||
{ |
|||
// obtenir la couleur de la texture |
|||
vec4 coul = v_color; |
|||
if ( length( positionSouris - gl_FragCoord.xy ) < 50.0 ) |
|||
{ |
|||
vec4 coulCercle = vec4( 1.0, 0.0, 0.0, 0.7 ); |
|||
coul = coul * (1.0-coulCercle.a) + coulCercle * coulCercle.a; |
|||
// coul = coul * coulCercle; // réponse aussi acceptée |
|||
} |
|||
gl_FragColor = vec4( coul.rgb, coul.a ); |
|||
} |
|||
</script> |
|||
|
|||
</head> |
|||
|
|||
<body onload="TPdebut()"> |
|||
<canvas class="canevas" id="tp-canevas" height="600" width="900"> |
|||
Si vous voyez ceci, votre navigateur ne supporte pas webgl. |
|||
</canvas> |
|||
|
|||
<div class="text">Les <a href="http://www.khronos.org/registry/webgl/specs/latest/1.0/">spécifications de Webgl</a> sont<br>remplies d'informations intéressantes.</div> |
|||
|
|||
</body> |
|||
</html> |
|||
@ -0,0 +1,254 @@ |
|||
// Prénoms, noms et matricule des membres de l'équipe:
|
|||
// - Prénom1 NOM1 (matricule1)
|
|||
// - Prénom2 NOM2 (matricule2)
|
|||
|
|||
|
|||
// déclaration d'une structure pour contenir toutes les variables globales
|
|||
var glob = { }; |
|||
|
|||
function handleMouseDown(event) |
|||
{ |
|||
glob.mouseDown = true; |
|||
glob.positionSourisX = event.clientX-15; |
|||
glob.positionSourisY = event.clientY-15; |
|||
} |
|||
|
|||
function handleMouseUp(event) |
|||
{ |
|||
glob.mouseDown = false; |
|||
glob.positionSourisX = null; |
|||
glob.positionSourisY = null; |
|||
} |
|||
|
|||
function handleMouseMove(event) |
|||
{ |
|||
if (!glob.mouseDown) return; |
|||
glob.positionSourisX = event.clientX-15; |
|||
glob.positionSourisY = event.clientY-15; |
|||
} |
|||
|
|||
//
|
|||
// makeRepere
|
|||
//
|
|||
// Create a repere with vertices, normals and texCoords. Create VBOs for each as well as the index array.
|
|||
// Return an object with the following properties:
|
|||
//
|
|||
// normalObject WebGLBuffer object for normals
|
|||
// texCoordObject WebGLBuffer object for texCoords
|
|||
// vertexObject WebGLBuffer object for vertices
|
|||
// indexObject WebGLBuffer object for indices
|
|||
// numIndices The number of indices in the indexObject
|
|||
//
|
|||
function makeRepere(ctx) |
|||
{ |
|||
// box
|
|||
// v2
|
|||
// |
|
|||
// |
|
|||
// |
|
|||
// v0 o------v1
|
|||
// /
|
|||
// v3
|
|||
//
|
|||
// vertex coords array
|
|||
var vertices = new Float32Array( |
|||
[ 0, 0, 0, 1, 0, 0, // v0 -> v1 vec X
|
|||
0, 0, 0, 0, 1, 0, // v0 -> v2 vec Y
|
|||
0, 0, 0, 0, 0, 1 ]// v0 -> v3 vec Z
|
|||
); |
|||
|
|||
// colors array
|
|||
var colors = new Float32Array( |
|||
[ 1, 0, 0, 1, 0, 0, // v0 -> v1 vec X
|
|||
0, 1, 0, 0, 1, 0, // v0 -> v2 vec Y
|
|||
0, 0, 1, 0, 0, 1 ]// v0 -> v3 vec Z
|
|||
); |
|||
|
|||
// index array
|
|||
var indices = new Uint8Array( |
|||
[ 0, 1, |
|||
2, 3, |
|||
4, 5 ] |
|||
); |
|||
|
|||
var retval = { }; |
|||
|
|||
retval.vertexObject = ctx.createBuffer(); |
|||
ctx.bindBuffer(ctx.ARRAY_BUFFER, retval.vertexObject); |
|||
ctx.bufferData(ctx.ARRAY_BUFFER, vertices, ctx.STATIC_DRAW); |
|||
|
|||
retval.colorObject = ctx.createBuffer(); |
|||
ctx.bindBuffer(ctx.ARRAY_BUFFER, retval.colorObject); |
|||
ctx.bufferData(ctx.ARRAY_BUFFER, colors, ctx.STATIC_DRAW); |
|||
|
|||
ctx.bindBuffer(ctx.ARRAY_BUFFER, null); |
|||
|
|||
retval.indexObject = ctx.createBuffer(); |
|||
ctx.bindBuffer(ctx.ELEMENT_ARRAY_BUFFER, retval.indexObject); |
|||
ctx.bufferData(ctx.ELEMENT_ARRAY_BUFFER, indices, ctx.STATIC_DRAW); |
|||
ctx.bindBuffer(ctx.ELEMENT_ARRAY_BUFFER, null); |
|||
|
|||
retval.numIndices = indices.length; |
|||
|
|||
return retval; |
|||
} |
|||
function TPchargerTextures() |
|||
{ |
|||
// Charger une image utilisée comme texture. (Retourne un objet WebGLTexture)
|
|||
glob.texture1 = loadImageTexture( gl, "images/Brouillard.jpg" ); |
|||
glob.texture2 = loadImageTexture( gl, "images/Exploration.jpg" ); |
|||
//glob.texture3 = loadImageTexture( gl, "images/Modelisation.jpg" );
|
|||
} |
|||
|
|||
function TPcreerModele() |
|||
{ |
|||
// Créer une boîte. Au retour, 'glob.box' contient une structure avec
|
|||
// les VBOs pour les sommets, normales, coordonnées de texture et connectivité.
|
|||
glob.box = makeRepere( gl ); |
|||
|
|||
// Initialiser les attributs pour les sommets, les normales et les coordonnées de texture
|
|||
// (dans le même ordre qu'à l'appel à simpleSetup() dans la fonction TPinitialiser())
|
|||
gl.enableVertexAttribArray( 0 ); |
|||
gl.bindBuffer( gl.ARRAY_BUFFER, glob.box.vertexObject ); |
|||
gl.vertexAttribPointer( 0, 3, gl.FLOAT, false, 0, 0 ); |
|||
|
|||
gl.enableVertexAttribArray( 1 ); |
|||
gl.bindBuffer( gl.ARRAY_BUFFER, glob.box.colorObject ); |
|||
gl.vertexAttribPointer( 1, 3, gl.FLOAT, false, 0, 0 ); |
|||
|
|||
// Lier le tableau de connectivité
|
|||
gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, glob.box.indexObject ); |
|||
} |
|||
|
|||
function TPinitialiser() |
|||
{ |
|||
// Initialiser webgl
|
|||
var gl = initWebGL( "tp-canevas" ); // L'identificateur du canevas
|
|||
if (!gl) return; |
|||
|
|||
glob.program = simpleSetup( gl, |
|||
// Les identificateurs des deux nuanceurs
|
|||
"nuanceurSommets", "nuanceurFragments", |
|||
// Les attributs utilisés par les nuanceurs (donnés dans le même ordre que leur indice)
|
|||
[ "vPosition", "vColor" ], |
|||
// La couleur de fond et la profondeur
|
|||
[ 0, 0, 0.3, 1 ], 100); |
|||
|
|||
// Les angles courants de rotation
|
|||
glob.angleRotX = 0, glob.angleRotY = 0, glob.angleRotZ = 0; |
|||
// Les incréments à chaque affichage
|
|||
glob.incrRotX = 0.2; glob.incrRotY = 0.3; glob.incrRotZ = 0.4; |
|||
|
|||
// Créer les matrices nécessaires et assigner les assigner dans le programme
|
|||
glob.modelViewMatrix = new J3DIMatrix4(); |
|||
// glob.u_modelViewMatrixLoc n'est pas utile car modelViewMatrix n'est pas utilisé dans les nuanceurs
|
|||
glob.mvpMatrix = new J3DIMatrix4(); |
|||
glob.u_modelViewProjMatrixLoc = gl.getUniformLocation( glob.program, "u_modelViewProjMatrix" ); |
|||
glob.normalMatrix = new J3DIMatrix4(); |
|||
glob.u_normalMatrixLoc = gl.getUniformLocation( glob.program, "u_normalMatrix" ); |
|||
|
|||
// terminer l'initialisation
|
|||
TPchargerTextures(); |
|||
TPcreerModele(); |
|||
|
|||
glob.mouseDown = false; |
|||
glob.positionSourisX = null; |
|||
glob.positionSourisY = null; |
|||
glob.canevas.onmousedown = handleMouseDown; |
|||
glob.canevas.onmouseup = handleMouseUp; |
|||
glob.canevas.onmousemove = handleMouseMove; |
|||
|
|||
// Initialiser les variables uniformes pour les nuanceurs
|
|||
gl.uniform3f( gl.getUniformLocation( glob.program, "lightDir" ), 0, 0, 1 ); |
|||
gl.uniform1i( gl.getUniformLocation( glob.program, "laTexture" ), 0 ); |
|||
gl.uniform2f( gl.getUniformLocation( glob.program, "positionSouris" ), glob.positionSourisX, glob.positionSourisY ); |
|||
|
|||
return gl; |
|||
} |
|||
|
|||
function TPafficherModele( gl, num ) |
|||
{ |
|||
// Incrémenter les angles de rotation
|
|||
glob.angleRotX += glob.incrRotX; if ( glob.angleRotX >= 360.0 ) glob.angleRotX -= 360.0; |
|||
glob.angleRotY += glob.incrRotY; if ( glob.angleRotY >= 360.0 ) glob.angleRotY -= 360.0; |
|||
glob.angleRotZ += glob.incrRotZ; if ( glob.angleRotZ >= 360.0 ) glob.angleRotZ -= 360.0; |
|||
|
|||
// Construire la matrice de modélisation
|
|||
glob.modelViewMatrix.makeIdentity(); |
|||
glob.modelViewMatrix.lookat( 0, 0, 7, 0, 0, 0, 0, 1, 0 ); |
|||
var sens = ( num == 1 ) ? +1 : -1; |
|||
glob.modelViewMatrix.rotate( sens*glob.angleRotX, 1.0, 0.0, 0.0 ); |
|||
glob.modelViewMatrix.rotate( sens*glob.angleRotY, 0.0, 1.0, 0.0 ); |
|||
glob.modelViewMatrix.rotate( sens*glob.angleRotZ, 0.0, 0.0, 1.0 ); |
|||
|
|||
// Construire le produit de matrice "modélisation * projection" et la passer aux nuanceurs
|
|||
glob.mvpMatrix.load( glob.perspectiveMatrix ); |
|||
glob.mvpMatrix.multiply( glob.modelViewMatrix ); |
|||
glob.mvpMatrix.setUniform( gl, glob.u_modelViewProjMatrixLoc, false ); |
|||
|
|||
// Construire la matrice de transformation des normales et la passer aux nuanceurs
|
|||
glob.normalMatrix.load( glob.modelViewMatrix ); |
|||
glob.normalMatrix.invert(); |
|||
glob.normalMatrix.transpose(); |
|||
glob.normalMatrix.setUniform( gl, glob.u_normalMatrixLoc, false ); |
|||
|
|||
// Activer la texture à utiliser
|
|||
gl.bindTexture( gl.TEXTURE_2D, ( num == 1 ) ? glob.texture1 : glob.texture2 ); |
|||
|
|||
gl.uniform2f( gl.getUniformLocation( glob.program, "positionSouris" ), glob.positionSourisX, glob.canevas.height-glob.positionSourisY ); |
|||
|
|||
// Tracer le cube
|
|||
gl.drawElements( gl.LINES, glob.box.numIndices, gl.UNSIGNED_BYTE, 0 ); |
|||
} |
|||
|
|||
function TPafficherScene(gl) |
|||
{ |
|||
glob.perspectiveMatrix = new J3DIMatrix4(); |
|||
glob.perspectiveMatrix.perspective( 40, glob.canevas.width / glob.canevas.height, 1, 10 ); |
|||
|
|||
// Effacer le canevas
|
|||
gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT ); |
|||
|
|||
// Définir la clôture 1
|
|||
gl.viewport( 0, 0, glob.canevas.width, glob.canevas.height ); |
|||
// Tracer le modèle
|
|||
TPafficherModele( gl, 1 ); |
|||
} |
|||
|
|||
var requestId; |
|||
function TPdebut() |
|||
{ |
|||
glob.canevas = document.getElementById('tp-canevas'); |
|||
//glob.canevas = WebGLDebugUtils.makeLostContextSimulatingCanvas(c);
|
|||
// indiquer de perdre le contexte afin de tester
|
|||
//glob.canevas.loseContextInNCalls(15);
|
|||
glob.canevas.addEventListener('webglcontextlost', handleContextLost, false); |
|||
glob.canevas.addEventListener('webglcontextrestored', handleContextRestored, false); |
|||
|
|||
var gl = TPinitialiser(); |
|||
if ( !gl ) return; |
|||
|
|||
var displayFunc = function() |
|||
{ |
|||
TPafficherScene(gl); |
|||
requestId = window.requestAnimFrame( displayFunc, glob.canevas ); |
|||
}; |
|||
displayFunc(); |
|||
|
|||
function handleContextLost(e) |
|||
{ |
|||
e.preventDefault(); |
|||
clearLoadingImages(); |
|||
if ( requestId !== undefined ) |
|||
{ |
|||
window.cancelAnimFrame(requestId); |
|||
requestId = undefined; |
|||
} |
|||
} |
|||
|
|||
function handleContextRestored() |
|||
{ |
|||
TPinitialiser(); |
|||
displayFunc(); |
|||
} |
|||
} |
|||
@ -0,0 +1,16 @@ |
|||
.text { |
|||
position: absolute; |
|||
top: 20px; |
|||
left: 20px; |
|||
font-size: 1em; |
|||
color: orange; |
|||
} |
|||
|
|||
a:link { color: yellow; } |
|||
a:hover { color: #bbaa00; } |
|||
|
|||
.canevas { |
|||
height: 600px; |
|||
width: 900px; |
|||
border: 5px solid orange; |
|||
} |
|||
@ -0,0 +1,875 @@ |
|||
/* |
|||
** Copyright (c) 2012 The Khronos Group Inc. |
|||
** |
|||
** Permission is hereby granted, free of charge, to any person obtaining a |
|||
** copy of this software and/or associated documentation files (the |
|||
** "Materials"), to deal in the Materials without restriction, including |
|||
** without limitation the rights to use, copy, modify, merge, publish, |
|||
** distribute, sublicense, and/or sell copies of the Materials, and to |
|||
** permit persons to whom the Materials are furnished to do so, subject to |
|||
** the following conditions: |
|||
** |
|||
** The above copyright notice and this permission notice shall be included |
|||
** in all copies or substantial portions of the Materials. |
|||
** |
|||
** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
|||
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
|||
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. |
|||
** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY |
|||
** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, |
|||
** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE |
|||
** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. |
|||
*/ |
|||
|
|||
// Various functions for helping debug WebGL apps.
|
|||
|
|||
WebGLDebugUtils = function() { |
|||
|
|||
/** |
|||
* Wrapped logging function. |
|||
* @param {string} msg Message to log. |
|||
*/ |
|||
var log = function(msg) { |
|||
if (window.console && window.console.log) { |
|||
window.console.log(msg); |
|||
} |
|||
}; |
|||
|
|||
/** |
|||
* Wrapped error logging function. |
|||
* @param {string} msg Message to log. |
|||
*/ |
|||
var error = function(msg) { |
|||
if (window.console && window.console.error) { |
|||
window.console.error(msg); |
|||
} else { |
|||
log(msg); |
|||
} |
|||
}; |
|||
|
|||
/** |
|||
* Which arguements are enums. |
|||
* @type {!Object.<number, string>} |
|||
*/ |
|||
var glValidEnumContexts = { |
|||
|
|||
// Generic setters and getters
|
|||
|
|||
'enable': { 0:true }, |
|||
'disable': { 0:true }, |
|||
'getParameter': { 0:true }, |
|||
|
|||
// Rendering
|
|||
|
|||
'drawArrays': { 0:true }, |
|||
'drawElements': { 0:true, 2:true }, |
|||
|
|||
// Shaders
|
|||
|
|||
'createShader': { 0:true }, |
|||
'getShaderParameter': { 1:true }, |
|||
'getProgramParameter': { 1:true }, |
|||
|
|||
// Vertex attributes
|
|||
|
|||
'getVertexAttrib': { 1:true }, |
|||
'vertexAttribPointer': { 2:true }, |
|||
|
|||
// Textures
|
|||
|
|||
'bindTexture': { 0:true }, |
|||
'activeTexture': { 0:true }, |
|||
'getTexParameter': { 0:true, 1:true }, |
|||
'texParameterf': { 0:true, 1:true }, |
|||
'texParameteri': { 0:true, 1:true, 2:true }, |
|||
'texImage2D': { 0:true, 2:true, 6:true, 7:true }, |
|||
'texSubImage2D': { 0:true, 6:true, 7:true }, |
|||
'copyTexImage2D': { 0:true, 2:true }, |
|||
'copyTexSubImage2D': { 0:true }, |
|||
'generateMipmap': { 0:true }, |
|||
|
|||
// Buffer objects
|
|||
|
|||
'bindBuffer': { 0:true }, |
|||
'bufferData': { 0:true, 2:true }, |
|||
'bufferSubData': { 0:true }, |
|||
'getBufferParameter': { 0:true, 1:true }, |
|||
|
|||
// Renderbuffers and framebuffers
|
|||
|
|||
'pixelStorei': { 0:true, 1:true }, |
|||
'readPixels': { 4:true, 5:true }, |
|||
'bindRenderbuffer': { 0:true }, |
|||
'bindFramebuffer': { 0:true }, |
|||
'checkFramebufferStatus': { 0:true }, |
|||
'framebufferRenderbuffer': { 0:true, 1:true, 2:true }, |
|||
'framebufferTexture2D': { 0:true, 1:true, 2:true }, |
|||
'getFramebufferAttachmentParameter': { 0:true, 1:true, 2:true }, |
|||
'getRenderbufferParameter': { 0:true, 1:true }, |
|||
'renderbufferStorage': { 0:true, 1:true }, |
|||
|
|||
// Frame buffer operations (clear, blend, depth test, stencil)
|
|||
|
|||
'clear': { 0:true }, |
|||
'depthFunc': { 0:true }, |
|||
'blendFunc': { 0:true, 1:true }, |
|||
'blendFuncSeparate': { 0:true, 1:true, 2:true, 3:true }, |
|||
'blendEquation': { 0:true }, |
|||
'blendEquationSeparate': { 0:true, 1:true }, |
|||
'stencilFunc': { 0:true }, |
|||
'stencilFuncSeparate': { 0:true, 1:true }, |
|||
'stencilMaskSeparate': { 0:true }, |
|||
'stencilOp': { 0:true, 1:true, 2:true }, |
|||
'stencilOpSeparate': { 0:true, 1:true, 2:true, 3:true }, |
|||
|
|||
// Culling
|
|||
|
|||
'cullFace': { 0:true }, |
|||
'frontFace': { 0:true }, |
|||
}; |
|||
|
|||
/** |
|||
* Map of numbers to names. |
|||
* @type {Object} |
|||
*/ |
|||
var glEnums = null; |
|||
|
|||
/** |
|||
* Initializes this module. Safe to call more than once. |
|||
* @param {!WebGLRenderingContext} ctx A WebGL context. If |
|||
* you have more than one context it doesn't matter which one |
|||
* you pass in, it is only used to pull out constants. |
|||
*/ |
|||
function init(ctx) { |
|||
if (glEnums == null) { |
|||
glEnums = { }; |
|||
for (var propertyName in ctx) { |
|||
if (typeof ctx[propertyName] == 'number') { |
|||
glEnums[ctx[propertyName]] = propertyName; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Checks the utils have been initialized. |
|||
*/ |
|||
function checkInit() { |
|||
if (glEnums == null) { |
|||
throw 'WebGLDebugUtils.init(ctx) not called'; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Returns true or false if value matches any WebGL enum |
|||
* @param {*} value Value to check if it might be an enum. |
|||
* @return {boolean} True if value matches one of the WebGL defined enums |
|||
*/ |
|||
function mightBeEnum(value) { |
|||
checkInit(); |
|||
return (glEnums[value] !== undefined); |
|||
} |
|||
|
|||
/** |
|||
* Gets an string version of an WebGL enum. |
|||
* |
|||
* Example: |
|||
* var str = WebGLDebugUtil.glEnumToString(ctx.getError()); |
|||
* |
|||
* @param {number} value Value to return an enum for |
|||
* @return {string} The string version of the enum. |
|||
*/ |
|||
function glEnumToString(value) { |
|||
checkInit(); |
|||
var name = glEnums[value]; |
|||
return (name !== undefined) ? name : |
|||
("*UNKNOWN WebGL ENUM (0x" + value.toString(16) + ")"); |
|||
} |
|||
|
|||
/** |
|||
* Returns the string version of a WebGL argument. |
|||
* Attempts to convert enum arguments to strings. |
|||
* @param {string} functionName the name of the WebGL function. |
|||
* @param {number} argumentIndx the index of the argument. |
|||
* @param {*} value The value of the argument. |
|||
* @return {string} The value as a string. |
|||
*/ |
|||
function glFunctionArgToString(functionName, argumentIndex, value) { |
|||
var funcInfo = glValidEnumContexts[functionName]; |
|||
if (funcInfo !== undefined) { |
|||
if (funcInfo[argumentIndex]) { |
|||
return glEnumToString(value); |
|||
} |
|||
} |
|||
if (value === null) { |
|||
return "null"; |
|||
} else if (value === undefined) { |
|||
return "undefined"; |
|||
} else { |
|||
return value.toString(); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Converts the arguments of a WebGL function to a string. |
|||
* Attempts to convert enum arguments to strings. |
|||
* |
|||
* @param {string} functionName the name of the WebGL function. |
|||
* @param {number} args The arguments. |
|||
* @return {string} The arguments as a string. |
|||
*/ |
|||
function glFunctionArgsToString(functionName, args) { |
|||
// apparently we can't do args.join(",");
|
|||
var argStr = ""; |
|||
for (var ii = 0; ii < args.length; ++ii) { |
|||
argStr += ((ii == 0) ? '' : ', ') + |
|||
glFunctionArgToString(functionName, ii, args[ii]); |
|||
} |
|||
return argStr; |
|||
}; |
|||
|
|||
|
|||
function makePropertyWrapper(wrapper, original, propertyName) { |
|||
//log("wrap prop: " + propertyName);
|
|||
wrapper.__defineGetter__(propertyName, function() { |
|||
return original[propertyName]; |
|||
}); |
|||
// TODO(gmane): this needs to handle properties that take more than
|
|||
// one value?
|
|||
wrapper.__defineSetter__(propertyName, function(value) { |
|||
//log("set: " + propertyName);
|
|||
original[propertyName] = value; |
|||
}); |
|||
} |
|||
|
|||
// Makes a function that calls a function on another object.
|
|||
function makeFunctionWrapper(original, functionName) { |
|||
//log("wrap fn: " + functionName);
|
|||
var f = original[functionName]; |
|||
return function() { |
|||
//log("call: " + functionName);
|
|||
var result = f.apply(original, arguments); |
|||
return result; |
|||
}; |
|||
} |
|||
|
|||
/** |
|||
* Given a WebGL context returns a wrapped context that calls |
|||
* gl.getError after every command and calls a function if the |
|||
* result is not gl.NO_ERROR. |
|||
* |
|||
* @param {!WebGLRenderingContext} ctx The webgl context to |
|||
* wrap. |
|||
* @param {!function(err, funcName, args): void} opt_onErrorFunc |
|||
* The function to call when gl.getError returns an |
|||
* error. If not specified the default function calls |
|||
* console.log with a message. |
|||
* @param {!function(funcName, args): void} opt_onFunc The |
|||
* function to call when each webgl function is called. |
|||
* You can use this to log all calls for example. |
|||
*/ |
|||
function makeDebugContext(ctx, opt_onErrorFunc, opt_onFunc) { |
|||
init(ctx); |
|||
opt_onErrorFunc = opt_onErrorFunc || function(err, functionName, args) { |
|||
// apparently we can't do args.join(",");
|
|||
var argStr = ""; |
|||
for (var ii = 0; ii < args.length; ++ii) { |
|||
argStr += ((ii == 0) ? '' : ', ') + |
|||
glFunctionArgToString(functionName, ii, args[ii]); |
|||
} |
|||
error("WebGL error "+ glEnumToString(err) + " in "+ functionName + |
|||
"(" + argStr + ")"); |
|||
}; |
|||
|
|||
// Holds booleans for each GL error so after we get the error ourselves
|
|||
// we can still return it to the client app.
|
|||
var glErrorShadow = { }; |
|||
|
|||
// Makes a function that calls a WebGL function and then calls getError.
|
|||
function makeErrorWrapper(ctx, functionName) { |
|||
return function() { |
|||
if (opt_onFunc) { |
|||
opt_onFunc(functionName, arguments); |
|||
} |
|||
var result = ctx[functionName].apply(ctx, arguments); |
|||
var err = ctx.getError(); |
|||
if (err != 0) { |
|||
glErrorShadow[err] = true; |
|||
opt_onErrorFunc(err, functionName, arguments); |
|||
} |
|||
return result; |
|||
}; |
|||
} |
|||
|
|||
// Make a an object that has a copy of every property of the WebGL context
|
|||
// but wraps all functions.
|
|||
var wrapper = {}; |
|||
for (var propertyName in ctx) { |
|||
if (typeof ctx[propertyName] == 'function') { |
|||
wrapper[propertyName] = makeErrorWrapper(ctx, propertyName); |
|||
} else { |
|||
makePropertyWrapper(wrapper, ctx, propertyName); |
|||
} |
|||
} |
|||
|
|||
// Override the getError function with one that returns our saved results.
|
|||
wrapper.getError = function() { |
|||
for (var err in glErrorShadow) { |
|||
if (glErrorShadow.hasOwnProperty(err)) { |
|||
if (glErrorShadow[err]) { |
|||
glErrorShadow[err] = false; |
|||
return err; |
|||
} |
|||
} |
|||
} |
|||
return ctx.NO_ERROR; |
|||
}; |
|||
|
|||
return wrapper; |
|||
} |
|||
|
|||
function resetToInitialState(ctx) { |
|||
var numAttribs = ctx.getParameter(ctx.MAX_VERTEX_ATTRIBS); |
|||
var tmp = ctx.createBuffer(); |
|||
ctx.bindBuffer(ctx.ARRAY_BUFFER, tmp); |
|||
for (var ii = 0; ii < numAttribs; ++ii) { |
|||
ctx.disableVertexAttribArray(ii); |
|||
ctx.vertexAttribPointer(ii, 4, ctx.FLOAT, false, 0, 0); |
|||
ctx.vertexAttrib1f(ii, 0); |
|||
} |
|||
ctx.deleteBuffer(tmp); |
|||
|
|||
var numTextureUnits = ctx.getParameter(ctx.MAX_TEXTURE_IMAGE_UNITS); |
|||
for (var ii = 0; ii < numTextureUnits; ++ii) { |
|||
ctx.activeTexture(ctx.TEXTURE0 + ii); |
|||
ctx.bindTexture(ctx.TEXTURE_CUBE_MAP, null); |
|||
ctx.bindTexture(ctx.TEXTURE_2D, null); |
|||
} |
|||
|
|||
ctx.activeTexture(ctx.TEXTURE0); |
|||
ctx.useProgram(null); |
|||
ctx.bindBuffer(ctx.ARRAY_BUFFER, null); |
|||
ctx.bindBuffer(ctx.ELEMENT_ARRAY_BUFFER, null); |
|||
ctx.bindFramebuffer(ctx.FRAMEBUFFER, null); |
|||
ctx.bindRenderbuffer(ctx.RENDERBUFFER, null); |
|||
ctx.disable(ctx.BLEND); |
|||
ctx.disable(ctx.CULL_FACE); |
|||
ctx.disable(ctx.DEPTH_TEST); |
|||
ctx.disable(ctx.DITHER); |
|||
ctx.disable(ctx.SCISSOR_TEST); |
|||
ctx.blendColor(0, 0, 0, 0); |
|||
ctx.blendEquation(ctx.FUNC_ADD); |
|||
ctx.blendFunc(ctx.ONE, ctx.ZERO); |
|||
ctx.clearColor(0, 0, 0, 0); |
|||
ctx.clearDepth(1); |
|||
ctx.clearStencil(-1); |
|||
ctx.colorMask(true, true, true, true); |
|||
ctx.cullFace(ctx.BACK); |
|||
ctx.depthFunc(ctx.LESS); |
|||
ctx.depthMask(true); |
|||
ctx.depthRange(0, 1); |
|||
ctx.frontFace(ctx.CCW); |
|||
ctx.hint(ctx.GENERATE_MIPMAP_HINT, ctx.DONT_CARE); |
|||
ctx.lineWidth(1); |
|||
ctx.pixelStorei(ctx.PACK_ALIGNMENT, 4); |
|||
ctx.pixelStorei(ctx.UNPACK_ALIGNMENT, 4); |
|||
ctx.pixelStorei(ctx.UNPACK_FLIP_Y_WEBGL, false); |
|||
ctx.pixelStorei(ctx.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); |
|||
// TODO: Delete this IF.
|
|||
if (ctx.UNPACK_COLORSPACE_CONVERSION_WEBGL) { |
|||
ctx.pixelStorei(ctx.UNPACK_COLORSPACE_CONVERSION_WEBGL, ctx.BROWSER_DEFAULT_WEBGL); |
|||
} |
|||
ctx.polygonOffset(0, 0); |
|||
ctx.sampleCoverage(1, false); |
|||
ctx.scissor(0, 0, ctx.canvas.width, ctx.canvas.height); |
|||
ctx.stencilFunc(ctx.ALWAYS, 0, 0xFFFFFFFF); |
|||
ctx.stencilMask(0xFFFFFFFF); |
|||
ctx.stencilOp(ctx.KEEP, ctx.KEEP, ctx.KEEP); |
|||
ctx.viewport(0, 0, ctx.canvas.width, ctx.canvas.height); |
|||
ctx.clear(ctx.COLOR_BUFFER_BIT | ctx.DEPTH_BUFFER_BIT | ctx.STENCIL_BUFFER_BIT); |
|||
|
|||
// TODO: This should NOT be needed but Firefox fails with 'hint'
|
|||
while(ctx.getError()); |
|||
} |
|||
|
|||
function makeLostContextSimulatingCanvas(canvas) { |
|||
var unwrappedContext_; |
|||
var wrappedContext_; |
|||
var onLost_ = []; |
|||
var onRestored_ = []; |
|||
var wrappedContext_ = {}; |
|||
var contextId_ = 1; |
|||
var contextLost_ = false; |
|||
var resourceId_ = 0; |
|||
var resourceDb_ = []; |
|||
var numCallsToLoseContext_ = 0; |
|||
var numCalls_ = 0; |
|||
var canRestore_ = false; |
|||
var restoreTimeout_ = 0; |
|||
|
|||
// Holds booleans for each GL error so can simulate errors.
|
|||
var glErrorShadow_ = { }; |
|||
|
|||
canvas.getContext = function(f) { |
|||
return function() { |
|||
var ctx = f.apply(canvas, arguments); |
|||
// Did we get a context and is it a WebGL context?
|
|||
if (ctx instanceof WebGLRenderingContext) { |
|||
if (ctx != unwrappedContext_) { |
|||
if (unwrappedContext_) { |
|||
throw "got different context" |
|||
} |
|||
unwrappedContext_ = ctx; |
|||
wrappedContext_ = makeLostContextSimulatingContext(unwrappedContext_); |
|||
} |
|||
return wrappedContext_; |
|||
} |
|||
return ctx; |
|||
} |
|||
}(canvas.getContext); |
|||
|
|||
function wrapEvent(listener) { |
|||
if (typeof(listener) == "function") { |
|||
return listener; |
|||
} else { |
|||
return function(info) { |
|||
listener.handleEvent(info); |
|||
} |
|||
} |
|||
} |
|||
|
|||
var addOnContextLostListener = function(listener) { |
|||
onLost_.push(wrapEvent(listener)); |
|||
}; |
|||
|
|||
var addOnContextRestoredListener = function(listener) { |
|||
onRestored_.push(wrapEvent(listener)); |
|||
}; |
|||
|
|||
|
|||
function wrapAddEventListener(canvas) { |
|||
var f = canvas.addEventListener; |
|||
canvas.addEventListener = function(type, listener, bubble) { |
|||
switch (type) { |
|||
case 'webglcontextlost': |
|||
addOnContextLostListener(listener); |
|||
break; |
|||
case 'webglcontextrestored': |
|||
addOnContextRestoredListener(listener); |
|||
break; |
|||
default: |
|||
f.apply(canvas, arguments); |
|||
} |
|||
}; |
|||
} |
|||
|
|||
wrapAddEventListener(canvas); |
|||
|
|||
canvas.loseContext = function() { |
|||
if (!contextLost_) { |
|||
contextLost_ = true; |
|||
numCallsToLoseContext_ = 0; |
|||
++contextId_; |
|||
while (unwrappedContext_.getError()); |
|||
clearErrors(); |
|||
glErrorShadow_[unwrappedContext_.CONTEXT_LOST_WEBGL] = true; |
|||
var event = makeWebGLContextEvent("context lost"); |
|||
var callbacks = onLost_.slice(); |
|||
setTimeout(function() { |
|||
//log("numCallbacks:" + callbacks.length);
|
|||
for (var ii = 0; ii < callbacks.length; ++ii) { |
|||
//log("calling callback:" + ii);
|
|||
callbacks[ii](event); |
|||
} |
|||
if (restoreTimeout_ >= 0) { |
|||
setTimeout(function() { |
|||
canvas.restoreContext(); |
|||
}, restoreTimeout_); |
|||
} |
|||
}, 0); |
|||
} |
|||
}; |
|||
|
|||
canvas.restoreContext = function() { |
|||
if (contextLost_) { |
|||
if (onRestored_.length) { |
|||
setTimeout(function() { |
|||
if (!canRestore_) { |
|||
throw "can not restore. webglcontestlost listener did not call event.preventDefault"; |
|||
} |
|||
freeResources(); |
|||
resetToInitialState(unwrappedContext_); |
|||
contextLost_ = false; |
|||
numCalls_ = 0; |
|||
canRestore_ = false; |
|||
var callbacks = onRestored_.slice(); |
|||
var event = makeWebGLContextEvent("context restored"); |
|||
for (var ii = 0; ii < callbacks.length; ++ii) { |
|||
callbacks[ii](event); |
|||
} |
|||
}, 0); |
|||
} |
|||
} |
|||
}; |
|||
|
|||
canvas.loseContextInNCalls = function(numCalls) { |
|||
if (contextLost_) { |
|||
throw "You can not ask a lost contet to be lost"; |
|||
} |
|||
numCallsToLoseContext_ = numCalls_ + numCalls; |
|||
}; |
|||
|
|||
canvas.getNumCalls = function() { |
|||
return numCalls_; |
|||
}; |
|||
|
|||
canvas.setRestoreTimeout = function(timeout) { |
|||
restoreTimeout_ = timeout; |
|||
}; |
|||
|
|||
function isWebGLObject(obj) { |
|||
//return false;
|
|||
return (obj instanceof WebGLBuffer || |
|||
obj instanceof WebGLFramebuffer || |
|||
obj instanceof WebGLProgram || |
|||
obj instanceof WebGLRenderbuffer || |
|||
obj instanceof WebGLShader || |
|||
obj instanceof WebGLTexture); |
|||
} |
|||
|
|||
function checkResources(args) { |
|||
for (var ii = 0; ii < args.length; ++ii) { |
|||
var arg = args[ii]; |
|||
if (isWebGLObject(arg)) { |
|||
return arg.__webglDebugContextLostId__ == contextId_; |
|||
} |
|||
} |
|||
return true; |
|||
} |
|||
|
|||
function clearErrors() { |
|||
var k = Object.keys(glErrorShadow_); |
|||
for (var ii = 0; ii < k.length; ++ii) { |
|||
delete glErrorShadow_[k]; |
|||
} |
|||
} |
|||
|
|||
function loseContextIfTime() { |
|||
++numCalls_; |
|||
if (!contextLost_) { |
|||
if (numCallsToLoseContext_ == numCalls_) { |
|||
canvas.loseContext(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
// Makes a function that simulates WebGL when out of context.
|
|||
function makeLostContextFunctionWrapper(ctx, functionName) { |
|||
var f = ctx[functionName]; |
|||
return function() { |
|||
// log("calling:" + functionName);
|
|||
// Only call the functions if the context is not lost.
|
|||
loseContextIfTime(); |
|||
if (!contextLost_) { |
|||
//if (!checkResources(arguments)) {
|
|||
// glErrorShadow_[wrappedContext_.INVALID_OPERATION] = true;
|
|||
// return;
|
|||
//}
|
|||
var result = f.apply(ctx, arguments); |
|||
return result; |
|||
} |
|||
}; |
|||
} |
|||
|
|||
function freeResources() { |
|||
for (var ii = 0; ii < resourceDb_.length; ++ii) { |
|||
var resource = resourceDb_[ii]; |
|||
if (resource instanceof WebGLBuffer) { |
|||
unwrappedContext_.deleteBuffer(resource); |
|||
} else if (resource instanceof WebGLFramebuffer) { |
|||
unwrappedContext_.deleteFramebuffer(resource); |
|||
} else if (resource instanceof WebGLProgram) { |
|||
unwrappedContext_.deleteProgram(resource); |
|||
} else if (resource instanceof WebGLRenderbuffer) { |
|||
unwrappedContext_.deleteRenderbuffer(resource); |
|||
} else if (resource instanceof WebGLShader) { |
|||
unwrappedContext_.deleteShader(resource); |
|||
} else if (resource instanceof WebGLTexture) { |
|||
unwrappedContext_.deleteTexture(resource); |
|||
} |
|||
} |
|||
} |
|||
|
|||
function makeWebGLContextEvent(statusMessage) { |
|||
return { |
|||
statusMessage: statusMessage, |
|||
preventDefault: function() { |
|||
canRestore_ = true; |
|||
} |
|||
}; |
|||
} |
|||
|
|||
return canvas; |
|||
|
|||
function makeLostContextSimulatingContext(ctx) { |
|||
// copy all functions and properties to wrapper
|
|||
for (var propertyName in ctx) { |
|||
if (typeof ctx[propertyName] == 'function') { |
|||
wrappedContext_[propertyName] = makeLostContextFunctionWrapper( |
|||
ctx, propertyName); |
|||
} else { |
|||
makePropertyWrapper(wrappedContext_, ctx, propertyName); |
|||
} |
|||
} |
|||
|
|||
// Wrap a few functions specially.
|
|||
wrappedContext_.getError = function() { |
|||
loseContextIfTime(); |
|||
if (!contextLost_) { |
|||
var err; |
|||
while (err = unwrappedContext_.getError()) { |
|||
glErrorShadow_[err] = true; |
|||
} |
|||
} |
|||
for (var err in glErrorShadow_) { |
|||
if (glErrorShadow_[err]) { |
|||
delete glErrorShadow_[err]; |
|||
return err; |
|||
} |
|||
} |
|||
return wrappedContext_.NO_ERROR; |
|||
}; |
|||
|
|||
var creationFunctions = [ |
|||
"createBuffer", |
|||
"createFramebuffer", |
|||
"createProgram", |
|||
"createRenderbuffer", |
|||
"createShader", |
|||
"createTexture" |
|||
]; |
|||
for (var ii = 0; ii < creationFunctions.length; ++ii) { |
|||
var functionName = creationFunctions[ii]; |
|||
wrappedContext_[functionName] = function(f) { |
|||
return function() { |
|||
loseContextIfTime(); |
|||
if (contextLost_) { |
|||
return null; |
|||
} |
|||
var obj = f.apply(ctx, arguments); |
|||
obj.__webglDebugContextLostId__ = contextId_; |
|||
resourceDb_.push(obj); |
|||
return obj; |
|||
}; |
|||
}(ctx[functionName]); |
|||
} |
|||
|
|||
var functionsThatShouldReturnNull = [ |
|||
"getActiveAttrib", |
|||
"getActiveUniform", |
|||
"getBufferParameter", |
|||
"getContextAttributes", |
|||
"getAttachedShaders", |
|||
"getFramebufferAttachmentParameter", |
|||
"getParameter", |
|||
"getProgramParameter", |
|||
"getProgramInfoLog", |
|||
"getRenderbufferParameter", |
|||
"getShaderParameter", |
|||
"getShaderInfoLog", |
|||
"getShaderSource", |
|||
"getTexParameter", |
|||
"getUniform", |
|||
"getUniformLocation", |
|||
"getVertexAttrib" |
|||
]; |
|||
for (var ii = 0; ii < functionsThatShouldReturnNull.length; ++ii) { |
|||
var functionName = functionsThatShouldReturnNull[ii]; |
|||
wrappedContext_[functionName] = function(f) { |
|||
return function() { |
|||
loseContextIfTime(); |
|||
if (contextLost_) { |
|||
return null; |
|||
} |
|||
return f.apply(ctx, arguments); |
|||
} |
|||
}(wrappedContext_[functionName]); |
|||
} |
|||
|
|||
var isFunctions = [ |
|||
"isBuffer", |
|||
"isEnabled", |
|||
"isFramebuffer", |
|||
"isProgram", |
|||
"isRenderbuffer", |
|||
"isShader", |
|||
"isTexture" |
|||
]; |
|||
for (var ii = 0; ii < isFunctions.length; ++ii) { |
|||
var functionName = isFunctions[ii]; |
|||
wrappedContext_[functionName] = function(f) { |
|||
return function() { |
|||
loseContextIfTime(); |
|||
if (contextLost_) { |
|||
return false; |
|||
} |
|||
return f.apply(ctx, arguments); |
|||
} |
|||
}(wrappedContext_[functionName]); |
|||
} |
|||
|
|||
wrappedContext_.checkFramebufferStatus = function(f) { |
|||
return function() { |
|||
loseContextIfTime(); |
|||
if (contextLost_) { |
|||
return wrappedContext_.FRAMEBUFFER_UNSUPPORTED; |
|||
} |
|||
return f.apply(ctx, arguments); |
|||
}; |
|||
}(wrappedContext_.checkFramebufferStatus); |
|||
|
|||
wrappedContext_.getAttribLocation = function(f) { |
|||
return function() { |
|||
loseContextIfTime(); |
|||
if (contextLost_) { |
|||
return -1; |
|||
} |
|||
return f.apply(ctx, arguments); |
|||
}; |
|||
}(wrappedContext_.getAttribLocation); |
|||
|
|||
wrappedContext_.getVertexAttribOffset = function(f) { |
|||
return function() { |
|||
loseContextIfTime(); |
|||
if (contextLost_) { |
|||
return 0; |
|||
} |
|||
return f.apply(ctx, arguments); |
|||
}; |
|||
}(wrappedContext_.getVertexAttribOffset); |
|||
|
|||
wrappedContext_.isContextLost = function() { |
|||
return contextLost_; |
|||
}; |
|||
|
|||
return wrappedContext_; |
|||
} |
|||
} |
|||
|
|||
return { |
|||
/** |
|||
* Initializes this module. Safe to call more than once. |
|||
* @param {!WebGLRenderingContext} ctx A WebGL context. If |
|||
} |
|||
* you have more than one context it doesn't matter which one |
|||
* you pass in, it is only used to pull out constants. |
|||
*/ |
|||
'init': init, |
|||
|
|||
/** |
|||
* Returns true or false if value matches any WebGL enum |
|||
* @param {*} value Value to check if it might be an enum. |
|||
* @return {boolean} True if value matches one of the WebGL defined enums |
|||
*/ |
|||
'mightBeEnum': mightBeEnum, |
|||
|
|||
/** |
|||
* Gets an string version of an WebGL enum. |
|||
* |
|||
* Example: |
|||
* WebGLDebugUtil.init(ctx); |
|||
* var str = WebGLDebugUtil.glEnumToString(ctx.getError()); |
|||
* |
|||
* @param {number} value Value to return an enum for |
|||
* @return {string} The string version of the enum. |
|||
*/ |
|||
'glEnumToString': glEnumToString, |
|||
|
|||
/** |
|||
* Converts the argument of a WebGL function to a string. |
|||
* Attempts to convert enum arguments to strings. |
|||
* |
|||
* Example: |
|||
* WebGLDebugUtil.init(ctx); |
|||
* var str = WebGLDebugUtil.glFunctionArgToString('bindTexture', 0, gl.TEXTURE_2D); |
|||
* |
|||
* would return 'TEXTURE_2D' |
|||
* |
|||
* @param {string} functionName the name of the WebGL function. |
|||
* @param {number} argumentIndx the index of the argument. |
|||
* @param {*} value The value of the argument. |
|||
* @return {string} The value as a string. |
|||
*/ |
|||
'glFunctionArgToString': glFunctionArgToString, |
|||
|
|||
/** |
|||
* Converts the arguments of a WebGL function to a string. |
|||
* Attempts to convert enum arguments to strings. |
|||
* |
|||
* @param {string} functionName the name of the WebGL function. |
|||
* @param {number} args The arguments. |
|||
* @return {string} The arguments as a string. |
|||
*/ |
|||
'glFunctionArgsToString': glFunctionArgsToString, |
|||
|
|||
/** |
|||
* Given a WebGL context returns a wrapped context that calls |
|||
* gl.getError after every command and calls a function if the |
|||
* result is not NO_ERROR. |
|||
* |
|||
* You can supply your own function if you want. For example, if you'd like |
|||
* an exception thrown on any GL error you could do this |
|||
* |
|||
* function throwOnGLError(err, funcName, args) { |
|||
* throw WebGLDebugUtils.glEnumToString(err) + |
|||
* " was caused by call to " + funcName; |
|||
* }; |
|||
* |
|||
* ctx = WebGLDebugUtils.makeDebugContext( |
|||
* canvas.getContext("webgl"), throwOnGLError); |
|||
* |
|||
* @param {!WebGLRenderingContext} ctx The webgl context to wrap. |
|||
* @param {!function(err, funcName, args): void} opt_onErrorFunc The function |
|||
* to call when gl.getError returns an error. If not specified the default |
|||
* function calls console.log with a message. |
|||
* @param {!function(funcName, args): void} opt_onFunc The |
|||
* function to call when each webgl function is called. You |
|||
* can use this to log all calls for example. |
|||
*/ |
|||
'makeDebugContext': makeDebugContext, |
|||
|
|||
/** |
|||
* Given a canvas element returns a wrapped canvas element that will |
|||
* simulate lost context. The canvas returned adds the following functions. |
|||
* |
|||
* loseContext: |
|||
* simulates a lost context event. |
|||
* |
|||
* restoreContext: |
|||
* simulates the context being restored. |
|||
* |
|||
* lostContextInNCalls: |
|||
* loses the context after N gl calls. |
|||
* |
|||
* getNumCalls: |
|||
* tells you how many gl calls there have been so far. |
|||
* |
|||
* setRestoreTimeout: |
|||
* sets the number of milliseconds until the context is restored |
|||
* after it has been lost. Defaults to 0. Pass -1 to prevent |
|||
* automatic restoring. |
|||
* |
|||
* @param {!Canvas} canvas The canvas element to wrap. |
|||
*/ |
|||
'makeLostContextSimulatingCanvas': makeLostContextSimulatingCanvas, |
|||
|
|||
/** |
|||
* Resets a context to the initial state. |
|||
* @param {!WebGLRenderingContext} ctx The webgl context to |
|||
* reset. |
|||
*/ |
|||
'resetToInitialState': resetToInitialState |
|||
}; |
|||
|
|||
}(); |
|||
|
|||
@ -0,0 +1,176 @@ |
|||
/* |
|||
* Copyright 2010, Google Inc. |
|||
* All rights reserved. |
|||
* |
|||
* Redistribution and use in source and binary forms, with or without |
|||
* modification, are permitted provided that the following conditions are |
|||
* met: |
|||
* |
|||
* * Redistributions of source code must retain the above copyright |
|||
* notice, this list of conditions and the following disclaimer. |
|||
* * Redistributions in binary form must reproduce the above |
|||
* copyright notice, this list of conditions and the following disclaimer |
|||
* in the documentation and/or other materials provided with the |
|||
* distribution. |
|||
* * Neither the name of Google Inc. nor the names of its |
|||
* contributors may be used to endorse or promote products derived from |
|||
* this software without specific prior written permission. |
|||
* |
|||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|||
*/ |
|||
|
|||
|
|||
/** |
|||
* @fileoverview This file contains functions every webgl program will need |
|||
* a version of one way or another. |
|||
* |
|||
* Instead of setting up a context manually it is recommended to |
|||
* use. This will check for success or failure. On failure it |
|||
* will attempt to present an approriate message to the user. |
|||
* |
|||
* gl = WebGLUtils.setupWebGL(canvas); |
|||
* |
|||
* For animated WebGL apps use of setTimeout or setInterval are |
|||
* discouraged. It is recommended you structure your rendering |
|||
* loop like this. |
|||
* |
|||
* function render() { |
|||
* window.requestAnimFrame(render, canvas); |
|||
* |
|||
* // do rendering
|
|||
* ... |
|||
* } |
|||
* render(); |
|||
* |
|||
* This will call your rendering function up to the refresh rate |
|||
* of your display but will stop rendering if your app is not |
|||
* visible. |
|||
*/ |
|||
|
|||
WebGLUtils = function() { |
|||
|
|||
/** |
|||
* Creates the HTLM for a failure message |
|||
* @param {string} canvasContainerId id of container of th |
|||
* canvas. |
|||
* @return {string} The html. |
|||
*/ |
|||
var makeFailHTML = function(msg) { |
|||
return '' + |
|||
'<table style="background-color: #8CE; width: 100%; height: 100%;"><tr>' + |
|||
'<td align="center">' + |
|||
'<div style="display: table-cell; vertical-align: middle;">' + |
|||
'<div style="">' + msg + '</div>' + |
|||
'</div>' + |
|||
'</td></tr></table>'; |
|||
}; |
|||
|
|||
/** |
|||
* Mesasge for getting a webgl browser |
|||
* @type {string} |
|||
*/ |
|||
var GET_A_WEBGL_BROWSER = '' + |
|||
'This page requires a browser that supports WebGL.<br/>' + |
|||
'<a href="http://get.webgl.org">Click here to upgrade your browser.</a>'; |
|||
|
|||
/** |
|||
* Mesasge for need better hardware |
|||
* @type {string} |
|||
*/ |
|||
var OTHER_PROBLEM = '' + |
|||
"It doesn't appear your computer can support WebGL.<br/>" + |
|||
'<a href="http://get.webgl.org/troubleshooting/">Click here for more information.</a>'; |
|||
|
|||
/** |
|||
* Creates a webgl context. If creation fails it will |
|||
* change the contents of the container of the <canvas> |
|||
* tag to an error message with the correct links for WebGL. |
|||
* @param {Element} canvas. The canvas element to create a |
|||
* context from. |
|||
* @param {WebGLContextCreationAttirbutes} opt_attribs Any |
|||
* creation attributes you want to pass in. |
|||
* @return {WebGLRenderingContext} The created context. |
|||
*/ |
|||
var setupWebGL = function(canvas, opt_attribs) { |
|||
function showLink(str) { |
|||
var container = canvas.parentNode; |
|||
if (container) { |
|||
container.innerHTML = makeFailHTML(str); |
|||
} |
|||
}; |
|||
|
|||
if (!window.WebGLRenderingContext) { |
|||
showLink(GET_A_WEBGL_BROWSER); |
|||
return null; |
|||
} |
|||
|
|||
var context = create3DContext(canvas, opt_attribs); |
|||
if (!context) { |
|||
showLink(OTHER_PROBLEM); |
|||
} |
|||
return context; |
|||
}; |
|||
|
|||
/** |
|||
* Creates a webgl context. |
|||
* @param {!Canvas} canvas The canvas tag to get context |
|||
* from. If one is not passed in one will be created. |
|||
* @return {!WebGLContext} The created context. |
|||
*/ |
|||
var create3DContext = function(canvas, opt_attribs) { |
|||
var names = ["webgl", "experimental-webgl", "webkit-3d", "moz-webgl"]; |
|||
var context = null; |
|||
for (var ii = 0; ii < names.length; ++ii) { |
|||
try { |
|||
context = canvas.getContext(names[ii], opt_attribs); |
|||
} catch(e) {} |
|||
if (context) { |
|||
break; |
|||
} |
|||
} |
|||
return context; |
|||
} |
|||
|
|||
return { |
|||
create3DContext: create3DContext, |
|||
setupWebGL: setupWebGL |
|||
}; |
|||
}(); |
|||
|
|||
/** |
|||
* Provides requestAnimationFrame in a cross browser way. |
|||
*/ |
|||
window.requestAnimFrame = (function() { |
|||
return window.requestAnimationFrame || |
|||
window.webkitRequestAnimationFrame || |
|||
window.mozRequestAnimationFrame || |
|||
window.oRequestAnimationFrame || |
|||
window.msRequestAnimationFrame || |
|||
function(/* function FrameRequestCallback */ callback, /* DOMElement Element */ element) { |
|||
return window.setTimeout(callback, 1000/60); |
|||
}; |
|||
})(); |
|||
|
|||
/** |
|||
* Provides cancelAnimationFrame in a cross browser way. |
|||
*/ |
|||
window.cancelAnimFrame = (function() { |
|||
return window.cancelAnimationFrame || |
|||
window.webkitCancelAnimationFrame || |
|||
window.mozCancelAnimationFrame || |
|||
window.oCancelAnimationFrame || |
|||
window.msCancelAnimationFrame || |
|||
window.clearTimeout; |
|||
})(); |
|||
|
|||
|
|||
Loading…
Reference in new issue