1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/canvas/test/webgl-conformance/conformance/resources/webgl-test-utils.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1408 @@ 1.4 +// Copyright (c) 2011 The Chromium Authors. All rights reserved. 1.5 +// Use of this source code is governed by a BSD-style license that can be 1.6 +// found in the LICENSE file. 1.7 + 1.8 +WebGLTestUtils = (function() { 1.9 + 1.10 +/** 1.11 + * Wrapped logging function. 1.12 + * @param {string} msg The message to log. 1.13 + */ 1.14 +var log = function(msg) { 1.15 + if (window.console && window.console.log) { 1.16 + window.console.log(msg); 1.17 + } 1.18 +}; 1.19 + 1.20 +/** 1.21 + * Wrapped logging function. 1.22 + * @param {string} msg The message to log. 1.23 + */ 1.24 +var error = function(msg) { 1.25 + if (window.console) { 1.26 + if (window.console.error) { 1.27 + window.console.error(msg); 1.28 + } 1.29 + else if (window.console.log) { 1.30 + window.console.log(msg); 1.31 + } 1.32 + } 1.33 +}; 1.34 + 1.35 +/** 1.36 + * Turn off all logging. 1.37 + */ 1.38 +var loggingOff = function() { 1.39 + log = function() {}; 1.40 + error = function() {}; 1.41 +}; 1.42 + 1.43 +/** 1.44 + * Converts a WebGL enum to a string 1.45 + * @param {!WebGLContext} gl The WebGLContext to use. 1.46 + * @param {number} value The enum value. 1.47 + * @return {string} The enum as a string. 1.48 + */ 1.49 +var glEnumToString = function(gl, value) { 1.50 + for (var p in gl) { 1.51 + if (gl[p] == value) { 1.52 + return p; 1.53 + } 1.54 + } 1.55 + return "0x" + value.toString(16); 1.56 +}; 1.57 + 1.58 +var lastError = ""; 1.59 + 1.60 +/** 1.61 + * Returns the last compiler/linker error. 1.62 + * @return {string} The last compiler/linker error. 1.63 + */ 1.64 +var getLastError = function() { 1.65 + return lastError; 1.66 +}; 1.67 + 1.68 +/** 1.69 + * Whether a haystack ends with a needle. 1.70 + * @param {string} haystack String to search 1.71 + * @param {string} needle String to search for. 1.72 + * @param {boolean} True if haystack ends with needle. 1.73 + */ 1.74 +var endsWith = function(haystack, needle) { 1.75 + return haystack.substr(haystack.length - needle.length) === needle; 1.76 +}; 1.77 + 1.78 +/** 1.79 + * Whether a haystack starts with a needle. 1.80 + * @param {string} haystack String to search 1.81 + * @param {string} needle String to search for. 1.82 + * @param {boolean} True if haystack starts with needle. 1.83 + */ 1.84 +var startsWith = function(haystack, needle) { 1.85 + return haystack.substr(0, needle.length) === needle; 1.86 +}; 1.87 + 1.88 +/** 1.89 + * A vertex shader for a single texture. 1.90 + * @type {string} 1.91 + */ 1.92 +var simpleTextureVertexShader = [ 1.93 + 'attribute vec4 vPosition;', 1.94 + 'attribute vec2 texCoord0;', 1.95 + 'varying vec2 texCoord;', 1.96 + 'void main() {', 1.97 + ' gl_Position = vPosition;', 1.98 + ' texCoord = texCoord0;', 1.99 + '}'].join('\n'); 1.100 + 1.101 +/** 1.102 + * A fragment shader for a single texture. 1.103 + * @type {string} 1.104 + */ 1.105 +var simpleTextureFragmentShader = [ 1.106 + 'precision mediump float;', 1.107 + 'uniform sampler2D tex;', 1.108 + 'varying vec2 texCoord;', 1.109 + 'void main() {', 1.110 + ' gl_FragData[0] = texture2D(tex, texCoord);', 1.111 + '}'].join('\n'); 1.112 + 1.113 +/** 1.114 + * Creates a simple texture vertex shader. 1.115 + * @param {!WebGLContext} gl The WebGLContext to use. 1.116 + * @return {!WebGLShader} 1.117 + */ 1.118 +var setupSimpleTextureVertexShader = function(gl) { 1.119 + return loadShader(gl, simpleTextureVertexShader, gl.VERTEX_SHADER); 1.120 +}; 1.121 + 1.122 +/** 1.123 + * Creates a simple texture fragment shader. 1.124 + * @param {!WebGLContext} gl The WebGLContext to use. 1.125 + * @return {!WebGLShader} 1.126 + */ 1.127 +var setupSimpleTextureFragmentShader = function(gl) { 1.128 + return loadShader( 1.129 + gl, simpleTextureFragmentShader, gl.FRAGMENT_SHADER); 1.130 +}; 1.131 + 1.132 +/** 1.133 + * Creates a program, attaches shaders, binds attrib locations, links the 1.134 + * program and calls useProgram. 1.135 + * @param {!Array.<!WebGLShader|string>} shaders The shaders to 1.136 + * attach, or the source, or the id of a script to get 1.137 + * the source from. 1.138 + * @param {!Array.<string>} opt_attribs The attribs names. 1.139 + * @param {!Array.<number>} opt_locations The locations for the attribs. 1.140 + */ 1.141 +var setupProgram = function(gl, shaders, opt_attribs, opt_locations) { 1.142 + var realShaders = []; 1.143 + var program = gl.createProgram(); 1.144 + for (var ii = 0; ii < shaders.length; ++ii) { 1.145 + var shader = shaders[ii]; 1.146 + if (typeof shader == 'string') { 1.147 + var element = document.getElementById(shader); 1.148 + if (element) { 1.149 + shader = loadShaderFromScript(gl, shader); 1.150 + } else { 1.151 + shader = loadShader(gl, shader, ii ? gl.FRAGMENT_SHADER : gl.VERTEX_SHADER); 1.152 + } 1.153 + } 1.154 + gl.attachShader(program, shader); 1.155 + } 1.156 + if (opt_attribs) { 1.157 + for (var ii = 0; ii < opt_attribs.length; ++ii) { 1.158 + gl.bindAttribLocation( 1.159 + program, 1.160 + opt_locations ? opt_locations[ii] : ii, 1.161 + opt_attribs[ii]); 1.162 + } 1.163 + } 1.164 + gl.linkProgram(program); 1.165 + 1.166 + // Check the link status 1.167 + var linked = gl.getProgramParameter(program, gl.LINK_STATUS); 1.168 + if (!linked) { 1.169 + // something went wrong with the link 1.170 + lastError = gl.getProgramInfoLog (program); 1.171 + error("Error in program linking:" + lastError); 1.172 + 1.173 + gl.deleteProgram(program); 1.174 + return null; 1.175 + } 1.176 + 1.177 + gl.useProgram(program); 1.178 + return program; 1.179 +}; 1.180 + 1.181 +/** 1.182 + * Creates a simple texture program. 1.183 + * @param {!WebGLContext} gl The WebGLContext to use. 1.184 + * @param {number} opt_positionLocation The attrib location for position. 1.185 + * @param {number} opt_texcoordLocation The attrib location for texture coords. 1.186 + * @return {WebGLProgram} 1.187 + */ 1.188 +var setupSimpleTextureProgram = function( 1.189 + gl, opt_positionLocation, opt_texcoordLocation) { 1.190 + opt_positionLocation = opt_positionLocation || 0; 1.191 + opt_texcoordLocation = opt_texcoordLocation || 1; 1.192 + var vs = setupSimpleTextureVertexShader(gl); 1.193 + var fs = setupSimpleTextureFragmentShader(gl); 1.194 + if (!vs || !fs) { 1.195 + return null; 1.196 + } 1.197 + var program = setupProgram( 1.198 + gl, 1.199 + [vs, fs], 1.200 + ['vPosition', 'texCoord0'], 1.201 + [opt_positionLocation, opt_texcoordLocation]); 1.202 + if (!program) { 1.203 + gl.deleteShader(fs); 1.204 + gl.deleteShader(vs); 1.205 + } 1.206 + gl.useProgram(program); 1.207 + return program; 1.208 +}; 1.209 + 1.210 +/** 1.211 + * Creates buffers for a textured unit quad and attaches them to vertex attribs. 1.212 + * @param {!WebGLContext} gl The WebGLContext to use. 1.213 + * @param {number} opt_positionLocation The attrib location for position. 1.214 + * @param {number} opt_texcoordLocation The attrib location for texture coords. 1.215 + * @return {!Array.<WebGLBuffer>} The buffer objects that were 1.216 + * created. 1.217 + */ 1.218 +var setupUnitQuad = function(gl, opt_positionLocation, opt_texcoordLocation) { 1.219 + return setupUnitQuadWithTexCoords(gl, [ 0.0, 0.0 ], [ 1.0, 1.0 ], 1.220 + opt_positionLocation, opt_texcoordLocation); 1.221 +}; 1.222 + 1.223 +/** 1.224 + * Draws a previously setupUnitQuad. 1.225 + * @param {!WebGLContext} gl The WebGLContext to use. 1.226 + */ 1.227 +var drawUnitQuad = function(gl) { 1.228 + gl.drawArrays(gl.TRIANGLES, 0, 6); 1.229 +}; 1.230 + 1.231 +/** 1.232 + * Clears then Draws a previously setupUnitQuad. 1.233 + * @param {!WebGLContext} gl The WebGLContext to use. 1.234 + * @param {!Array.<number>} opt_color The color to fill clear with before 1.235 + * drawing. A 4 element array where each element is in the range 0 to 1.236 + * 255. Default [255, 255, 255, 255] 1.237 + */ 1.238 +var clearAndDrawUnitQuad = function(gl, opt_color) { 1.239 + opt_color = opt_color || [255, 255, 255, 255]; 1.240 + 1.241 + // Save and restore. 1.242 + var prevClearColor = gl.getParameter(gl.COLOR_CLEAR_VALUE); 1.243 + 1.244 + gl.clearColor(opt_color[0] / 255, 1.245 + opt_color[1] / 255, 1.246 + opt_color[2] / 255, 1.247 + opt_color[3] / 255); 1.248 + gl.clear(gl.COLOR_BUFFER_BIT); 1.249 + drawUnitQuad(gl); 1.250 + 1.251 + gl.clearColor(prevClearColor[0], 1.252 + prevClearColor[1], 1.253 + prevClearColor[2], 1.254 + prevClearColor[3]); 1.255 +}; 1.256 + 1.257 +/** 1.258 + * Creates buffers for a textured unit quad with specified lower left 1.259 + * and upper right texture coordinates, and attaches them to vertex 1.260 + * attribs. 1.261 + * @param {!WebGLContext} gl The WebGLContext to use. 1.262 + * @param {!Array.<number>} lowerLeftTexCoords The texture coordinates for the lower left corner. 1.263 + * @param {!Array.<number>} upperRightTexCoords The texture coordinates for the upper right corner. 1.264 + * @param {number} opt_positionLocation The attrib location for position. 1.265 + * @param {number} opt_texcoordLocation The attrib location for texture coords. 1.266 + * @return {!Array.<WebGLBuffer>} The buffer objects that were 1.267 + * created. 1.268 + */ 1.269 +var setupUnitQuadWithTexCoords = function( 1.270 + gl, lowerLeftTexCoords, upperRightTexCoords, 1.271 + opt_positionLocation, opt_texcoordLocation) { 1.272 + opt_positionLocation = opt_positionLocation || 0; 1.273 + opt_texcoordLocation = opt_texcoordLocation || 1; 1.274 + var objects = []; 1.275 + 1.276 + var vertexObject = gl.createBuffer(); 1.277 + gl.bindBuffer(gl.ARRAY_BUFFER, vertexObject); 1.278 + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ 1.279 + 1.0, 1.0, 0.0, 1.280 + -1.0, 1.0, 0.0, 1.281 + -1.0, -1.0, 0.0, 1.282 + 1.0, 1.0, 0.0, 1.283 + -1.0, -1.0, 0.0, 1.284 + 1.0, -1.0, 0.0]), gl.STATIC_DRAW); 1.285 + gl.enableVertexAttribArray(opt_positionLocation); 1.286 + gl.vertexAttribPointer(opt_positionLocation, 3, gl.FLOAT, false, 0, 0); 1.287 + objects.push(vertexObject); 1.288 + 1.289 + var llx = lowerLeftTexCoords[0]; 1.290 + var lly = lowerLeftTexCoords[1]; 1.291 + var urx = upperRightTexCoords[0]; 1.292 + var ury = upperRightTexCoords[1]; 1.293 + 1.294 + var vertexObject = gl.createBuffer(); 1.295 + gl.bindBuffer(gl.ARRAY_BUFFER, vertexObject); 1.296 + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ 1.297 + urx, ury, 1.298 + llx, ury, 1.299 + llx, lly, 1.300 + urx, ury, 1.301 + llx, lly, 1.302 + urx, lly]), gl.STATIC_DRAW); 1.303 + gl.enableVertexAttribArray(opt_texcoordLocation); 1.304 + gl.vertexAttribPointer(opt_texcoordLocation, 2, gl.FLOAT, false, 0, 0); 1.305 + objects.push(vertexObject); 1.306 + return objects; 1.307 +}; 1.308 + 1.309 +/** 1.310 + * Creates a program and buffers for rendering a textured quad. 1.311 + * @param {!WebGLContext} gl The WebGLContext to use. 1.312 + * @param {number} opt_positionLocation The attrib location for position. 1.313 + * @param {number} opt_texcoordLocation The attrib location for texture coords. 1.314 + * @return {!WebGLProgram} 1.315 + */ 1.316 +var setupTexturedQuad = function( 1.317 + gl, opt_positionLocation, opt_texcoordLocation) { 1.318 + var program = setupSimpleTextureProgram( 1.319 + gl, opt_positionLocation, opt_texcoordLocation); 1.320 + setupUnitQuad(gl, opt_positionLocation, opt_texcoordLocation); 1.321 + return program; 1.322 +}; 1.323 + 1.324 +/** 1.325 + * Creates a program and buffers for rendering a textured quad with 1.326 + * specified lower left and upper right texture coordinates. 1.327 + * @param {!WebGLContext} gl The WebGLContext to use. 1.328 + * @param {!Array.<number>} lowerLeftTexCoords The texture coordinates for the lower left corner. 1.329 + * @param {!Array.<number>} upperRightTexCoords The texture coordinates for the upper right corner. 1.330 + * @param {number} opt_positionLocation The attrib location for position. 1.331 + * @param {number} opt_texcoordLocation The attrib location for texture coords. 1.332 + * @return {!WebGLProgram} 1.333 + */ 1.334 +var setupTexturedQuadWithTexCoords = function( 1.335 + gl, lowerLeftTexCoords, upperRightTexCoords, 1.336 + opt_positionLocation, opt_texcoordLocation) { 1.337 + var program = setupSimpleTextureProgram( 1.338 + gl, opt_positionLocation, opt_texcoordLocation); 1.339 + setupUnitQuadWithTexCoords(gl, lowerLeftTexCoords, upperRightTexCoords, 1.340 + opt_positionLocation, opt_texcoordLocation); 1.341 + return program; 1.342 +}; 1.343 + 1.344 +/** 1.345 + * Creates a unit quad with only positions of a given resolution. 1.346 + * @param {!WebGLContext} gl The WebGLContext to use. 1.347 + * @param {number} gridRes The resolution of the mesh grid, expressed in the number of triangles across and down. 1.348 + * @param {number} opt_positionLocation The attrib location for position. 1.349 + */ 1.350 +var setupQuad = function ( 1.351 + gl, gridRes, opt_positionLocation, opt_flipOddTriangles) { 1.352 + var positionLocation = opt_positionLocation || 0; 1.353 + var objects = []; 1.354 + 1.355 + var vertsAcross = gridRes + 1; 1.356 + var numVerts = vertsAcross * vertsAcross; 1.357 + var positions = new Float32Array(numVerts * 3); 1.358 + var indices = new Uint16Array(6 * gridRes * gridRes); 1.359 + 1.360 + var poffset = 0; 1.361 + 1.362 + for (var yy = 0; yy <= gridRes; ++yy) { 1.363 + for (var xx = 0; xx <= gridRes; ++xx) { 1.364 + positions[poffset + 0] = -1 + 2 * xx / gridRes; 1.365 + positions[poffset + 1] = -1 + 2 * yy / gridRes; 1.366 + positions[poffset + 2] = 0; 1.367 + 1.368 + poffset += 3; 1.369 + } 1.370 + } 1.371 + 1.372 + var tbase = 0; 1.373 + for (var yy = 0; yy < gridRes; ++yy) { 1.374 + var index = yy * vertsAcross; 1.375 + for (var xx = 0; xx < gridRes; ++xx) { 1.376 + indices[tbase + 0] = index + 0; 1.377 + indices[tbase + 1] = index + 1; 1.378 + indices[tbase + 2] = index + vertsAcross; 1.379 + indices[tbase + 3] = index + vertsAcross; 1.380 + indices[tbase + 4] = index + 1; 1.381 + indices[tbase + 5] = index + vertsAcross + 1; 1.382 + 1.383 + if (opt_flipOddTriangles) { 1.384 + indices[tbase + 4] = index + vertsAcross + 1; 1.385 + indices[tbase + 5] = index + 1; 1.386 + } 1.387 + 1.388 + index += 1; 1.389 + tbase += 6; 1.390 + } 1.391 + } 1.392 + 1.393 + var buf = gl.createBuffer(); 1.394 + gl.bindBuffer(gl.ARRAY_BUFFER, buf); 1.395 + gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW); 1.396 + gl.enableVertexAttribArray(positionLocation); 1.397 + gl.vertexAttribPointer(positionLocation, 3, gl.FLOAT, false, 0, 0); 1.398 + objects.push(buf); 1.399 + 1.400 + var buf = gl.createBuffer(); 1.401 + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buf); 1.402 + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW); 1.403 + objects.push(buf); 1.404 + 1.405 + return objects; 1.406 +}; 1.407 + 1.408 +/** 1.409 + * Fills the given texture with a solid color 1.410 + * @param {!WebGLContext} gl The WebGLContext to use. 1.411 + * @param {!WebGLTexture} tex The texture to fill. 1.412 + * @param {number} width The width of the texture to create. 1.413 + * @param {number} height The height of the texture to create. 1.414 + * @param {!Array.<number>} color The color to fill with. A 4 element array 1.415 + * where each element is in the range 0 to 255. 1.416 + * @param {number} opt_level The level of the texture to fill. Default = 0. 1.417 + */ 1.418 +var fillTexture = function(gl, tex, width, height, color, opt_level) { 1.419 + opt_level = opt_level || 0; 1.420 + var numPixels = width * height; 1.421 + var size = numPixels * 4; 1.422 + var buf = new Uint8Array(size); 1.423 + for (var ii = 0; ii < numPixels; ++ii) { 1.424 + var off = ii * 4; 1.425 + buf[off + 0] = color[0]; 1.426 + buf[off + 1] = color[1]; 1.427 + buf[off + 2] = color[2]; 1.428 + buf[off + 3] = color[3]; 1.429 + } 1.430 + gl.bindTexture(gl.TEXTURE_2D, tex); 1.431 + gl.texImage2D( 1.432 + gl.TEXTURE_2D, opt_level, gl.RGBA, width, height, 0, 1.433 + gl.RGBA, gl.UNSIGNED_BYTE, buf); 1.434 + }; 1.435 + 1.436 +/** 1.437 + * Creates a textures and fills it with a solid color 1.438 + * @param {!WebGLContext} gl The WebGLContext to use. 1.439 + * @param {number} width The width of the texture to create. 1.440 + * @param {number} height The height of the texture to create. 1.441 + * @param {!Array.<number>} color The color to fill with. A 4 element array 1.442 + * where each element is in the range 0 to 255. 1.443 + * @return {!WebGLTexture} 1.444 + */ 1.445 +var createColoredTexture = function(gl, width, height, color) { 1.446 + var tex = gl.createTexture(); 1.447 + fillTexture(gl, tex, width, height, color); 1.448 + return tex; 1.449 +}; 1.450 + 1.451 +/** 1.452 + * Draws a previously setup quad. 1.453 + * @param {!WebGLContext} gl The WebGLContext to use. 1.454 + * @param {!Array.<number>} opt_color The color to fill clear with before 1.455 + * drawing. A 4 element array where each element is in the range 0 to 1.456 + * 255. Default [255, 255, 255, 255] 1.457 + */ 1.458 +var drawQuad = function(gl, opt_color) { 1.459 + opt_color = opt_color || [255, 255, 255, 255]; 1.460 + gl.clearColor( 1.461 + opt_color[0] / 255, 1.462 + opt_color[1] / 255, 1.463 + opt_color[2] / 255, 1.464 + opt_color[3] / 255); 1.465 + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); 1.466 + gl.drawArrays(gl.TRIANGLES, 0, 6); 1.467 +}; 1.468 + 1.469 +/** 1.470 + * Checks that a portion of a canvas is 1 color. 1.471 + * @param {!WebGLContext} gl The WebGLContext to use. 1.472 + * @param {number} x left corner of region to check. 1.473 + * @param {number} y bottom corner of region to check. 1.474 + * @param {number} width width of region to check. 1.475 + * @param {number} height width of region to check. 1.476 + * @param {!Array.<number>} color The color to fill clear with before drawing. A 1.477 + * 4 element array where each element is in the range 0 to 255. 1.478 + * @param {string} msg Message to associate with success. Eg ("should be red"). 1.479 + * @param {number} errorRange Optional. Acceptable error in 1.480 + * color checking. 0 by default. 1.481 + */ 1.482 +var checkCanvasRect = function(gl, x, y, width, height, color, msg, errorRange) { 1.483 + errorRange = errorRange || 0; 1.484 + var buf = new Uint8Array(width * height * 4); 1.485 + gl.readPixels(x, y, width, height, gl.RGBA, gl.UNSIGNED_BYTE, buf); 1.486 + for (var i = 0; i < width * height; ++i) { 1.487 + var offset = i * 4; 1.488 + for (var j = 0; j < color.length; ++j) { 1.489 + if (Math.abs(buf[offset + j] - color[j]) > errorRange) { 1.490 + var was = buf[offset + 0].toString(); 1.491 + for (j = 1; j < color.length; ++j) { 1.492 + was += "," + buf[offset + j]; 1.493 + } 1.494 + 1.495 + var cv = document.createElement('canvas'); 1.496 + cv.height = height; 1.497 + cv.width = width; 1.498 + var ctx = cv.getContext('2d'); 1.499 + ctx.fillStyle="rgba(" + color[0] + ", " + color[1] + ", " + color[2] + ", 255)"; 1.500 + ctx.fillRect(0, 0, width, height); 1.501 + testFailedRender(msg, ctx, buf, width, height); 1.502 + 1.503 + debug('at (' + (i % width) + ', ' + Math.floor(i / width) + 1.504 + ') expected: ' + color + ' was ' + was); 1.505 + return; 1.506 + } 1.507 + } 1.508 + } 1.509 + testPassed(msg); 1.510 +}; 1.511 + 1.512 +/** 1.513 + * Checks that an entire canvas is 1 color. 1.514 + * @param {!WebGLContext} gl The WebGLContext to use. 1.515 + * @param {!Array.<number>} color The color to fill clear with before drawing. A 1.516 + * 4 element array where each element is in the range 0 to 255. 1.517 + * @param {string} msg Message to associate with success. Eg ("should be red"). 1.518 + * @param {number} errorRange Optional. Acceptable error in 1.519 + * color checking. 0 by default. 1.520 + */ 1.521 +var checkCanvas = function(gl, color, msg, errorRange) { 1.522 + checkCanvasRect(gl, 0, 0, gl.canvas.width, gl.canvas.height, color, msg, errorRange); 1.523 +}; 1.524 + 1.525 +/** 1.526 + * Loads a texture, calls callback when finished. 1.527 + * @param {!WebGLContext} gl The WebGLContext to use. 1.528 + * @param {string} url URL of image to load 1.529 + * @param {function(!Image): void} callback Function that gets called after 1.530 + * image has loaded 1.531 + * @return {!WebGLTexture} The created texture. 1.532 + */ 1.533 +var loadTexture = function(gl, url, callback) { 1.534 + var texture = gl.createTexture(); 1.535 + gl.bindTexture(gl.TEXTURE_2D, texture); 1.536 + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); 1.537 + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); 1.538 + var image = new Image(); 1.539 + image.onload = function() { 1.540 + gl.bindTexture(gl.TEXTURE_2D, texture); 1.541 + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true); 1.542 + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image); 1.543 + callback(image); 1.544 + }; 1.545 + image.src = url; 1.546 + return texture; 1.547 +}; 1.548 + 1.549 +/** 1.550 + * Creates a webgl context. 1.551 + * @param {!Canvas|string} opt_canvas The canvas tag to get 1.552 + * context from. If one is not passed in one will be 1.553 + * created. If it's a string it's assumed to be the id of a 1.554 + * canvas. 1.555 + * @return {!WebGLContext} The created context. 1.556 + */ 1.557 +var create3DContext = function(opt_canvas, opt_attributes) { 1.558 + opt_canvas = opt_canvas || document.createElement("canvas"); 1.559 + if (typeof opt_canvas == 'string') { 1.560 + opt_canvas = document.getElementById(opt_canvas); 1.561 + } 1.562 + var context = null; 1.563 + var names = ["webgl", "experimental-webgl"]; 1.564 + for (var i = 0; i < names.length; ++i) { 1.565 + try { 1.566 + context = opt_canvas.getContext(names[i], opt_attributes); 1.567 + } catch (e) { 1.568 + } 1.569 + if (context) { 1.570 + break; 1.571 + } 1.572 + } 1.573 + if (!context) { 1.574 + testFailed("Unable to fetch WebGL rendering context for Canvas"); 1.575 + } 1.576 + return context; 1.577 +} 1.578 + 1.579 +/** 1.580 + * Gets a GLError value as a string. 1.581 + * @param {!WebGLContext} gl The WebGLContext to use. 1.582 + * @param {number} err The webgl error as retrieved from gl.getError(). 1.583 + * @return {string} the error as a string. 1.584 + */ 1.585 +var getGLErrorAsString = function(gl, err) { 1.586 + if (err === gl.NO_ERROR) { 1.587 + return "NO_ERROR"; 1.588 + } 1.589 + for (var name in gl) { 1.590 + if (gl[name] === err) { 1.591 + return name; 1.592 + } 1.593 + } 1.594 + return err.toString(); 1.595 +}; 1.596 + 1.597 +/** 1.598 + * Wraps a WebGL function with a function that throws an exception if there is 1.599 + * an error. 1.600 + * @param {!WebGLContext} gl The WebGLContext to use. 1.601 + * @param {string} fname Name of function to wrap. 1.602 + * @return {function} The wrapped function. 1.603 + */ 1.604 +var createGLErrorWrapper = function(context, fname) { 1.605 + return function() { 1.606 + var rv = context[fname].apply(context, arguments); 1.607 + var err = context.getError(); 1.608 + if (err != 0) 1.609 + throw "GL error " + getGLErrorAsString(err) + " in " + fname; 1.610 + return rv; 1.611 + }; 1.612 +}; 1.613 + 1.614 +/** 1.615 + * Creates a WebGL context where all functions are wrapped to throw an exception 1.616 + * if there is an error. 1.617 + * @param {!Canvas} canvas The HTML canvas to get a context from. 1.618 + * @return {!Object} The wrapped context. 1.619 + */ 1.620 +function create3DContextWithWrapperThatThrowsOnGLError(canvas) { 1.621 + var context = create3DContext(canvas); 1.622 + var wrap = {}; 1.623 + for (var i in context) { 1.624 + try { 1.625 + if (typeof context[i] == 'function') { 1.626 + wrap[i] = createGLErrorWrapper(context, i); 1.627 + } else { 1.628 + wrap[i] = context[i]; 1.629 + } 1.630 + } catch (e) { 1.631 + error("createContextWrapperThatThrowsOnGLError: Error accessing " + i); 1.632 + } 1.633 + } 1.634 + wrap.getError = function() { 1.635 + return context.getError(); 1.636 + }; 1.637 + return wrap; 1.638 +}; 1.639 + 1.640 +/** 1.641 + * Tests that an evaluated expression generates a specific GL error. 1.642 + * @param {!WebGLContext} gl The WebGLContext to use. 1.643 + * @param {number} glError The expected gl error. 1.644 + * @param {string} evalSTr The string to evaluate. 1.645 + */ 1.646 +var shouldGenerateGLError = function(gl, glError, evalStr) { 1.647 + var exception; 1.648 + try { 1.649 + eval(evalStr); 1.650 + } catch (e) { 1.651 + exception = e; 1.652 + } 1.653 + if (exception) { 1.654 + testFailed(evalStr + " threw exception " + exception); 1.655 + } else { 1.656 + var err = gl.getError(); 1.657 + if (err != glError) { 1.658 + testFailed(evalStr + " expected: " + getGLErrorAsString(gl, glError) + ". Was " + getGLErrorAsString(gl, err) + "."); 1.659 + } else { 1.660 + testPassed(evalStr + " was expected value: " + getGLErrorAsString(gl, glError) + "."); 1.661 + } 1.662 + } 1.663 +}; 1.664 + 1.665 +/** 1.666 + * Tests that the first error GL returns is the specified error. 1.667 + * @param {!WebGLContext} gl The WebGLContext to use. 1.668 + * @param {number} glError The expected gl error. 1.669 + * @param {string} opt_msg 1.670 + */ 1.671 +var glErrorShouldBe = function(gl, glError, opt_msg) { 1.672 + opt_msg = opt_msg || ""; 1.673 + var err = gl.getError(); 1.674 + if (err != glError) { 1.675 + testFailed("getError expected: " + getGLErrorAsString(gl, glError) + 1.676 + ". Was " + getGLErrorAsString(gl, err) + " : " + opt_msg); 1.677 + } else { 1.678 + testPassed("getError was expected value: " + 1.679 + getGLErrorAsString(gl, glError) + " : " + opt_msg); 1.680 + } 1.681 +}; 1.682 + 1.683 +/** 1.684 + * Links a WebGL program, throws if there are errors. 1.685 + * @param {!WebGLContext} gl The WebGLContext to use. 1.686 + * @param {!WebGLProgram} program The WebGLProgram to link. 1.687 + * @param {function(string): void) opt_errorCallback callback for errors. 1.688 + */ 1.689 +var linkProgram = function(gl, program, opt_errorCallback) { 1.690 + errFn = opt_errorCallback || testFailed; 1.691 + // Link the program 1.692 + gl.linkProgram(program); 1.693 + 1.694 + // Check the link status 1.695 + var linked = gl.getProgramParameter(program, gl.LINK_STATUS); 1.696 + if (!linked) { 1.697 + // something went wrong with the link 1.698 + var error = gl.getProgramInfoLog (program); 1.699 + 1.700 + errFn("Error in program linking:" + error); 1.701 + 1.702 + gl.deleteProgram(program); 1.703 + } 1.704 +}; 1.705 + 1.706 +/** 1.707 + * Sets up WebGL with shaders. 1.708 + * @param {string} canvasName The id of the canvas. 1.709 + * @param {string} vshader The id of the script tag that contains the vertex 1.710 + * shader source. 1.711 + * @param {string} fshader The id of the script tag that contains the fragment 1.712 + * shader source. 1.713 + * @param {!Array.<string>} attribs An array of attrib names used to bind 1.714 + * attribs to the ordinal of the name in this array. 1.715 + * @param {!Array.<number>} opt_clearColor The color to cla 1.716 + * @return {!WebGLContext} The created WebGLContext. 1.717 + */ 1.718 +var setupWebGLWithShaders = function( 1.719 + canvasName, vshader, fshader, attribs) { 1.720 + var canvas = document.getElementById(canvasName); 1.721 + var gl = create3DContext(canvas); 1.722 + if (!gl) { 1.723 + testFailed("No WebGL context found"); 1.724 + } 1.725 + 1.726 + // create our shaders 1.727 + var vertexShader = loadShaderFromScript(gl, vshader); 1.728 + var fragmentShader = loadShaderFromScript(gl, fshader); 1.729 + 1.730 + if (!vertexShader || !fragmentShader) { 1.731 + return null; 1.732 + } 1.733 + 1.734 + // Create the program object 1.735 + program = gl.createProgram(); 1.736 + 1.737 + if (!program) { 1.738 + return null; 1.739 + } 1.740 + 1.741 + // Attach our two shaders to the program 1.742 + gl.attachShader (program, vertexShader); 1.743 + gl.attachShader (program, fragmentShader); 1.744 + 1.745 + // Bind attributes 1.746 + for (var i in attribs) { 1.747 + gl.bindAttribLocation (program, i, attribs[i]); 1.748 + } 1.749 + 1.750 + linkProgram(gl, program); 1.751 + 1.752 + gl.useProgram(program); 1.753 + 1.754 + gl.clearColor(0,0,0,1); 1.755 + gl.clearDepth(1); 1.756 + 1.757 + gl.enable(gl.DEPTH_TEST); 1.758 + gl.enable(gl.BLEND); 1.759 + gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); 1.760 + 1.761 + gl.program = program; 1.762 + return gl; 1.763 +}; 1.764 + 1.765 +/** 1.766 + * Loads text from an external file. This function is synchronous. 1.767 + * @param {string} url The url of the external file. 1.768 + * @param {!function(bool, string): void} callback that is sent a bool for 1.769 + * success and the string. 1.770 + */ 1.771 +var loadTextFileAsync = function(url, callback) { 1.772 + log ("loading: " + url); 1.773 + var error = 'loadTextFileSynchronous failed to load url "' + url + '"'; 1.774 + var request; 1.775 + if (window.XMLHttpRequest) { 1.776 + request = new XMLHttpRequest(); 1.777 + if (request.overrideMimeType) { 1.778 + request.overrideMimeType('text/plain'); 1.779 + } 1.780 + } else { 1.781 + throw 'XMLHttpRequest is disabled'; 1.782 + } 1.783 + try { 1.784 + request.open('GET', url, true); 1.785 + request.onreadystatechange = function() { 1.786 + if (request.readyState == 4) { 1.787 + var text = ''; 1.788 + // HTTP reports success with a 200 status. The file protocol reports 1.789 + // success with zero. HTTP does not use zero as a status code (they 1.790 + // start at 100). 1.791 + // https://developer.mozilla.org/En/Using_XMLHttpRequest 1.792 + var success = request.status == 200 || request.status == 0; 1.793 + if (success) { 1.794 + text = request.responseText; 1.795 + } 1.796 + log("loaded: " + url); 1.797 + callback(success, text); 1.798 + } 1.799 + }; 1.800 + request.send(null); 1.801 + } catch (e) { 1.802 + log("failed to load: " + url); 1.803 + callback(false, ''); 1.804 + } 1.805 +}; 1.806 + 1.807 +// Add your prefix here. 1.808 +var browserPrefixes = [ 1.809 + "", 1.810 + "MOZ_", 1.811 + "OP_", 1.812 + "WEBKIT_" 1.813 +]; 1.814 + 1.815 +/** 1.816 + * Given an extension name like WEBGL_compressed_texture_s3tc 1.817 + * returns the name of the supported version extension, like 1.818 + * WEBKIT_WEBGL_compressed_teture_s3tc 1.819 + * @param {string} name Name of extension to look for 1.820 + * @return {string} name of extension found or undefined if not 1.821 + * found. 1.822 + */ 1.823 +var getSupportedExtensionWithKnownPrefixes = function(gl, name) { 1.824 + var supported = gl.getSupportedExtensions(); 1.825 + for (var ii = 0; ii < browserPrefixes.length; ++ii) { 1.826 + var prefixedName = browserPrefixes[ii] + name; 1.827 + if (supported.indexOf(prefixedName) >= 0) { 1.828 + return prefixedName; 1.829 + } 1.830 + } 1.831 +}; 1.832 + 1.833 +/** 1.834 + * Given an extension name like WEBGL_compressed_texture_s3tc 1.835 + * returns the supported version extension, like 1.836 + * WEBKIT_WEBGL_compressed_teture_s3tc 1.837 + * @param {string} name Name of extension to look for 1.838 + * @return {WebGLExtension} The extension or undefined if not 1.839 + * found. 1.840 + */ 1.841 +var getExtensionWithKnownPrefixes = function(gl, name) { 1.842 + for (var ii = 0; ii < browserPrefixes.length; ++ii) { 1.843 + var prefixedName = browserPrefixes[ii] + name; 1.844 + var ext = gl.getExtension(prefixedName); 1.845 + if (ext) { 1.846 + return ext; 1.847 + } 1.848 + } 1.849 +}; 1.850 + 1.851 +/** 1.852 + * Recursively loads a file as a list. Each line is parsed for a relative 1.853 + * path. If the file ends in .txt the contents of that file is inserted in 1.854 + * the list. 1.855 + * 1.856 + * @param {string} url The url of the external file. 1.857 + * @param {!function(bool, Array<string>): void} callback that is sent a bool 1.858 + * for success and the array of strings. 1.859 + */ 1.860 +var getFileListAsync = function(url, callback) { 1.861 + var files = []; 1.862 + 1.863 + var getFileListImpl = function(url, callback) { 1.864 + var files = []; 1.865 + if (url.substr(url.length - 4) == '.txt') { 1.866 + loadTextFileAsync(url, function() { 1.867 + return function(success, text) { 1.868 + if (!success) { 1.869 + callback(false, ''); 1.870 + return; 1.871 + } 1.872 + var lines = text.split('\n'); 1.873 + var prefix = ''; 1.874 + var lastSlash = url.lastIndexOf('/'); 1.875 + if (lastSlash >= 0) { 1.876 + prefix = url.substr(0, lastSlash + 1); 1.877 + } 1.878 + var fail = false; 1.879 + var count = 1; 1.880 + var index = 0; 1.881 + for (var ii = 0; ii < lines.length; ++ii) { 1.882 + var str = lines[ii].replace(/^\s\s*/, '').replace(/\s\s*$/, ''); 1.883 + if (str.length > 4 && 1.884 + str[0] != '#' && 1.885 + str[0] != ";" && 1.886 + str.substr(0, 2) != "//") { 1.887 + var names = str.split(/ +/); 1.888 + new_url = prefix + str; 1.889 + if (names.length == 1) { 1.890 + new_url = prefix + str; 1.891 + ++count; 1.892 + getFileListImpl(new_url, function(index) { 1.893 + return function(success, new_files) { 1.894 + log("got files: " + new_files.length); 1.895 + if (success) { 1.896 + files[index] = new_files; 1.897 + } 1.898 + finish(success); 1.899 + }; 1.900 + }(index++)); 1.901 + } else { 1.902 + var s = ""; 1.903 + var p = ""; 1.904 + for (var jj = 0; jj < names.length; ++jj) { 1.905 + s += p + prefix + names[jj]; 1.906 + p = " "; 1.907 + } 1.908 + files[index++] = s; 1.909 + } 1.910 + } 1.911 + } 1.912 + finish(true); 1.913 + 1.914 + function finish(success) { 1.915 + if (!success) { 1.916 + fail = true; 1.917 + } 1.918 + --count; 1.919 + log("count: " + count); 1.920 + if (!count) { 1.921 + callback(!fail, files); 1.922 + } 1.923 + } 1.924 + } 1.925 + }()); 1.926 + 1.927 + } else { 1.928 + files.push(url); 1.929 + callback(true, files); 1.930 + } 1.931 + }; 1.932 + 1.933 + getFileListImpl(url, function(success, files) { 1.934 + // flatten 1.935 + var flat = []; 1.936 + flatten(files); 1.937 + function flatten(files) { 1.938 + for (var ii = 0; ii < files.length; ++ii) { 1.939 + var value = files[ii]; 1.940 + if (typeof(value) == "string") { 1.941 + flat.push(value); 1.942 + } else { 1.943 + flatten(value); 1.944 + } 1.945 + } 1.946 + } 1.947 + callback(success, flat); 1.948 + }); 1.949 +}; 1.950 + 1.951 +/** 1.952 + * Gets a file from a file/URL 1.953 + * @param {string} file the URL of the file to get. 1.954 + * @return {string} The contents of the file. 1.955 + */ 1.956 +var readFile = function(file) { 1.957 + var xhr = new XMLHttpRequest(); 1.958 + xhr.open("GET", file, false); 1.959 + xhr.send(); 1.960 + return xhr.responseText.replace(/\r/g, ""); 1.961 +}; 1.962 + 1.963 +var readFileList = function(url) { 1.964 + var files = []; 1.965 + if (url.substr(url.length - 4) == '.txt') { 1.966 + var lines = readFile(url).split('\n'); 1.967 + var prefix = ''; 1.968 + var lastSlash = url.lastIndexOf('/'); 1.969 + if (lastSlash >= 0) { 1.970 + prefix = url.substr(0, lastSlash + 1); 1.971 + } 1.972 + for (var ii = 0; ii < lines.length; ++ii) { 1.973 + var str = lines[ii].replace(/^\s\s*/, '').replace(/\s\s*$/, ''); 1.974 + if (str.length > 4 && 1.975 + str[0] != '#' && 1.976 + str[0] != ";" && 1.977 + str.substr(0, 2) != "//") { 1.978 + var names = str.split(/ +/); 1.979 + if (names.length == 1) { 1.980 + new_url = prefix + str; 1.981 + files = files.concat(readFileList(new_url)); 1.982 + } else { 1.983 + var s = ""; 1.984 + var p = ""; 1.985 + for (var jj = 0; jj < names.length; ++jj) { 1.986 + s += p + prefix + names[jj]; 1.987 + p = " "; 1.988 + } 1.989 + files.push(s); 1.990 + } 1.991 + } 1.992 + } 1.993 + } else { 1.994 + files.push(url); 1.995 + } 1.996 + return files; 1.997 +}; 1.998 + 1.999 +/** 1.1000 + * Loads a shader. 1.1001 + * @param {!WebGLContext} gl The WebGLContext to use. 1.1002 + * @param {string} shaderSource The shader source. 1.1003 + * @param {number} shaderType The type of shader. 1.1004 + * @param {function(string): void) opt_errorCallback callback for errors. 1.1005 + * @return {!WebGLShader} The created shader. 1.1006 + */ 1.1007 +var loadShader = function(gl, shaderSource, shaderType, opt_errorCallback) { 1.1008 + var errFn = opt_errorCallback || error; 1.1009 + // Create the shader object 1.1010 + var shader = gl.createShader(shaderType); 1.1011 + if (shader == null) { 1.1012 + errFn("*** Error: unable to create shader '"+shaderSource+"'"); 1.1013 + return null; 1.1014 + } 1.1015 + 1.1016 + // Load the shader source 1.1017 + gl.shaderSource(shader, shaderSource); 1.1018 + var err = gl.getError(); 1.1019 + if (err != gl.NO_ERROR) { 1.1020 + errFn("*** Error loading shader '" + shader + "':" + glEnumToString(gl, err)); 1.1021 + return null; 1.1022 + } 1.1023 + 1.1024 + // Compile the shader 1.1025 + gl.compileShader(shader); 1.1026 + 1.1027 + // Check the compile status 1.1028 + var compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS); 1.1029 + if (!compiled) { 1.1030 + // Something went wrong during compilation; get the error 1.1031 + lastError = gl.getShaderInfoLog(shader); 1.1032 + errFn("*** Error compiling shader '" + shader + "':" + lastError); 1.1033 + gl.deleteShader(shader); 1.1034 + return null; 1.1035 + } 1.1036 + 1.1037 + return shader; 1.1038 +} 1.1039 + 1.1040 +/** 1.1041 + * Loads a shader from a URL. 1.1042 + * @param {!WebGLContext} gl The WebGLContext to use. 1.1043 + * @param {file} file The URL of the shader source. 1.1044 + * @param {number} type The type of shader. 1.1045 + * @param {function(string): void) opt_errorCallback callback for errors. 1.1046 + * @return {!WebGLShader} The created shader. 1.1047 + */ 1.1048 +var loadShaderFromFile = function(gl, file, type, opt_errorCallback) { 1.1049 + var shaderSource = readFile(file); 1.1050 + return loadShader(gl, shaderSource, type, opt_errorCallback); 1.1051 +}; 1.1052 + 1.1053 +/** 1.1054 + * Gets the content of script. 1.1055 + */ 1.1056 +var getScript = function(scriptId) { 1.1057 + var shaderScript = document.getElementById(scriptId); 1.1058 + if (!shaderScript) { 1.1059 + throw("*** Error: unknown script element" + scriptId); 1.1060 + } 1.1061 + return shaderScript.text; 1.1062 +}; 1.1063 + 1.1064 +/** 1.1065 + * Loads a shader from a script tag. 1.1066 + * @param {!WebGLContext} gl The WebGLContext to use. 1.1067 + * @param {string} scriptId The id of the script tag. 1.1068 + * @param {number} opt_shaderType The type of shader. If not passed in it will 1.1069 + * be derived from the type of the script tag. 1.1070 + * @param {function(string): void) opt_errorCallback callback for errors. 1.1071 + * @return {!WebGLShader} The created shader. 1.1072 + */ 1.1073 +var loadShaderFromScript = function( 1.1074 + gl, scriptId, opt_shaderType, opt_errorCallback) { 1.1075 + var shaderSource = ""; 1.1076 + var shaderType; 1.1077 + var shaderScript = document.getElementById(scriptId); 1.1078 + if (!shaderScript) { 1.1079 + throw("*** Error: unknown script element " + scriptId); 1.1080 + } 1.1081 + shaderSource = shaderScript.text; 1.1082 + 1.1083 + if (!opt_shaderType) { 1.1084 + if (shaderScript.type == "x-shader/x-vertex") { 1.1085 + shaderType = gl.VERTEX_SHADER; 1.1086 + } else if (shaderScript.type == "x-shader/x-fragment") { 1.1087 + shaderType = gl.FRAGMENT_SHADER; 1.1088 + } else if (shaderType != gl.VERTEX_SHADER && shaderType != gl.FRAGMENT_SHADER) { 1.1089 + throw("*** Error: unknown shader type"); 1.1090 + return null; 1.1091 + } 1.1092 + } 1.1093 + 1.1094 + return loadShader( 1.1095 + gl, shaderSource, opt_shaderType ? opt_shaderType : shaderType, 1.1096 + opt_errorCallback); 1.1097 +}; 1.1098 + 1.1099 +var loadStandardProgram = function(gl) { 1.1100 + var program = gl.createProgram(); 1.1101 + gl.attachShader(program, loadStandardVertexShader(gl)); 1.1102 + gl.attachShader(program, loadStandardFragmentShader(gl)); 1.1103 + linkProgram(gl, program); 1.1104 + return program; 1.1105 +}; 1.1106 + 1.1107 +/** 1.1108 + * Loads shaders from files, creates a program, attaches the shaders and links. 1.1109 + * @param {!WebGLContext} gl The WebGLContext to use. 1.1110 + * @param {string} vertexShaderPath The URL of the vertex shader. 1.1111 + * @param {string} fragmentShaderPath The URL of the fragment shader. 1.1112 + * @param {function(string): void) opt_errorCallback callback for errors. 1.1113 + * @return {!WebGLProgram} The created program. 1.1114 + */ 1.1115 +var loadProgramFromFile = function( 1.1116 + gl, vertexShaderPath, fragmentShaderPath, opt_errorCallback) { 1.1117 + var program = gl.createProgram(); 1.1118 + gl.attachShader( 1.1119 + program, 1.1120 + loadShaderFromFile( 1.1121 + gl, vertexShaderPath, gl.VERTEX_SHADER, opt_errorCallback)); 1.1122 + gl.attachShader( 1.1123 + program, 1.1124 + loadShaderFromFile( 1.1125 + gl, fragmentShaderPath, gl.FRAGMENT_SHADER, opt_errorCallback)); 1.1126 + linkProgram(gl, program, opt_errorCallback); 1.1127 + return program; 1.1128 +}; 1.1129 + 1.1130 +/** 1.1131 + * Loads shaders from script tags, creates a program, attaches the shaders and 1.1132 + * links. 1.1133 + * @param {!WebGLContext} gl The WebGLContext to use. 1.1134 + * @param {string} vertexScriptId The id of the script tag that contains the 1.1135 + * vertex shader. 1.1136 + * @param {string} fragmentScriptId The id of the script tag that contains the 1.1137 + * fragment shader. 1.1138 + * @param {function(string): void) opt_errorCallback callback for errors. 1.1139 + * @return {!WebGLProgram} The created program. 1.1140 + */ 1.1141 +var loadProgramFromScript = function loadProgramFromScript( 1.1142 + gl, vertexScriptId, fragmentScriptId, opt_errorCallback) { 1.1143 + var program = gl.createProgram(); 1.1144 + gl.attachShader( 1.1145 + program, 1.1146 + loadShaderFromScript( 1.1147 + gl, vertexScriptId, gl.VERTEX_SHADER, opt_errorCallback)); 1.1148 + gl.attachShader( 1.1149 + program, 1.1150 + loadShaderFromScript( 1.1151 + gl, fragmentScriptId, gl.FRAGMENT_SHADER, opt_errorCallback)); 1.1152 + linkProgram(gl, program, opt_errorCallback); 1.1153 + return program; 1.1154 +}; 1.1155 + 1.1156 +/** 1.1157 + * Loads shaders from source, creates a program, attaches the shaders and 1.1158 + * links. 1.1159 + * @param {!WebGLContext} gl The WebGLContext to use. 1.1160 + * @param {string} vertexShader The vertex shader. 1.1161 + * @param {string} fragmentShader The fragment shader. 1.1162 + * @param {function(string): void) opt_errorCallback callback for errors. 1.1163 + * @return {!WebGLProgram} The created program. 1.1164 + */ 1.1165 +var loadProgram = function( 1.1166 + gl, vertexShader, fragmentShader, opt_errorCallback) { 1.1167 + var program = gl.createProgram(); 1.1168 + gl.attachShader( 1.1169 + program, 1.1170 + loadShader( 1.1171 + gl, vertexShader, gl.VERTEX_SHADER, opt_errorCallback)); 1.1172 + gl.attachShader( 1.1173 + program, 1.1174 + loadShader( 1.1175 + gl, fragmentShader, gl.FRAGMENT_SHADER, opt_errorCallback)); 1.1176 + linkProgram(gl, program, opt_errorCallback); 1.1177 + return program; 1.1178 +}; 1.1179 + 1.1180 +/** 1.1181 + * Loads shaders from source, creates a program, attaches the shaders and 1.1182 + * links but expects error. 1.1183 + * 1.1184 + * GLSL 1.0.17 10.27 effectively says that compileShader can 1.1185 + * always succeed as long as linkProgram fails so we can't 1.1186 + * rely on compileShader failing. This function expects 1.1187 + * one of the shader to fail OR linking to fail. 1.1188 + * 1.1189 + * @param {!WebGLContext} gl The WebGLContext to use. 1.1190 + * @param {string} vertexShaderScriptId The vertex shader. 1.1191 + * @param {string} fragmentShaderScriptId The fragment shader. 1.1192 + * @return {WebGLProgram} The created program. 1.1193 + */ 1.1194 +var loadProgramFromScriptExpectError = function( 1.1195 + gl, vertexShaderScriptId, fragmentShaderScriptId) { 1.1196 + var vertexShader = loadShaderFromScript(gl, vertexShaderScriptId); 1.1197 + if (!vertexShader) { 1.1198 + return null; 1.1199 + } 1.1200 + var fragmentShader = loadShaderFromScript(gl, fragmentShaderScriptId); 1.1201 + if (!fragmentShader) { 1.1202 + return null; 1.1203 + } 1.1204 + var linkSuccess = true; 1.1205 + var program = gl.createProgram(); 1.1206 + gl.attachShader(program, vertexShader); 1.1207 + gl.attachShader(program, fragmentShader); 1.1208 + linkSuccess = true; 1.1209 + linkProgram(gl, program, function() { 1.1210 + linkSuccess = false; 1.1211 + }); 1.1212 + return linkSuccess ? program : null; 1.1213 +}; 1.1214 + 1.1215 +var basePath; 1.1216 +var getBasePath = function() { 1.1217 + if (!basePath) { 1.1218 + var expectedBase = "webgl-test-utils.js"; 1.1219 + var scripts = document.getElementsByTagName('script'); 1.1220 + for (var script, i = 0; script = scripts[i]; i++) { 1.1221 + var src = script.src; 1.1222 + var l = src.length; 1.1223 + if (src.substr(l - expectedBase.length) == expectedBase) { 1.1224 + basePath = src.substr(0, l - expectedBase.length); 1.1225 + } 1.1226 + } 1.1227 + } 1.1228 + return basePath; 1.1229 +}; 1.1230 + 1.1231 +var loadStandardVertexShader = function(gl) { 1.1232 + return loadShaderFromFile( 1.1233 + gl, getBasePath() + "vertexShader.vert", gl.VERTEX_SHADER); 1.1234 +}; 1.1235 + 1.1236 +var loadStandardFragmentShader = function(gl) { 1.1237 + return loadShaderFromFile( 1.1238 + gl, getBasePath() + "fragmentShader.frag", gl.FRAGMENT_SHADER); 1.1239 +}; 1.1240 + 1.1241 +/** 1.1242 + * Loads an image asynchronously. 1.1243 + * @param {string} url URL of image to load. 1.1244 + * @param {!function(!Element): void} callback Function to call 1.1245 + * with loaded image. 1.1246 + */ 1.1247 +var loadImageAsync = function(url, callback) { 1.1248 + var img = document.createElement('img'); 1.1249 + img.onload = function() { 1.1250 + callback(img); 1.1251 + }; 1.1252 + img.src = url; 1.1253 +}; 1.1254 + 1.1255 +/** 1.1256 + * Loads an array of images. 1.1257 + * @param {!Array.<string>} urls URLs of images to load. 1.1258 + * @param {!function(!{string, img}): void} callback. Callback 1.1259 + * that gets passed map of urls to img tags. 1.1260 + */ 1.1261 +var loadImagesAsync = function(urls, callback) { 1.1262 + var count = 1; 1.1263 + var images = { }; 1.1264 + function countDown() { 1.1265 + --count; 1.1266 + if (count == 0) { 1.1267 + callback(images); 1.1268 + } 1.1269 + } 1.1270 + function imageLoaded(url) { 1.1271 + return function(img) { 1.1272 + images[url] = img; 1.1273 + countDown(); 1.1274 + } 1.1275 + } 1.1276 + for (var ii = 0; ii < urls.length; ++ii) { 1.1277 + ++count; 1.1278 + loadImageAsync(urls[ii], imageLoaded(urls[ii])); 1.1279 + } 1.1280 + countDown(); 1.1281 +}; 1.1282 + 1.1283 +var getUrlArguments = function() { 1.1284 + var args = {}; 1.1285 + try { 1.1286 + var s = window.location.href; 1.1287 + var q = s.indexOf("?"); 1.1288 + var e = s.indexOf("#"); 1.1289 + if (e < 0) { 1.1290 + e = s.length; 1.1291 + } 1.1292 + var query = s.substring(q + 1, e); 1.1293 + var pairs = query.split("&"); 1.1294 + for (var ii = 0; ii < pairs.length; ++ii) { 1.1295 + var keyValue = pairs[ii].split("="); 1.1296 + var key = keyValue[0]; 1.1297 + var value = decodeURIComponent(keyValue[1]); 1.1298 + args[key] = value; 1.1299 + } 1.1300 + } catch (e) { 1.1301 + throw "could not parse url"; 1.1302 + } 1.1303 + return args; 1.1304 +}; 1.1305 + 1.1306 +var makeImage = function(canvas) { 1.1307 + var img = document.createElement('img'); 1.1308 + img.src = canvas.toDataURL(); 1.1309 + return img; 1.1310 +}; 1.1311 + 1.1312 +var insertImage = function(element, caption, img) { 1.1313 + var div = document.createElement("div"); 1.1314 + div.appendChild(img); 1.1315 + var label = document.createElement("div"); 1.1316 + label.appendChild(document.createTextNode(caption)); 1.1317 + div.appendChild(label); 1.1318 + element.appendChild(div); 1.1319 +}; 1.1320 + 1.1321 +var addShaderSource = function(element, label, source) { 1.1322 + var div = document.createElement("div"); 1.1323 + var s = document.createElement("pre"); 1.1324 + s.className = "shader-source"; 1.1325 + s.style.display = "none"; 1.1326 + var ol = document.createElement("ol"); 1.1327 + //s.appendChild(document.createTextNode(source)); 1.1328 + var lines = source.split("\n"); 1.1329 + for (var ii = 0; ii < lines.length; ++ii) { 1.1330 + var line = lines[ii]; 1.1331 + var li = document.createElement("li"); 1.1332 + li.appendChild(document.createTextNode(line)); 1.1333 + ol.appendChild(li); 1.1334 + } 1.1335 + s.appendChild(ol); 1.1336 + var l = document.createElement("a"); 1.1337 + l.href = "show-shader-source"; 1.1338 + l.appendChild(document.createTextNode(label)); 1.1339 + l.addEventListener('click', function(event) { 1.1340 + if (event.preventDefault) { 1.1341 + event.preventDefault(); 1.1342 + } 1.1343 + s.style.display = (s.style.display == 'none') ? 'block' : 'none'; 1.1344 + return false; 1.1345 + }, false); 1.1346 + div.appendChild(l); 1.1347 + div.appendChild(s); 1.1348 + element.appendChild(div); 1.1349 +} 1.1350 + 1.1351 +return { 1.1352 + addShaderSource: addShaderSource, 1.1353 + clearAndDrawUnitQuad : clearAndDrawUnitQuad, 1.1354 + create3DContext: create3DContext, 1.1355 + create3DContextWithWrapperThatThrowsOnGLError: 1.1356 + create3DContextWithWrapperThatThrowsOnGLError, 1.1357 + checkCanvas: checkCanvas, 1.1358 + checkCanvasRect: checkCanvasRect, 1.1359 + createColoredTexture: createColoredTexture, 1.1360 + drawQuad: drawQuad, 1.1361 + drawUnitQuad: drawUnitQuad, 1.1362 + endsWith: endsWith, 1.1363 + getExtensionWithKnownPrefixes: getExtensionWithKnownPrefixes, 1.1364 + getFileListAsync: getFileListAsync, 1.1365 + getLastError: getLastError, 1.1366 + getScript: getScript, 1.1367 + getSupportedExtensionWithKnownPrefixes: getSupportedExtensionWithKnownPrefixes, 1.1368 + getUrlArguments: getUrlArguments, 1.1369 + glEnumToString: glEnumToString, 1.1370 + glErrorShouldBe: glErrorShouldBe, 1.1371 + fillTexture: fillTexture, 1.1372 + insertImage: insertImage, 1.1373 + loadImageAsync: loadImageAsync, 1.1374 + loadImagesAsync: loadImagesAsync, 1.1375 + loadProgram: loadProgram, 1.1376 + loadProgramFromFile: loadProgramFromFile, 1.1377 + loadProgramFromScript: loadProgramFromScript, 1.1378 + loadProgramFromScriptExpectError: loadProgramFromScriptExpectError, 1.1379 + loadShader: loadShader, 1.1380 + loadShaderFromFile: loadShaderFromFile, 1.1381 + loadShaderFromScript: loadShaderFromScript, 1.1382 + loadStandardProgram: loadStandardProgram, 1.1383 + loadStandardVertexShader: loadStandardVertexShader, 1.1384 + loadStandardFragmentShader: loadStandardFragmentShader, 1.1385 + loadTextFileAsync: loadTextFileAsync, 1.1386 + loadTexture: loadTexture, 1.1387 + log: log, 1.1388 + loggingOff: loggingOff, 1.1389 + makeImage: makeImage, 1.1390 + error: error, 1.1391 + setupProgram: setupProgram, 1.1392 + setupQuad: setupQuad, 1.1393 + setupSimpleTextureFragmentShader: setupSimpleTextureFragmentShader, 1.1394 + setupSimpleTextureProgram: setupSimpleTextureProgram, 1.1395 + setupSimpleTextureVertexShader: setupSimpleTextureVertexShader, 1.1396 + setupTexturedQuad: setupTexturedQuad, 1.1397 + setupTexturedQuadWithTexCoords: setupTexturedQuadWithTexCoords, 1.1398 + setupUnitQuad: setupUnitQuad, 1.1399 + setupUnitQuadWithTexCoords: setupUnitQuadWithTexCoords, 1.1400 + setupWebGLWithShaders: setupWebGLWithShaders, 1.1401 + startsWith: startsWith, 1.1402 + shouldGenerateGLError: shouldGenerateGLError, 1.1403 + readFile: readFile, 1.1404 + readFileList: readFileList, 1.1405 + 1.1406 + none: false 1.1407 +}; 1.1408 + 1.1409 +}()); 1.1410 + 1.1411 +