Commit initial. Import depuis webgl-tp6
This commit is contained in:
commit
063ceb5293
7 changed files with 3059 additions and 0 deletions
603
J3DI.js
Normal file
603
J3DI.js
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
1065
J3DIMath.js
Normal file
1065
J3DIMath.js
Normal file
File diff suppressed because it is too large
Load diff
70
index.html
Normal file
70
index.html
Normal file
|
@ -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>
|
254
main.js
Normal file
254
main.js
Normal file
|
@ -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();
|
||||
}
|
||||
}
|
16
tp.css
Normal file
16
tp.css
Normal file
|
@ -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;
|
||||
}
|
875
webgl-debug.js
Normal file
875
webgl-debug.js
Normal file
|
@ -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
|
||||
};
|
||||
|
||||
}();
|
||||
|
176
webgl-utils.js
Normal file
176
webgl-utils.js
Normal file
|
@ -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 a new issue