/* Copyright (C) 2009 Apple Computer, 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 COMPUTER, 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 COMPUTER, 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. */ function webglTestLog(msg) { if (window.console && window.console.log) { window.console.log(msg); } if (document.getElementById("console")) { var log = document.getElementById("console"); log.innerHTML += msg + "
"; } } // // create3DContext // // Returns the WebGLRenderingContext for any known implementation. // function create3DContext(canvas, attributes) { if (!canvas) canvas = document.createElement("canvas"); var context = null; try { context = canvas.getContext("webgl", attributes); } catch(e) {} if (!context) { try { context = canvas.getContext("experimental-webgl", attributes); } catch(e) {} } if (!context) { throw "Unable to fetch WebGL rendering context for Canvas"; } return context; } function createGLErrorWrapper(context, fname) { return function() { var rv = context[fname].apply(context, arguments); var err = context.getError(); if (err != 0) throw "GL error " + err + " in " + fname; return rv; }; } function create3DContextWithWrapperThatThrowsOnGLError(canvas, attributes) { var context = create3DContext(canvas, attributes); // Thanks to Ilmari Heikkinen for the idea on how to implement this so elegantly. var wrap = {}; for (var i in context) { try { if (typeof context[i] == 'function') { wrap[i] = createGLErrorWrapper(context, i); } else { wrap[i] = context[i]; } } catch (e) { webglTestLog("createContextWrapperThatThrowsOnGLError: Error accessing " + i); } } wrap.getError = function() { return context.getError(); }; return wrap; } function getGLErrorAsString(ctx, err) { if (err === ctx.NO_ERROR) { return "NO_ERROR"; } for (var name in ctx) { if (ctx[name] === err) { return name; } } return "0x" + err.toString(16); } function shouldGenerateGLError(ctx, glError, evalStr) { var exception; try { eval(evalStr); } catch (e) { exception = e; } if (exception) { testFailed(evalStr + " threw exception " + exception); } else { var err = ctx.getError(); if (err != glError) { testFailed(evalStr + " expected: " + getGLErrorAsString(ctx, glError) + ". Was " + getGLErrorAsString(ctx, err) + "."); } else { testPassed(evalStr + " generated expected GL error: " + getGLErrorAsString(ctx, glError) + "."); } } } /** * Tests that the first error GL returns is the specified error. * @param {!WebGLContext} gl The WebGLContext to use. * @param {number} glError The expected gl error. * @param {string} opt_msg Optional additional message. */ function glErrorShouldBe(gl, glError, opt_msg) { opt_msg = opt_msg || ""; var err = gl.getError(); if (err != glError) { testFailed("getError expected: " + getGLErrorAsString(gl, glError) + ". Was " + getGLErrorAsString(gl, err) + " : " + opt_msg); } else { testPassed("getError was expected value: " + getGLErrorAsString(gl, glError) + " : " + opt_msg); } }; // // createProgram // // Create and return a program object, attaching each of the given shaders. // // If attribs are given, bind an attrib with that name at that index. // function createProgram(gl, vshaders, fshaders, attribs) { if (typeof(vshaders) == "string") vshaders = [vshaders]; if (typeof(fshaders) == "string") fshaders = [fshaders]; var shaders = []; var i; for (i = 0; i < vshaders.length; ++i) { var shader = loadShader(gl, vshaders[i], gl.VERTEX_SHADER); if (!shader) return null; shaders.push(shader); } for (i = 0; i < fshaders.length; ++i) { var shader = loadShader(gl, fshaders[i], gl.FRAGMENT_SHADER); if (!shader) return null; shaders.push(shader); } var prog = gl.createProgram(); for (i = 0; i < shaders.length; ++i) { gl.attachShader(prog, shaders[i]); } if (attribs) { for (var i in attribs) { gl.bindAttribLocation(prog, parseInt(i), attribs[i]); } } gl.linkProgram(prog); // Check the link status var linked = gl.getProgramParameter(prog, gl.LINK_STATUS); if (!linked) { // something went wrong with the link var error = gl.getProgramInfoLog(prog); webglTestLog("Error in program linking:" + error); gl.deleteProgram(prog); for (i = 0; i < shaders.length; ++i) gl.deleteShader(shaders[i]); return null; } return prog; } // // initWebGL // // Initialize the Canvas element with the passed name as a WebGL object and return the // WebGLRenderingContext. // // 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) // function initWebGL(canvasName, vshader, fshader, attribs, clearColor, clearDepth, contextAttribs) { var canvas = document.getElementById(canvasName); var gl = create3DContext(canvas, contextAttribs); if (!gl) { alert("No WebGL context found"); return null; } // Create the program object gl.program = createProgram(gl, vshader, fshader, attribs); if (!gl.program) return null; gl.useProgram(gl.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 gl; } // // getShaderSource // // Load the source from the passed shader file. // function getShaderSource(file) { var xhr = new XMLHttpRequest(); xhr.open("GET", file, false); xhr.send(); return xhr.responseText; } // // loadShader // // 'shader' is either the id of a