OLD | NEW |
(Empty) | |
| 1 /* |
| 2 ** Copyright (c) 2012 The Khronos Group Inc. |
| 3 ** |
| 4 ** Permission is hereby granted, free of charge, to any person obtaining a |
| 5 ** copy of this software and/or associated documentation files (the |
| 6 ** "Materials"), to deal in the Materials without restriction, including |
| 7 ** without limitation the rights to use, copy, modify, merge, publish, |
| 8 ** distribute, sublicense, and/or sell copies of the Materials, and to |
| 9 ** permit persons to whom the Materials are furnished to do so, subject to |
| 10 ** the following conditions: |
| 11 ** |
| 12 ** The above copyright notice and this permission notice shall be included |
| 13 ** in all copies or substantial portions of the Materials. |
| 14 ** |
| 15 ** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
| 16 ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| 17 ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. |
| 18 ** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY |
| 19 ** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, |
| 20 ** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE |
| 21 ** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. |
| 22 */ |
| 23 var WebGLTestUtils = (function() { |
| 24 "use strict"; |
| 25 |
| 26 /** |
| 27 * Wrapped logging function. |
| 28 * @param {string} msg The message to log. |
| 29 */ |
| 30 var log = function(msg) { |
| 31 if (window.console && window.console.log) { |
| 32 window.console.log(msg); |
| 33 } |
| 34 }; |
| 35 |
| 36 /** |
| 37 * Wrapped logging function. |
| 38 * @param {string} msg The message to log. |
| 39 */ |
| 40 var error = function(msg) { |
| 41 if (window.console) { |
| 42 if (window.console.error) { |
| 43 window.console.error(msg); |
| 44 } |
| 45 else if (window.console.log) { |
| 46 window.console.log(msg); |
| 47 } |
| 48 } |
| 49 }; |
| 50 |
| 51 /** |
| 52 * Turn off all logging. |
| 53 */ |
| 54 var loggingOff = function() { |
| 55 log = function() {}; |
| 56 error = function() {}; |
| 57 }; |
| 58 |
| 59 /** |
| 60 * Converts a WebGL enum to a string. |
| 61 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. |
| 62 * @param {number} value The enum value. |
| 63 * @return {string} The enum as a string. |
| 64 */ |
| 65 var glEnumToString = function(gl, value) { |
| 66 for (var p in gl) { |
| 67 if (gl[p] == value) { |
| 68 return p; |
| 69 } |
| 70 } |
| 71 return "0x" + value.toString(16); |
| 72 }; |
| 73 |
| 74 var lastError = ""; |
| 75 |
| 76 /** |
| 77 * Returns the last compiler/linker error. |
| 78 * @return {string} The last compiler/linker error. |
| 79 */ |
| 80 var getLastError = function() { |
| 81 return lastError; |
| 82 }; |
| 83 |
| 84 /** |
| 85 * Whether a haystack ends with a needle. |
| 86 * @param {string} haystack String to search |
| 87 * @param {string} needle String to search for. |
| 88 * @param {boolean} True if haystack ends with needle. |
| 89 */ |
| 90 var endsWith = function(haystack, needle) { |
| 91 return haystack.substr(haystack.length - needle.length) === needle; |
| 92 }; |
| 93 |
| 94 /** |
| 95 * Whether a haystack starts with a needle. |
| 96 * @param {string} haystack String to search |
| 97 * @param {string} needle String to search for. |
| 98 * @param {boolean} True if haystack starts with needle. |
| 99 */ |
| 100 var startsWith = function(haystack, needle) { |
| 101 return haystack.substr(0, needle.length) === needle; |
| 102 }; |
| 103 |
| 104 /** |
| 105 * A vertex shader for a single texture. |
| 106 * @type {string} |
| 107 */ |
| 108 var simpleTextureVertexShader = [ |
| 109 'attribute vec4 vPosition;', |
| 110 'attribute vec2 texCoord0;', |
| 111 'varying vec2 texCoord;', |
| 112 'void main() {', |
| 113 ' gl_Position = vPosition;', |
| 114 ' texCoord = texCoord0;', |
| 115 '}'].join('\n'); |
| 116 |
| 117 /** |
| 118 * A fragment shader for a single texture. |
| 119 * @type {string} |
| 120 */ |
| 121 var simpleTextureFragmentShader = [ |
| 122 'precision mediump float;', |
| 123 'uniform sampler2D tex;', |
| 124 'varying vec2 texCoord;', |
| 125 'void main() {', |
| 126 ' gl_FragData[0] = texture2D(tex, texCoord);', |
| 127 '}'].join('\n'); |
| 128 |
| 129 /** |
| 130 * A vertex shader for a single texture. |
| 131 * @type {string} |
| 132 */ |
| 133 var noTexCoordTextureVertexShader = [ |
| 134 'attribute vec4 vPosition;', |
| 135 'varying vec2 texCoord;', |
| 136 'void main() {', |
| 137 ' gl_Position = vPosition;', |
| 138 ' texCoord = vPosition.xy * 0.5 + 0.5;', |
| 139 '}'].join('\n'); |
| 140 |
| 141 /** |
| 142 * A vertex shader for a uniform color. |
| 143 * @type {string} |
| 144 */ |
| 145 var simpleColorVertexShader = [ |
| 146 'attribute vec4 vPosition;', |
| 147 'void main() {', |
| 148 ' gl_Position = vPosition;', |
| 149 '}'].join('\n'); |
| 150 |
| 151 /** |
| 152 * A fragment shader for a uniform color. |
| 153 * @type {string} |
| 154 */ |
| 155 var simpleColorFragmentShader = [ |
| 156 'precision mediump float;', |
| 157 'uniform vec4 u_color;', |
| 158 'void main() {', |
| 159 ' gl_FragData[0] = u_color;', |
| 160 '}'].join('\n'); |
| 161 |
| 162 /** |
| 163 * A vertex shader for vertex colors. |
| 164 * @type {string} |
| 165 */ |
| 166 var simpleVertexColorVertexShader = [ |
| 167 'attribute vec4 vPosition;', |
| 168 'attribute vec4 a_color;', |
| 169 'varying vec4 v_color;', |
| 170 'void main() {', |
| 171 ' gl_Position = vPosition;', |
| 172 ' v_color = a_color;', |
| 173 '}'].join('\n'); |
| 174 |
| 175 /** |
| 176 * A fragment shader for vertex colors. |
| 177 * @type {string} |
| 178 */ |
| 179 var simpleVertexColorFragmentShader = [ |
| 180 'precision mediump float;', |
| 181 'varying vec4 v_color;', |
| 182 'void main() {', |
| 183 ' gl_FragData[0] = v_color;', |
| 184 '}'].join('\n'); |
| 185 |
| 186 /** |
| 187 * Creates a simple texture vertex shader. |
| 188 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. |
| 189 * @return {!WebGLShader} |
| 190 */ |
| 191 var setupSimpleTextureVertexShader = function(gl) { |
| 192 return loadShader(gl, simpleTextureVertexShader, gl.VERTEX_SHADER); |
| 193 }; |
| 194 |
| 195 /** |
| 196 * Creates a simple texture fragment shader. |
| 197 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. |
| 198 * @return {!WebGLShader} |
| 199 */ |
| 200 var setupSimpleTextureFragmentShader = function(gl) { |
| 201 return loadShader( |
| 202 gl, simpleTextureFragmentShader, gl.FRAGMENT_SHADER); |
| 203 }; |
| 204 |
| 205 /** |
| 206 * Creates a texture vertex shader that doesn't need texcoords. |
| 207 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. |
| 208 * @return {!WebGLShader} |
| 209 */ |
| 210 var setupNoTexCoordTextureVertexShader = function(gl) { |
| 211 return loadShader(gl, noTexCoordTextureVertexShader, gl.VERTEX_SHADER); |
| 212 }; |
| 213 |
| 214 /** |
| 215 * Creates a simple vertex color vertex shader. |
| 216 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. |
| 217 * @return {!WebGLShader} |
| 218 */ |
| 219 var setupSimpleVertexColorVertexShader = function(gl) { |
| 220 return loadShader(gl, simpleVertexColorVertexShader, gl.VERTEX_SHADER); |
| 221 }; |
| 222 |
| 223 /** |
| 224 * Creates a simple vertex color fragment shader. |
| 225 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. |
| 226 * @return {!WebGLShader} |
| 227 */ |
| 228 var setupSimpleVertexColorFragmentShader = function(gl) { |
| 229 return loadShader( |
| 230 gl, simpleVertexColorFragmentShader, gl.FRAGMENT_SHADER); |
| 231 }; |
| 232 |
| 233 /** |
| 234 * Creates a simple color vertex shader. |
| 235 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. |
| 236 * @return {!WebGLShader} |
| 237 */ |
| 238 var setupSimpleColorVertexShader = function(gl) { |
| 239 return loadShader(gl, simpleColorVertexShader, gl.VERTEX_SHADER); |
| 240 }; |
| 241 |
| 242 /** |
| 243 * Creates a simple color fragment shader. |
| 244 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. |
| 245 * @return {!WebGLShader} |
| 246 */ |
| 247 var setupSimpleColorFragmentShader = function(gl) { |
| 248 return loadShader( |
| 249 gl, simpleColorFragmentShader, gl.FRAGMENT_SHADER); |
| 250 }; |
| 251 |
| 252 /** |
| 253 * Creates a program, attaches shaders, binds attrib locations, links the |
| 254 * program and calls useProgram. |
| 255 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. |
| 256 * @param {!Array.<!WebGLShader|string>} shaders The shaders to |
| 257 * attach, or the source, or the id of a script to get |
| 258 * the source from. |
| 259 * @param {!Array.<string>} opt_attribs The attribs names. |
| 260 * @param {!Array.<number>} opt_locations The locations for the attribs. |
| 261 */ |
| 262 var setupProgram = function(gl, shaders, opt_attribs, opt_locations) { |
| 263 var realShaders = []; |
| 264 var program = gl.createProgram(); |
| 265 var shaderCount = 0; |
| 266 for (var ii = 0; ii < shaders.length; ++ii) { |
| 267 var shader = shaders[ii]; |
| 268 var shaderType = undefined; |
| 269 if (typeof shader == 'string') { |
| 270 var element = document.getElementById(shader); |
| 271 if (element) { |
| 272 if (element.type != "x-shader/x-vertex" && element.type != "x-shader/x-f
ragment") |
| 273 shaderType = ii ? gl.FRAGMENT_SHADER : gl.VERTEX_SHADER; |
| 274 shader = loadShaderFromScript(gl, shader, shaderType); |
| 275 } else if (endsWith(shader, ".vert")) { |
| 276 shader = loadShaderFromFile(gl, shader, gl.VERTEX_SHADER); |
| 277 } else if (endsWith(shader, ".frag")) { |
| 278 shader = loadShaderFromFile(gl, shader, gl.FRAGMENT_SHADER); |
| 279 } else { |
| 280 shader = loadShader(gl, shader, ii ? gl.FRAGMENT_SHADER : gl.VERTEX_SHAD
ER); |
| 281 } |
| 282 } |
| 283 if (shader) { |
| 284 ++shaderCount; |
| 285 gl.attachShader(program, shader); |
| 286 } |
| 287 } |
| 288 if (shaderCount != 2) { |
| 289 error("Error in compiling shader"); |
| 290 return null; |
| 291 } |
| 292 if (opt_attribs) { |
| 293 for (var ii = 0; ii < opt_attribs.length; ++ii) { |
| 294 gl.bindAttribLocation( |
| 295 program, |
| 296 opt_locations ? opt_locations[ii] : ii, |
| 297 opt_attribs[ii]); |
| 298 } |
| 299 } |
| 300 gl.linkProgram(program); |
| 301 |
| 302 // Check the link status |
| 303 var linked = gl.getProgramParameter(program, gl.LINK_STATUS); |
| 304 if (!linked) { |
| 305 // something went wrong with the link |
| 306 lastError = gl.getProgramInfoLog (program); |
| 307 error("Error in program linking:" + lastError); |
| 308 |
| 309 gl.deleteProgram(program); |
| 310 return null; |
| 311 } |
| 312 |
| 313 gl.useProgram(program); |
| 314 return program; |
| 315 }; |
| 316 |
| 317 /** |
| 318 * Creates a simple texture program. |
| 319 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. |
| 320 * @return {WebGLProgram} |
| 321 */ |
| 322 var setupNoTexCoordTextureProgram = function(gl) { |
| 323 var vs = setupNoTexCoordTextureVertexShader(gl); |
| 324 var fs = setupSimpleTextureFragmentShader(gl); |
| 325 if (!vs || !fs) { |
| 326 return null; |
| 327 } |
| 328 var program = setupProgram( |
| 329 gl, |
| 330 [vs, fs], |
| 331 ['vPosition'], |
| 332 [0]); |
| 333 if (!program) { |
| 334 gl.deleteShader(fs); |
| 335 gl.deleteShader(vs); |
| 336 } |
| 337 gl.useProgram(program); |
| 338 return program; |
| 339 }; |
| 340 |
| 341 /** |
| 342 * Creates a simple texture program. |
| 343 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. |
| 344 * @param {number} opt_positionLocation The attrib location for position. |
| 345 * @param {number} opt_texcoordLocation The attrib location for texture coords. |
| 346 * @return {WebGLProgram} |
| 347 */ |
| 348 var setupSimpleTextureProgram = function( |
| 349 gl, opt_positionLocation, opt_texcoordLocation) { |
| 350 opt_positionLocation = opt_positionLocation || 0; |
| 351 opt_texcoordLocation = opt_texcoordLocation || 1; |
| 352 var vs = setupSimpleTextureVertexShader(gl); |
| 353 var fs = setupSimpleTextureFragmentShader(gl); |
| 354 if (!vs || !fs) { |
| 355 return null; |
| 356 } |
| 357 var program = setupProgram( |
| 358 gl, |
| 359 [vs, fs], |
| 360 ['vPosition', 'texCoord0'], |
| 361 [opt_positionLocation, opt_texcoordLocation]); |
| 362 if (!program) { |
| 363 gl.deleteShader(fs); |
| 364 gl.deleteShader(vs); |
| 365 } |
| 366 gl.useProgram(program); |
| 367 return program; |
| 368 }; |
| 369 |
| 370 /** |
| 371 * Creates a simple vertex color program. |
| 372 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. |
| 373 * @param {number} opt_positionLocation The attrib location for position. |
| 374 * @param {number} opt_vertexColorLocation The attrib location |
| 375 * for vertex colors. |
| 376 * @return {WebGLProgram} |
| 377 */ |
| 378 var setupSimpleVertexColorProgram = function( |
| 379 gl, opt_positionLocation, opt_vertexColorLocation) { |
| 380 opt_positionLocation = opt_positionLocation || 0; |
| 381 opt_vertexColorLocation = opt_vertexColorLocation || 1; |
| 382 var vs = setupSimpleVertexColorVertexShader(gl); |
| 383 var fs = setupSimpleVertexColorFragmentShader(gl); |
| 384 if (!vs || !fs) { |
| 385 return null; |
| 386 } |
| 387 var program = setupProgram( |
| 388 gl, |
| 389 [vs, fs], |
| 390 ['vPosition', 'a_color'], |
| 391 [opt_positionLocation, opt_vertexColorLocation]); |
| 392 if (!program) { |
| 393 gl.deleteShader(fs); |
| 394 gl.deleteShader(vs); |
| 395 } |
| 396 gl.useProgram(program); |
| 397 return program; |
| 398 }; |
| 399 |
| 400 /** |
| 401 * Creates a simple color program. |
| 402 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. |
| 403 * @param {number} opt_positionLocation The attrib location for position. |
| 404 * @return {WebGLProgram} |
| 405 */ |
| 406 var setupSimpleColorProgram = function(gl, opt_positionLocation) { |
| 407 opt_positionLocation = opt_positionLocation || 0; |
| 408 var vs = setupSimpleColorVertexShader(gl); |
| 409 var fs = setupSimpleColorFragmentShader(gl); |
| 410 if (!vs || !fs) { |
| 411 return null; |
| 412 } |
| 413 var program = setupProgram( |
| 414 gl, |
| 415 [vs, fs], |
| 416 ['vPosition'], |
| 417 [opt_positionLocation]); |
| 418 if (!program) { |
| 419 gl.deleteShader(fs); |
| 420 gl.deleteShader(vs); |
| 421 } |
| 422 gl.useProgram(program); |
| 423 return program; |
| 424 }; |
| 425 |
| 426 /** |
| 427 * Creates buffers for a textured unit quad and attaches them to vertex attribs. |
| 428 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. |
| 429 * @param {number} opt_positionLocation The attrib location for position. |
| 430 * @param {number} opt_texcoordLocation The attrib location for texture coords. |
| 431 * @return {!Array.<WebGLBuffer>} The buffer objects that were |
| 432 * created. |
| 433 */ |
| 434 var setupUnitQuad = function(gl, opt_positionLocation, opt_texcoordLocation) { |
| 435 return setupUnitQuadWithTexCoords(gl, [ 0.0, 0.0 ], [ 1.0, 1.0 ], |
| 436 opt_positionLocation, opt_texcoordLocation); |
| 437 }; |
| 438 |
| 439 /** |
| 440 * Creates buffers for a textured unit quad with specified lower left |
| 441 * and upper right texture coordinates, and attaches them to vertex |
| 442 * attribs. |
| 443 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. |
| 444 * @param {!Array.<number>} lowerLeftTexCoords The texture coordinates for the l
ower left corner. |
| 445 * @param {!Array.<number>} upperRightTexCoords The texture coordinates for the
upper right corner. |
| 446 * @param {number} opt_positionLocation The attrib location for position. |
| 447 * @param {number} opt_texcoordLocation The attrib location for texture coords. |
| 448 * @return {!Array.<WebGLBuffer>} The buffer objects that were |
| 449 * created. |
| 450 */ |
| 451 var setupUnitQuadWithTexCoords = function( |
| 452 gl, lowerLeftTexCoords, upperRightTexCoords, |
| 453 opt_positionLocation, opt_texcoordLocation) { |
| 454 return setupQuad(gl, { |
| 455 positionLocation: opt_positionLocation || 0, |
| 456 texcoordLocation: opt_texcoordLocation || 1, |
| 457 lowerLeftTexCoords: lowerLeftTexCoords, |
| 458 upperRightTexCoords: upperRightTexCoords, |
| 459 }); |
| 460 }; |
| 461 |
| 462 /** |
| 463 * Makes a quad with various options. |
| 464 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. |
| 465 * @param {!Object} options. |
| 466 * |
| 467 * scale: scale to multiple unit quad values by. default 1.0. |
| 468 * positionLocation: attribute location for position. |
| 469 * texcoordLocation: attribute location for texcoords. |
| 470 * If this does not exist no texture coords are created. |
| 471 * lowerLeftTexCoords: an array of 2 values for the |
| 472 * lowerLeftTexCoords. |
| 473 * upperRightTexCoords: an array of 2 values for the |
| 474 * upperRightTexCoords. |
| 475 */ |
| 476 var setupQuad = function(gl, options) { |
| 477 var positionLocation = options.positionLocation || 0; |
| 478 var scale = options.scale || 1; |
| 479 |
| 480 var objects = []; |
| 481 |
| 482 var vertexObject = gl.createBuffer(); |
| 483 gl.bindBuffer(gl.ARRAY_BUFFER, vertexObject); |
| 484 gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ |
| 485 1.0 * scale , 1.0 * scale, |
| 486 -1.0 * scale , 1.0 * scale, |
| 487 -1.0 * scale , -1.0 * scale, |
| 488 1.0 * scale , 1.0 * scale, |
| 489 -1.0 * scale , -1.0 * scale, |
| 490 1.0 * scale , -1.0 * scale,]), gl.STATIC_DRAW); |
| 491 gl.enableVertexAttribArray(positionLocation); |
| 492 gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0); |
| 493 objects.push(vertexObject); |
| 494 |
| 495 if (options.texcoordLocation !== undefined) { |
| 496 var llx = options.lowerLeftTexCoords[0]; |
| 497 var lly = options.lowerLeftTexCoords[1]; |
| 498 var urx = options.upperRightTexCoords[0]; |
| 499 var ury = options.upperRightTexCoords[1]; |
| 500 |
| 501 var vertexObject = gl.createBuffer(); |
| 502 gl.bindBuffer(gl.ARRAY_BUFFER, vertexObject); |
| 503 gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ |
| 504 urx, ury, |
| 505 llx, ury, |
| 506 llx, lly, |
| 507 urx, ury, |
| 508 llx, lly, |
| 509 urx, lly]), gl.STATIC_DRAW); |
| 510 gl.enableVertexAttribArray(options.texcoordLocation); |
| 511 gl.vertexAttribPointer(options.texcoordLocation, 2, gl.FLOAT, false, 0, 0); |
| 512 objects.push(vertexObject); |
| 513 } |
| 514 |
| 515 return objects; |
| 516 }; |
| 517 |
| 518 /** |
| 519 * Creates a program and buffers for rendering a textured quad. |
| 520 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. |
| 521 * @param {number} opt_positionLocation The attrib location for |
| 522 * position. Default = 0. |
| 523 * @param {number} opt_texcoordLocation The attrib location for |
| 524 * texture coords. Default = 1. |
| 525 * @return {!WebGLProgram} |
| 526 */ |
| 527 var setupTexturedQuad = function( |
| 528 gl, opt_positionLocation, opt_texcoordLocation) { |
| 529 var program = setupSimpleTextureProgram( |
| 530 gl, opt_positionLocation, opt_texcoordLocation); |
| 531 setupUnitQuad(gl, opt_positionLocation, opt_texcoordLocation); |
| 532 return program; |
| 533 }; |
| 534 |
| 535 /** |
| 536 * Creates a program and buffers for rendering a color quad. |
| 537 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. |
| 538 * @param {number} opt_positionLocation The attrib location for position. |
| 539 * @return {!WebGLProgram} |
| 540 */ |
| 541 var setupColorQuad = function(gl, opt_positionLocation) { |
| 542 opt_positionLocation = opt_positionLocation || 0; |
| 543 var program = setupSimpleColorProgram(gl); |
| 544 setupUnitQuad(gl, opt_positionLocation); |
| 545 return program; |
| 546 }; |
| 547 |
| 548 /** |
| 549 * Creates a program and buffers for rendering a textured quad with |
| 550 * specified lower left and upper right texture coordinates. |
| 551 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. |
| 552 * @param {!Array.<number>} lowerLeftTexCoords The texture coordinates for the l
ower left corner. |
| 553 * @param {!Array.<number>} upperRightTexCoords The texture coordinates for the
upper right corner. |
| 554 * @param {number} opt_positionLocation The attrib location for position. |
| 555 * @param {number} opt_texcoordLocation The attrib location for texture coords. |
| 556 * @return {!WebGLProgram} |
| 557 */ |
| 558 var setupTexturedQuadWithTexCoords = function( |
| 559 gl, lowerLeftTexCoords, upperRightTexCoords, |
| 560 opt_positionLocation, opt_texcoordLocation) { |
| 561 var program = setupSimpleTextureProgram( |
| 562 gl, opt_positionLocation, opt_texcoordLocation); |
| 563 setupUnitQuadWithTexCoords(gl, lowerLeftTexCoords, upperRightTexCoords, |
| 564 opt_positionLocation, opt_texcoordLocation); |
| 565 return program; |
| 566 }; |
| 567 |
| 568 /** |
| 569 * Creates a unit quad with only positions of a given resolution. |
| 570 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. |
| 571 * @param {number} gridRes The resolution of the mesh grid, |
| 572 * expressed in the number of quads across and down. |
| 573 * @param {number} opt_positionLocation The attrib location for position. |
| 574 */ |
| 575 var setupIndexedQuad = function ( |
| 576 gl, gridRes, opt_positionLocation, opt_flipOddTriangles) { |
| 577 return setupIndexedQuadWithOptions(gl, |
| 578 { gridRes: gridRes, |
| 579 positionLocation: opt_positionLocation, |
| 580 flipOddTriangles: opt_flipOddTriangles |
| 581 }); |
| 582 }; |
| 583 |
| 584 /** |
| 585 * Creates a quad with various options. |
| 586 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. |
| 587 * @param {!Object) options The options. See below. |
| 588 * @return {!Array.<WebGLBuffer>} The created buffers. |
| 589 * [positions, <colors>, indices] |
| 590 * |
| 591 * Options: |
| 592 * gridRes: number of quads across and down grid. |
| 593 * positionLocation: attrib location for position |
| 594 * flipOddTriangles: reverse order of vertices of every other |
| 595 * triangle |
| 596 * positionOffset: offset added to each vertex |
| 597 * positionMult: multipier for each vertex |
| 598 * colorLocation: attrib location for vertex colors. If |
| 599 * undefined no vertex colors will be created. |
| 600 */ |
| 601 var setupIndexedQuadWithOptions = function (gl, options) { |
| 602 var positionLocation = options.positionLocation || 0; |
| 603 var objects = []; |
| 604 |
| 605 var gridRes = options.gridRes || 1; |
| 606 var positionOffset = options.positionOffset || 0; |
| 607 var positionMult = options.positionMult || 1; |
| 608 var vertsAcross = gridRes + 1; |
| 609 var numVerts = vertsAcross * vertsAcross; |
| 610 var positions = new Float32Array(numVerts * 3); |
| 611 var indices = new Uint16Array(6 * gridRes * gridRes); |
| 612 var poffset = 0; |
| 613 |
| 614 for (var yy = 0; yy <= gridRes; ++yy) { |
| 615 for (var xx = 0; xx <= gridRes; ++xx) { |
| 616 positions[poffset + 0] = (-1 + 2 * xx / gridRes) * positionMult + position
Offset; |
| 617 positions[poffset + 1] = (-1 + 2 * yy / gridRes) * positionMult + position
Offset; |
| 618 positions[poffset + 2] = 0; |
| 619 |
| 620 poffset += 3; |
| 621 } |
| 622 } |
| 623 |
| 624 var buf = gl.createBuffer(); |
| 625 gl.bindBuffer(gl.ARRAY_BUFFER, buf); |
| 626 gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW); |
| 627 gl.enableVertexAttribArray(positionLocation); |
| 628 gl.vertexAttribPointer(positionLocation, 3, gl.FLOAT, false, 0, 0); |
| 629 objects.push(buf); |
| 630 |
| 631 if (options.colorLocation !== undefined) { |
| 632 var colors = new Float32Array(numVerts * 4); |
| 633 for (var yy = 0; yy <= gridRes; ++yy) { |
| 634 for (var xx = 0; xx <= gridRes; ++xx) { |
| 635 if (options.color !== undefined) { |
| 636 colors[poffset + 0] = options.color[0]; |
| 637 colors[poffset + 1] = options.color[1]; |
| 638 colors[poffset + 2] = options.color[2]; |
| 639 colors[poffset + 3] = options.color[3]; |
| 640 } else { |
| 641 colors[poffset + 0] = xx / gridRes; |
| 642 colors[poffset + 1] = yy / gridRes; |
| 643 colors[poffset + 2] = (xx / gridRes) * (yy / gridRes); |
| 644 colors[poffset + 3] = (yy % 2) * 0.5 + 0.5; |
| 645 } |
| 646 poffset += 4; |
| 647 } |
| 648 } |
| 649 |
| 650 var buf = gl.createBuffer(); |
| 651 gl.bindBuffer(gl.ARRAY_BUFFER, buf); |
| 652 gl.bufferData(gl.ARRAY_BUFFER, colors, gl.STATIC_DRAW); |
| 653 gl.enableVertexAttribArray(options.colorLocation); |
| 654 gl.vertexAttribPointer(options.colorLocation, 4, gl.FLOAT, false, 0, 0); |
| 655 objects.push(buf); |
| 656 } |
| 657 |
| 658 var tbase = 0; |
| 659 for (var yy = 0; yy < gridRes; ++yy) { |
| 660 var index = yy * vertsAcross; |
| 661 for (var xx = 0; xx < gridRes; ++xx) { |
| 662 indices[tbase + 0] = index + 0; |
| 663 indices[tbase + 1] = index + 1; |
| 664 indices[tbase + 2] = index + vertsAcross; |
| 665 indices[tbase + 3] = index + vertsAcross; |
| 666 indices[tbase + 4] = index + 1; |
| 667 indices[tbase + 5] = index + vertsAcross + 1; |
| 668 |
| 669 if (options.flipOddTriangles) { |
| 670 indices[tbase + 4] = index + vertsAcross + 1; |
| 671 indices[tbase + 5] = index + 1; |
| 672 } |
| 673 |
| 674 index += 1; |
| 675 tbase += 6; |
| 676 } |
| 677 } |
| 678 |
| 679 var buf = gl.createBuffer(); |
| 680 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buf); |
| 681 gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW); |
| 682 objects.push(buf); |
| 683 |
| 684 return objects; |
| 685 }; |
| 686 |
| 687 /** |
| 688 * Returns the constructor for an ArrayBuffer that |
| 689 * corresponds to the given WebGL type. |
| 690 * @param {!WebGLRenderingContext} gl A WebGLRenderingContext. |
| 691 * @param {number} type The WebGL type (eg, gl.UNSIGNED_BYTE) |
| 692 * @return {!Constructor} The ArrayBuffer constructor that |
| 693 * corresponds to the given type. |
| 694 */ |
| 695 var glTypeToArrayBufferType = function(gl, type) { |
| 696 switch (type) { |
| 697 case gl.BYTE: |
| 698 return window.Int8Array; |
| 699 case gl.UNSIGNED_BYTE: |
| 700 return window.Uint8Array; |
| 701 case gl.SHORT: |
| 702 return window.Int16Array; |
| 703 case gl.UNSIGNED_SHORT: |
| 704 case gl.UNSIGNED_SHORT_5_6_5: |
| 705 case gl.UNSIGNED_SHORT_4_4_4_4: |
| 706 case gl.UNSIGNED_SHORT_5_5_5_1: |
| 707 return window.Uint16Array; |
| 708 case gl.INT: |
| 709 return window.Int32Array; |
| 710 case gl.UNSIGNED_INT: |
| 711 return window.Uint32Array; |
| 712 default: |
| 713 throw 'unknown gl type ' + glEnumToString(gl, type); |
| 714 } |
| 715 }; |
| 716 |
| 717 /** |
| 718 * Returns the number of bytes per component for a given WebGL |
| 719 * type. |
| 720 * @param {!WebGLRenderingContext} gl A WebGLRenderingContext. |
| 721 * @param {number} type The WebGL type (eg, gl.UNSIGNED_BYTE) |
| 722 * @return {!Constructor} The ArrayBuffer constructor that |
| 723 * corresponds to the given type. |
| 724 */ |
| 725 var getBytesPerComponent = function(gl, type) { |
| 726 switch (type) { |
| 727 case gl.BYTE: |
| 728 case gl.UNSIGNED_BYTE: |
| 729 return 1; |
| 730 case gl.SHORT: |
| 731 case gl.UNSIGNED_SHORT: |
| 732 case gl.UNSIGNED_SHORT_5_6_5: |
| 733 case gl.UNSIGNED_SHORT_4_4_4_4: |
| 734 case gl.UNSIGNED_SHORT_5_5_5_1: |
| 735 return 2; |
| 736 case gl.INT: |
| 737 case gl.UNSIGNED_INT: |
| 738 return 4; |
| 739 default: |
| 740 throw 'unknown gl type ' + glEnumToString(gl, type); |
| 741 } |
| 742 }; |
| 743 |
| 744 /** |
| 745 * Fills the given texture with a solid color. |
| 746 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. |
| 747 * @param {!WebGLTexture} tex The texture to fill. |
| 748 * @param {number} width The width of the texture to create. |
| 749 * @param {number} height The height of the texture to create. |
| 750 * @param {!Array.<number>} color The color to fill with. |
| 751 * where each element is in the range 0 to 255. |
| 752 * @param {number} opt_level The level of the texture to fill. Default = 0. |
| 753 * @param {number} opt_format The format for the texture. |
| 754 */ |
| 755 var fillTexture = function(gl, tex, width, height, color, opt_level, opt_format,
opt_type) { |
| 756 opt_level = opt_level || 0; |
| 757 opt_format = opt_format || gl.RGBA; |
| 758 opt_type = opt_type || gl.UNSIGNED_BYTE; |
| 759 var pack = gl.getParameter(gl.UNPACK_ALIGNMENT); |
| 760 var numComponents = color.length; |
| 761 var bytesPerComponent = getBytesPerComponent(gl, opt_type); |
| 762 var rowSize = numComponents * width * bytesPerComponent; |
| 763 var paddedRowSize = Math.floor((rowSize + pack - 1) / pack) * pack; |
| 764 var size = rowSize + (height - 1) * paddedRowSize; |
| 765 size = Math.floor((size + bytesPerComponent - 1) / bytesPerComponent) * bytesP
erComponent; |
| 766 var buf = new (glTypeToArrayBufferType(gl, opt_type))(size); |
| 767 for (var yy = 0; yy < height; ++yy) { |
| 768 var off = yy * paddedRowSize; |
| 769 for (var xx = 0; xx < width; ++xx) { |
| 770 for (var jj = 0; jj < numComponents; ++jj) { |
| 771 buf[off++] = color[jj]; |
| 772 } |
| 773 } |
| 774 } |
| 775 gl.bindTexture(gl.TEXTURE_2D, tex); |
| 776 gl.texImage2D( |
| 777 gl.TEXTURE_2D, opt_level, opt_format, width, height, 0, |
| 778 opt_format, opt_type, buf); |
| 779 }; |
| 780 |
| 781 /** |
| 782 * Creates a texture and fills it with a solid color. |
| 783 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. |
| 784 * @param {number} width The width of the texture to create. |
| 785 * @param {number} height The height of the texture to create. |
| 786 * @param {!Array.<number>} color The color to fill with. A 4 element array |
| 787 * where each element is in the range 0 to 255. |
| 788 * @return {!WebGLTexture} |
| 789 */ |
| 790 var createColoredTexture = function(gl, width, height, color) { |
| 791 var tex = gl.createTexture(); |
| 792 fillTexture(gl, tex, width, height, color); |
| 793 return tex; |
| 794 }; |
| 795 |
| 796 var ubyteToFloat = function(c) { |
| 797 return c / 255; |
| 798 }; |
| 799 |
| 800 var ubyteColorToFloatColor = function(color) { |
| 801 var floatColor = []; |
| 802 for (var ii = 0; ii < color.length; ++ii) { |
| 803 floatColor[ii] = ubyteToFloat(color[ii]); |
| 804 } |
| 805 return floatColor; |
| 806 }; |
| 807 |
| 808 /** |
| 809 * Sets the "u_color" uniform of the current program to color. |
| 810 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. |
| 811 * @param {!Array.<number> color 4 element array of 0-1 color |
| 812 * components. |
| 813 */ |
| 814 var setFloatDrawColor = function(gl, color) { |
| 815 var program = gl.getParameter(gl.CURRENT_PROGRAM); |
| 816 var colorLocation = gl.getUniformLocation(program, "u_color"); |
| 817 gl.uniform4fv(colorLocation, color); |
| 818 }; |
| 819 |
| 820 /** |
| 821 * Sets the "u_color" uniform of the current program to color. |
| 822 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. |
| 823 * @param {!Array.<number> color 4 element array of 0-255 color |
| 824 * components. |
| 825 */ |
| 826 var setUByteDrawColor = function(gl, color) { |
| 827 setFloatDrawColor(gl, ubyteColorToFloatColor(color)); |
| 828 }; |
| 829 |
| 830 /** |
| 831 * Draws a previously setup quad in the given color. |
| 832 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. |
| 833 * @param {!Array.<number>} color The color to draw with. A 4 |
| 834 * element array where each element is in the range 0 to |
| 835 * 1. |
| 836 */ |
| 837 var drawFloatColorQuad = function(gl, color) { |
| 838 var program = gl.getParameter(gl.CURRENT_PROGRAM); |
| 839 var colorLocation = gl.getUniformLocation(program, "u_color"); |
| 840 gl.uniform4fv(colorLocation, color); |
| 841 gl.drawArrays(gl.TRIANGLES, 0, 6); |
| 842 }; |
| 843 |
| 844 |
| 845 /** |
| 846 * Draws a previously setup quad in the given color. |
| 847 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. |
| 848 * @param {!Array.<number>} color The color to draw with. A 4 |
| 849 * element array where each element is in the range 0 to |
| 850 * 255. |
| 851 */ |
| 852 var drawUByteColorQuad = function(gl, color) { |
| 853 drawFloatColorQuad(gl, ubyteColorToFloatColor(color)); |
| 854 }; |
| 855 |
| 856 /** |
| 857 * Draws a previously setupUnitQuad. |
| 858 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. |
| 859 */ |
| 860 var drawUnitQuad = function(gl) { |
| 861 gl.drawArrays(gl.TRIANGLES, 0, 6); |
| 862 }; |
| 863 |
| 864 /** |
| 865 * Clears then Draws a previously setupUnitQuad. |
| 866 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. |
| 867 * @param {!Array.<number>} opt_color The color to fill clear with before |
| 868 * drawing. A 4 element array where each element is in the range 0 to |
| 869 * 255. Default [255, 255, 255, 255] |
| 870 */ |
| 871 var clearAndDrawUnitQuad = function(gl, opt_color) { |
| 872 opt_color = opt_color || [255, 255, 255, 255]; |
| 873 gl.clearColor( |
| 874 opt_color[0] / 255, |
| 875 opt_color[1] / 255, |
| 876 opt_color[2] / 255, |
| 877 opt_color[3] / 255); |
| 878 gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); |
| 879 drawUnitQuad(gl); |
| 880 }; |
| 881 |
| 882 /** |
| 883 * Draws a quad previously setup with setupIndexedQuad. |
| 884 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. |
| 885 * @param {number} gridRes Resolution of grid. |
| 886 */ |
| 887 var drawIndexedQuad = function(gl, gridRes) { |
| 888 gl.drawElements(gl.TRIANGLES, gridRes * gridRes * 6, gl.UNSIGNED_SHORT, 0); |
| 889 }; |
| 890 |
| 891 /** |
| 892 * Draws a previously setupIndexedQuad |
| 893 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. |
| 894 * @param {number} gridRes Resolution of grid. |
| 895 * @param {!Array.<number>} opt_color The color to fill clear with before |
| 896 * drawing. A 4 element array where each element is in the range 0 to |
| 897 * 255. Default [255, 255, 255, 255] |
| 898 */ |
| 899 var clearAndDrawIndexedQuad = function(gl, gridRes, opt_color) { |
| 900 opt_color = opt_color || [255, 255, 255, 255]; |
| 901 gl.clearColor( |
| 902 opt_color[0] / 255, |
| 903 opt_color[1] / 255, |
| 904 opt_color[2] / 255, |
| 905 opt_color[3] / 255); |
| 906 gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); |
| 907 drawIndexedQuad(gl, gridRes); |
| 908 }; |
| 909 |
| 910 /** |
| 911 * Clips a range to min, max |
| 912 * (Eg. clipToRange(-5,7,0,20) would return {value:0,extent:2} |
| 913 * @param {number} value start of range |
| 914 * @param {number} extent extent of range |
| 915 * @param {number} min min. |
| 916 * @param {number} max max. |
| 917 * @return {!{value:number,extent:number} The clipped value. |
| 918 */ |
| 919 var clipToRange = function(value, extent, min, max) { |
| 920 if (value < min) { |
| 921 extent -= min - value; |
| 922 value = min; |
| 923 } |
| 924 var end = value + extent; |
| 925 if (end > max) { |
| 926 extent -= end - max; |
| 927 } |
| 928 if (extent < 0) { |
| 929 value = max; |
| 930 extent = 0; |
| 931 } |
| 932 return {value:value, extent: extent}; |
| 933 }; |
| 934 |
| 935 /** |
| 936 * Checks that a portion of a canvas is 1 color. |
| 937 * @param {!WebGLRenderingContext|CanvasRenderingContext2D} gl The |
| 938 * WebGLRenderingContext or 2D context to use. |
| 939 * @param {number} x left corner of region to check. |
| 940 * @param {number} y bottom corner of region to check in case of checking from |
| 941 * a GL context or top corner in case of checking from a 2D context. |
| 942 * @param {number} width width of region to check. |
| 943 * @param {number} height width of region to check. |
| 944 * @param {!Array.<number>} color The color expected. A 4 element array where |
| 945 * each element is in the range 0 to 255. |
| 946 * @param {number} opt_errorRange Optional. Acceptable error in |
| 947 * color checking. 0 by default. |
| 948 * @param {!function()} sameFn Function to call if all pixels |
| 949 * are the same as color. |
| 950 * @param {!function()} differentFn Function to call if a pixel |
| 951 * is different than color |
| 952 * @param {!function()} logFn Function to call for logging. |
| 953 */ |
| 954 var checkCanvasRectColor = function(gl, x, y, width, height, color, opt_errorRan
ge, sameFn, differentFn, logFn) { |
| 955 if ((gl instanceof WebGLRenderingContext) && !gl.getParameter(gl.FRAMEBUFFER_B
INDING)) { |
| 956 // We're reading the backbuffer so clip. |
| 957 var xr = clipToRange(x, width, 0, gl.canvas.width); |
| 958 var yr = clipToRange(y, height, 0, gl.canvas.height); |
| 959 if (!xr.extent || !yr.extent) { |
| 960 logFn("checking rect: effective width or heigh is zero"); |
| 961 sameFn(); |
| 962 return; |
| 963 } |
| 964 x = xr.value; |
| 965 y = yr.value; |
| 966 width = xr.extent; |
| 967 height = yr.extent; |
| 968 } |
| 969 var errorRange = opt_errorRange || 0; |
| 970 if (!errorRange.length) { |
| 971 errorRange = [errorRange, errorRange, errorRange, errorRange] |
| 972 } |
| 973 var buf; |
| 974 if (gl instanceof WebGLRenderingContext) { |
| 975 buf = new Uint8Array(width * height * 4); |
| 976 gl.readPixels(x, y, width, height, gl.RGBA, gl.UNSIGNED_BYTE, buf); |
| 977 } else { |
| 978 buf = gl.getImageData(x, y, width, height).data; |
| 979 } |
| 980 for (var i = 0; i < width * height; ++i) { |
| 981 var offset = i * 4; |
| 982 for (var j = 0; j < color.length; ++j) { |
| 983 if (Math.abs(buf[offset + j] - color[j]) > errorRange[j]) { |
| 984 differentFn(); |
| 985 var was = buf[offset + 0].toString(); |
| 986 for (j = 1; j < color.length; ++j) { |
| 987 was += "," + buf[offset + j]; |
| 988 } |
| 989 logFn('at (' + (x + (i % width)) + ', ' + (y + Math.floor(i / width)) + |
| 990 ') expected: ' + color + ' was ' + was); |
| 991 return; |
| 992 } |
| 993 } |
| 994 } |
| 995 sameFn(); |
| 996 }; |
| 997 |
| 998 /** |
| 999 * Checks that a portion of a canvas is 1 color. |
| 1000 * @param {!WebGLRenderingContext|CanvasRenderingContext2D} gl The |
| 1001 * WebGLRenderingContext or 2D context to use. |
| 1002 * @param {number} x left corner of region to check. |
| 1003 * @param {number} y bottom corner of region to check in case of checking from |
| 1004 * a GL context or top corner in case of checking from a 2D context. |
| 1005 * @param {number} width width of region to check. |
| 1006 * @param {number} height width of region to check. |
| 1007 * @param {!Array.<number>} color The color expected. A 4 element array where |
| 1008 * each element is in the range 0 to 255. |
| 1009 * @param {string} opt_msg Message to associate with success. Eg |
| 1010 * ("should be red"). |
| 1011 * @param {number} opt_errorRange Optional. Acceptable error in |
| 1012 * color checking. 0 by default. |
| 1013 */ |
| 1014 var checkCanvasRect = function(gl, x, y, width, height, color, opt_msg, opt_erro
rRange) { |
| 1015 var msg = opt_msg; |
| 1016 if (msg === undefined) { |
| 1017 msg = "should be " + color.toString(); |
| 1018 } |
| 1019 checkCanvasRectColor( |
| 1020 gl, x, y, width, height, color, opt_errorRange, |
| 1021 function() { |
| 1022 testPassed(msg); |
| 1023 }, |
| 1024 function() { |
| 1025 testFailed(msg); |
| 1026 }, |
| 1027 debug); |
| 1028 }; |
| 1029 |
| 1030 /** |
| 1031 * Checks that an entire canvas is 1 color. |
| 1032 * @param {!WebGLRenderingContext|CanvasRenderingContext2D} gl The |
| 1033 * WebGLRenderingContext or 2D context to use. |
| 1034 * @param {!Array.<number>} color The color expected. A 4 element array where |
| 1035 * each element is in the range 0 to 255. |
| 1036 * @param {string} msg Message to associate with success. Eg ("should be red"). |
| 1037 * @param {number} errorRange Optional. Acceptable error in |
| 1038 * color checking. 0 by default. |
| 1039 */ |
| 1040 var checkCanvas = function(gl, color, msg, errorRange) { |
| 1041 checkCanvasRect(gl, 0, 0, gl.canvas.width, gl.canvas.height, color, msg, error
Range); |
| 1042 }; |
| 1043 |
| 1044 /** |
| 1045 * Checks a rectangular area both inside the area and outside |
| 1046 * the area. |
| 1047 * @param {!WebGLRenderingContext|CanvasRenderingContext2D} gl The |
| 1048 * WebGLRenderingContext or 2D context to use. |
| 1049 * @param {number} x left corner of region to check. |
| 1050 * @param {number} y bottom corner of region to check in case of checking from |
| 1051 * a GL context or top corner in case of checking from a 2D context. |
| 1052 * @param {number} width width of region to check. |
| 1053 * @param {number} height width of region to check. |
| 1054 * @param {!Array.<number>} innerColor The color expected inside |
| 1055 * the area. A 4 element array where each element is in the |
| 1056 * range 0 to 255. |
| 1057 * @param {!Array.<number>} outerColor The color expected |
| 1058 * outside. A 4 element array where each element is in the |
| 1059 * range 0 to 255. |
| 1060 * @param {!number} opt_edgeSize: The number of pixels to skip |
| 1061 * around the edges of the area. Defaut 0. |
| 1062 * @param {!{width:number, height:number}} opt_outerDimensions |
| 1063 * The outer dimensions. Default the size of gl.canvas. |
| 1064 */ |
| 1065 var checkAreaInAndOut = function(gl, x, y, width, height, innerColor, outerColor
, opt_edgeSize, opt_outerDimensions) { |
| 1066 var outerDimensions = opt_outerDimensions || { width: gl.canvas.width, height:
gl.canvas.height }; |
| 1067 var edgeSize = opt_edgeSize || 0; |
| 1068 checkCanvasRect(gl, x + edgeSize, y + edgeSize, width - edgeSize * 2, height -
edgeSize * 2, innerColor); |
| 1069 checkCanvasRect(gl, 0, 0, x - edgeSize, outerDimensions.height, outerColor); |
| 1070 checkCanvasRect(gl, x + width + edgeSize, 0, outerDimensions.width - x - width
- edgeSize, outerDimensions.height, outerColor); |
| 1071 checkCanvasRect(gl, 0, 0, outerDimensions.width, y - edgeSize, outerColor); |
| 1072 checkCanvasRect(gl, 0, y + height + edgeSize, outerDimensions.width, outerDime
nsions.height - y - height - edgeSize, outerColor); |
| 1073 }; |
| 1074 |
| 1075 /** |
| 1076 * Loads a texture, calls callback when finished. |
| 1077 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. |
| 1078 * @param {string} url URL of image to load |
| 1079 * @param {function(!Image): void} callback Function that gets called after |
| 1080 * image has loaded |
| 1081 * @return {!WebGLTexture} The created texture. |
| 1082 */ |
| 1083 var loadTexture = function(gl, url, callback) { |
| 1084 var texture = gl.createTexture(); |
| 1085 gl.bindTexture(gl.TEXTURE_2D, texture); |
| 1086 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); |
| 1087 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); |
| 1088 var image = new Image(); |
| 1089 image.onload = function() { |
| 1090 gl.bindTexture(gl.TEXTURE_2D, texture); |
| 1091 gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true); |
| 1092 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, imag
e); |
| 1093 callback(image); |
| 1094 }; |
| 1095 image.src = url; |
| 1096 return texture; |
| 1097 }; |
| 1098 |
| 1099 /** |
| 1100 * Makes a shallow copy of an object. |
| 1101 * @param {!Object) src Object to copy |
| 1102 * @return {!Object} The copy of src. |
| 1103 */ |
| 1104 var shallowCopyObject = function(src) { |
| 1105 var dst = {}; |
| 1106 for (var attr in src) { |
| 1107 if (src.hasOwnProperty(attr)) { |
| 1108 dst[attr] = src[attr]; |
| 1109 } |
| 1110 } |
| 1111 return dst; |
| 1112 }; |
| 1113 |
| 1114 /** |
| 1115 * Checks if an attribute exists on an object case insensitive. |
| 1116 * @param {!Object) obj Object to check |
| 1117 * @param {string} attr Name of attribute to look for. |
| 1118 * @return {string?} The name of the attribute if it exists, |
| 1119 * undefined if not. |
| 1120 */ |
| 1121 var hasAttributeCaseInsensitive = function(obj, attr) { |
| 1122 var lower = attr.toLowerCase(); |
| 1123 for (var key in obj) { |
| 1124 if (obj.hasOwnProperty(key) && key.toLowerCase() == lower) { |
| 1125 return key; |
| 1126 } |
| 1127 } |
| 1128 }; |
| 1129 |
| 1130 /** |
| 1131 * Creates a webgl context. |
| 1132 * @param {!Canvas|string} opt_canvas The canvas tag to get |
| 1133 * context from. If one is not passed in one will be |
| 1134 * created. If it's a string it's assumed to be the id of a |
| 1135 * canvas. |
| 1136 * @param {Object} opt_attributes Context attributes. |
| 1137 * @return {!WebGLRenderingContext} The created context. |
| 1138 */ |
| 1139 var create3DContext = function(opt_canvas, opt_attributes) { |
| 1140 if (window.initTestingHarness) { |
| 1141 window.initTestingHarness(); |
| 1142 } |
| 1143 var attributes = shallowCopyObject(opt_attributes || {}); |
| 1144 if (!hasAttributeCaseInsensitive(attributes, "antialias")) { |
| 1145 attributes.antialias = false; |
| 1146 } |
| 1147 |
| 1148 opt_canvas = opt_canvas || document.createElement("canvas"); |
| 1149 if (typeof opt_canvas == 'string') { |
| 1150 opt_canvas = document.getElementById(opt_canvas); |
| 1151 } |
| 1152 var context = null; |
| 1153 var names = ["webgl", "experimental-webgl"]; |
| 1154 for (var i = 0; i < names.length; ++i) { |
| 1155 try { |
| 1156 context = opt_canvas.getContext(names[i], attributes); |
| 1157 } catch (e) { |
| 1158 } |
| 1159 if (context) { |
| 1160 break; |
| 1161 } |
| 1162 } |
| 1163 if (!context) { |
| 1164 testFailed("Unable to fetch WebGL rendering context for Canvas"); |
| 1165 } |
| 1166 return context; |
| 1167 } |
| 1168 |
| 1169 /** |
| 1170 * Gets a GLError value as a string. |
| 1171 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. |
| 1172 * @param {number} err The webgl error as retrieved from gl.getError(). |
| 1173 * @return {string} the error as a string. |
| 1174 */ |
| 1175 var getGLErrorAsString = function(gl, err) { |
| 1176 if (err === gl.NO_ERROR) { |
| 1177 return "NO_ERROR"; |
| 1178 } |
| 1179 for (var name in gl) { |
| 1180 if (gl[name] === err) { |
| 1181 return name; |
| 1182 } |
| 1183 } |
| 1184 return err.toString(); |
| 1185 }; |
| 1186 |
| 1187 /** |
| 1188 * Wraps a WebGL function with a function that throws an exception if there is |
| 1189 * an error. |
| 1190 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. |
| 1191 * @param {string} fname Name of function to wrap. |
| 1192 * @return {function} The wrapped function. |
| 1193 */ |
| 1194 var createGLErrorWrapper = function(context, fname) { |
| 1195 return function() { |
| 1196 var rv = context[fname].apply(context, arguments); |
| 1197 var err = context.getError(); |
| 1198 if (err != context.NO_ERROR) |
| 1199 throw "GL error " + getGLErrorAsString(context, err) + " in " + fname; |
| 1200 return rv; |
| 1201 }; |
| 1202 }; |
| 1203 |
| 1204 /** |
| 1205 * Creates a WebGL context where all functions are wrapped to throw an exception |
| 1206 * if there is an error. |
| 1207 * @param {!Canvas} canvas The HTML canvas to get a context from. |
| 1208 * @return {!Object} The wrapped context. |
| 1209 */ |
| 1210 function create3DContextWithWrapperThatThrowsOnGLError(canvas) { |
| 1211 var context = create3DContext(canvas); |
| 1212 var wrap = {}; |
| 1213 for (var i in context) { |
| 1214 try { |
| 1215 if (typeof context[i] == 'function') { |
| 1216 wrap[i] = createGLErrorWrapper(context, i); |
| 1217 } else { |
| 1218 wrap[i] = context[i]; |
| 1219 } |
| 1220 } catch (e) { |
| 1221 error("createContextWrapperThatThrowsOnGLError: Error accessing " + i); |
| 1222 } |
| 1223 } |
| 1224 wrap.getError = function() { |
| 1225 return context.getError(); |
| 1226 }; |
| 1227 return wrap; |
| 1228 }; |
| 1229 |
| 1230 /** |
| 1231 * Tests that an evaluated expression generates a specific GL error. |
| 1232 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. |
| 1233 * @param {number} glError The expected gl error. |
| 1234 * @param {string} evalSTr The string to evaluate. |
| 1235 */ |
| 1236 var shouldGenerateGLError = function(gl, glError, evalStr) { |
| 1237 var exception; |
| 1238 try { |
| 1239 eval(evalStr); |
| 1240 } catch (e) { |
| 1241 exception = e; |
| 1242 } |
| 1243 if (exception) { |
| 1244 testFailed(evalStr + " threw exception " + exception); |
| 1245 } else { |
| 1246 var err = gl.getError(); |
| 1247 if (err != glError) { |
| 1248 testFailed(evalStr + " expected: " + getGLErrorAsString(gl, glError) + ".
Was " + getGLErrorAsString(gl, err) + "."); |
| 1249 } else { |
| 1250 testPassed(evalStr + " was expected value: " + getGLErrorAsString(gl, glEr
ror) + "."); |
| 1251 } |
| 1252 } |
| 1253 }; |
| 1254 |
| 1255 /** |
| 1256 * Tests that the first error GL returns is the specified error. |
| 1257 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. |
| 1258 * @param {number} glError The expected gl error. |
| 1259 * @param {string} opt_msg |
| 1260 */ |
| 1261 var glErrorShouldBe = function(gl, glError, opt_msg) { |
| 1262 opt_msg = opt_msg || ""; |
| 1263 var err = gl.getError(); |
| 1264 if (err != glError) { |
| 1265 testFailed("getError expected: " + getGLErrorAsString(gl, glError) + |
| 1266 ". Was " + getGLErrorAsString(gl, err) + " : " + opt_msg); |
| 1267 } else { |
| 1268 testPassed("getError was expected value: " + |
| 1269 getGLErrorAsString(gl, glError) + " : " + opt_msg); |
| 1270 } |
| 1271 }; |
| 1272 |
| 1273 /** |
| 1274 * Links a WebGL program, throws if there are errors. |
| 1275 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. |
| 1276 * @param {!WebGLProgram} program The WebGLProgram to link. |
| 1277 * @param {function(string): void) opt_errorCallback callback for errors. |
| 1278 */ |
| 1279 var linkProgram = function(gl, program, opt_errorCallback) { |
| 1280 var errFn = opt_errorCallback || testFailed; |
| 1281 // Link the program |
| 1282 gl.linkProgram(program); |
| 1283 |
| 1284 // Check the link status |
| 1285 var linked = gl.getProgramParameter(program, gl.LINK_STATUS); |
| 1286 if (!linked) { |
| 1287 // something went wrong with the link |
| 1288 var error = gl.getProgramInfoLog (program); |
| 1289 |
| 1290 errFn("Error in program linking:" + error); |
| 1291 |
| 1292 gl.deleteProgram(program); |
| 1293 } |
| 1294 }; |
| 1295 |
| 1296 /** |
| 1297 * Loads text from an external file. This function is synchronous. |
| 1298 * @param {string} url The url of the external file. |
| 1299 * @param {!function(bool, string): void} callback that is sent a bool for |
| 1300 * success and the string. |
| 1301 */ |
| 1302 var loadTextFileAsync = function(url, callback) { |
| 1303 log ("loading: " + url); |
| 1304 var error = 'loadTextFileSynchronous failed to load url "' + url + '"'; |
| 1305 var request; |
| 1306 if (window.XMLHttpRequest) { |
| 1307 request = new XMLHttpRequest(); |
| 1308 if (request.overrideMimeType) { |
| 1309 request.overrideMimeType('text/plain'); |
| 1310 } |
| 1311 } else { |
| 1312 throw 'XMLHttpRequest is disabled'; |
| 1313 } |
| 1314 try { |
| 1315 request.open('GET', url, true); |
| 1316 request.onreadystatechange = function() { |
| 1317 if (request.readyState == 4) { |
| 1318 var text = ''; |
| 1319 // HTTP reports success with a 200 status. The file protocol reports |
| 1320 // success with zero. HTTP does not use zero as a status code (they |
| 1321 // start at 100). |
| 1322 // https://developer.mozilla.org/En/Using_XMLHttpRequest |
| 1323 var success = request.status == 200 || request.status == 0; |
| 1324 if (success) { |
| 1325 text = request.responseText; |
| 1326 } |
| 1327 log("loaded: " + url); |
| 1328 callback(success, text); |
| 1329 } |
| 1330 }; |
| 1331 request.send(null); |
| 1332 } catch (e) { |
| 1333 log("failed to load: " + url); |
| 1334 callback(false, ''); |
| 1335 } |
| 1336 }; |
| 1337 |
| 1338 /** |
| 1339 * Recursively loads a file as a list. Each line is parsed for a relative |
| 1340 * path. If the file ends in .txt the contents of that file is inserted in |
| 1341 * the list. |
| 1342 * |
| 1343 * @param {string} url The url of the external file. |
| 1344 * @param {!function(bool, Array<string>): void} callback that is sent a bool |
| 1345 * for success and the array of strings. |
| 1346 */ |
| 1347 var getFileListAsync = function(url, callback) { |
| 1348 var files = []; |
| 1349 |
| 1350 var getFileListImpl = function(url, callback) { |
| 1351 var files = []; |
| 1352 if (url.substr(url.length - 4) == '.txt') { |
| 1353 loadTextFileAsync(url, function() { |
| 1354 return function(success, text) { |
| 1355 if (!success) { |
| 1356 callback(false, ''); |
| 1357 return; |
| 1358 } |
| 1359 var lines = text.split('\n'); |
| 1360 var prefix = ''; |
| 1361 var lastSlash = url.lastIndexOf('/'); |
| 1362 if (lastSlash >= 0) { |
| 1363 prefix = url.substr(0, lastSlash + 1); |
| 1364 } |
| 1365 var fail = false; |
| 1366 var count = 1; |
| 1367 var index = 0; |
| 1368 for (var ii = 0; ii < lines.length; ++ii) { |
| 1369 var str = lines[ii].replace(/^\s\s*/, '').replace(/\s\s*$/, ''); |
| 1370 if (str.length > 4 && |
| 1371 str[0] != '#' && |
| 1372 str[0] != ";" && |
| 1373 str.substr(0, 2) != "//") { |
| 1374 var names = str.split(/ +/); |
| 1375 new_url = prefix + str; |
| 1376 if (names.length == 1) { |
| 1377 new_url = prefix + str; |
| 1378 ++count; |
| 1379 getFileListImpl(new_url, function(index) { |
| 1380 return function(success, new_files) { |
| 1381 log("got files: " + new_files.length); |
| 1382 if (success) { |
| 1383 files[index] = new_files; |
| 1384 } |
| 1385 finish(success); |
| 1386 }; |
| 1387 }(index++)); |
| 1388 } else { |
| 1389 var s = ""; |
| 1390 var p = ""; |
| 1391 for (var jj = 0; jj < names.length; ++jj) { |
| 1392 s += p + prefix + names[jj]; |
| 1393 p = " "; |
| 1394 } |
| 1395 files[index++] = s; |
| 1396 } |
| 1397 } |
| 1398 } |
| 1399 finish(true); |
| 1400 |
| 1401 function finish(success) { |
| 1402 if (!success) { |
| 1403 fail = true; |
| 1404 } |
| 1405 --count; |
| 1406 log("count: " + count); |
| 1407 if (!count) { |
| 1408 callback(!fail, files); |
| 1409 } |
| 1410 } |
| 1411 } |
| 1412 }()); |
| 1413 |
| 1414 } else { |
| 1415 files.push(url); |
| 1416 callback(true, files); |
| 1417 } |
| 1418 }; |
| 1419 |
| 1420 getFileListImpl(url, function(success, files) { |
| 1421 // flatten |
| 1422 var flat = []; |
| 1423 flatten(files); |
| 1424 function flatten(files) { |
| 1425 for (var ii = 0; ii < files.length; ++ii) { |
| 1426 var value = files[ii]; |
| 1427 if (typeof(value) == "string") { |
| 1428 flat.push(value); |
| 1429 } else { |
| 1430 flatten(value); |
| 1431 } |
| 1432 } |
| 1433 } |
| 1434 callback(success, flat); |
| 1435 }); |
| 1436 }; |
| 1437 |
| 1438 /** |
| 1439 * Gets a file from a file/URL. |
| 1440 * @param {string} file the URL of the file to get. |
| 1441 * @return {string} The contents of the file. |
| 1442 */ |
| 1443 var readFile = function(file) { |
| 1444 var xhr = new XMLHttpRequest(); |
| 1445 xhr.open("GET", file, false); |
| 1446 xhr.send(); |
| 1447 return xhr.responseText.replace(/\r/g, ""); |
| 1448 }; |
| 1449 |
| 1450 var readFileList = function(url) { |
| 1451 var files = []; |
| 1452 if (url.substr(url.length - 4) == '.txt') { |
| 1453 var lines = readFile(url).split('\n'); |
| 1454 var prefix = ''; |
| 1455 var lastSlash = url.lastIndexOf('/'); |
| 1456 if (lastSlash >= 0) { |
| 1457 prefix = url.substr(0, lastSlash + 1); |
| 1458 } |
| 1459 for (var ii = 0; ii < lines.length; ++ii) { |
| 1460 var str = lines[ii].replace(/^\s\s*/, '').replace(/\s\s*$/, ''); |
| 1461 if (str.length > 4 && |
| 1462 str[0] != '#' && |
| 1463 str[0] != ";" && |
| 1464 str.substr(0, 2) != "//") { |
| 1465 var names = str.split(/ +/); |
| 1466 if (names.length == 1) { |
| 1467 new_url = prefix + str; |
| 1468 files = files.concat(readFileList(new_url)); |
| 1469 } else { |
| 1470 var s = ""; |
| 1471 var p = ""; |
| 1472 for (var jj = 0; jj < names.length; ++jj) { |
| 1473 s += p + prefix + names[jj]; |
| 1474 p = " "; |
| 1475 } |
| 1476 files.push(s); |
| 1477 } |
| 1478 } |
| 1479 } |
| 1480 } else { |
| 1481 files.push(url); |
| 1482 } |
| 1483 return files; |
| 1484 }; |
| 1485 |
| 1486 /** |
| 1487 * Loads a shader. |
| 1488 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. |
| 1489 * @param {string} shaderSource The shader source. |
| 1490 * @param {number} shaderType The type of shader. |
| 1491 * @param {function(string): void) opt_errorCallback callback for errors. |
| 1492 * @return {!WebGLShader} The created shader. |
| 1493 */ |
| 1494 var loadShader = function(gl, shaderSource, shaderType, opt_errorCallback) { |
| 1495 var errFn = opt_errorCallback || error; |
| 1496 // Create the shader object |
| 1497 var shader = gl.createShader(shaderType); |
| 1498 if (shader == null) { |
| 1499 errFn("*** Error: unable to create shader '"+shaderSource+"'"); |
| 1500 return null; |
| 1501 } |
| 1502 |
| 1503 // Load the shader source |
| 1504 gl.shaderSource(shader, shaderSource); |
| 1505 var err = gl.getError(); |
| 1506 if (err != gl.NO_ERROR) { |
| 1507 errFn("*** Error loading shader '" + shader + "':" + glEnumToString(gl, err)
); |
| 1508 return null; |
| 1509 } |
| 1510 |
| 1511 // Compile the shader |
| 1512 gl.compileShader(shader); |
| 1513 |
| 1514 // Check the compile status |
| 1515 var compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS); |
| 1516 if (!compiled) { |
| 1517 // Something went wrong during compilation; get the error |
| 1518 lastError = gl.getShaderInfoLog(shader); |
| 1519 errFn("*** Error compiling " + glEnumToString(gl, shaderType) + " '" + shade
r + "':" + lastError); |
| 1520 gl.deleteShader(shader); |
| 1521 return null; |
| 1522 } |
| 1523 |
| 1524 return shader; |
| 1525 } |
| 1526 |
| 1527 /** |
| 1528 * Loads a shader from a URL. |
| 1529 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. |
| 1530 * @param {file} file The URL of the shader source. |
| 1531 * @param {number} type The type of shader. |
| 1532 * @param {function(string): void) opt_errorCallback callback for errors. |
| 1533 * @return {!WebGLShader} The created shader. |
| 1534 */ |
| 1535 var loadShaderFromFile = function(gl, file, type, opt_errorCallback) { |
| 1536 var shaderSource = readFile(file); |
| 1537 return loadShader(gl, shaderSource, type, opt_errorCallback); |
| 1538 }; |
| 1539 |
| 1540 /** |
| 1541 * Gets the content of script. |
| 1542 * @param {string} scriptId The id of the script tag. |
| 1543 * @return {string} The content of the script. |
| 1544 */ |
| 1545 var getScript = function(scriptId) { |
| 1546 var shaderScript = document.getElementById(scriptId); |
| 1547 if (!shaderScript) { |
| 1548 throw("*** Error: unknown script element" + scriptId); |
| 1549 } |
| 1550 return shaderScript.text; |
| 1551 }; |
| 1552 |
| 1553 /** |
| 1554 * Loads a shader from a script tag. |
| 1555 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. |
| 1556 * @param {string} scriptId The id of the script tag. |
| 1557 * @param {number} opt_shaderType The type of shader. If not passed in it will |
| 1558 * be derived from the type of the script tag. |
| 1559 * @param {function(string): void) opt_errorCallback callback for errors. |
| 1560 * @return {!WebGLShader} The created shader. |
| 1561 */ |
| 1562 var loadShaderFromScript = function( |
| 1563 gl, scriptId, opt_shaderType, opt_errorCallback) { |
| 1564 var shaderSource = ""; |
| 1565 var shaderScript = document.getElementById(scriptId); |
| 1566 if (!shaderScript) { |
| 1567 throw("*** Error: unknown script element " + scriptId); |
| 1568 } |
| 1569 shaderSource = shaderScript.text; |
| 1570 |
| 1571 if (!opt_shaderType) { |
| 1572 if (shaderScript.type == "x-shader/x-vertex") { |
| 1573 opt_shaderType = gl.VERTEX_SHADER; |
| 1574 } else if (shaderScript.type == "x-shader/x-fragment") { |
| 1575 opt_shaderType = gl.FRAGMENT_SHADER; |
| 1576 } else { |
| 1577 throw("*** Error: unknown shader type"); |
| 1578 return null; |
| 1579 } |
| 1580 } |
| 1581 |
| 1582 return loadShader( |
| 1583 gl, shaderSource, opt_shaderType, opt_errorCallback); |
| 1584 }; |
| 1585 |
| 1586 var loadStandardProgram = function(gl) { |
| 1587 var program = gl.createProgram(); |
| 1588 gl.attachShader(program, loadStandardVertexShader(gl)); |
| 1589 gl.attachShader(program, loadStandardFragmentShader(gl)); |
| 1590 gl.bindAttribLocation(program, 0, "a_vertex"); |
| 1591 gl.bindAttribLocation(program, 1, "a_normal"); |
| 1592 linkProgram(gl, program); |
| 1593 return program; |
| 1594 }; |
| 1595 |
| 1596 /** |
| 1597 * Loads shaders from files, creates a program, attaches the shaders and links. |
| 1598 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. |
| 1599 * @param {string} vertexShaderPath The URL of the vertex shader. |
| 1600 * @param {string} fragmentShaderPath The URL of the fragment shader. |
| 1601 * @param {function(string): void) opt_errorCallback callback for errors. |
| 1602 * @return {!WebGLProgram} The created program. |
| 1603 */ |
| 1604 var loadProgramFromFile = function( |
| 1605 gl, vertexShaderPath, fragmentShaderPath, opt_errorCallback) { |
| 1606 var program = gl.createProgram(); |
| 1607 var vs = loadShaderFromFile( |
| 1608 gl, vertexShaderPath, gl.VERTEX_SHADER, opt_errorCallback); |
| 1609 var fs = loadShaderFromFile( |
| 1610 gl, fragmentShaderPath, gl.FRAGMENT_SHADER, opt_errorCallback); |
| 1611 if (vs && fs) { |
| 1612 gl.attachShader(program, vs); |
| 1613 gl.attachShader(program, fs); |
| 1614 linkProgram(gl, program, opt_errorCallback); |
| 1615 } |
| 1616 if (vs) { |
| 1617 gl.deleteShader(vs); |
| 1618 } |
| 1619 if (fs) { |
| 1620 gl.deleteShader(fs); |
| 1621 } |
| 1622 return program; |
| 1623 }; |
| 1624 |
| 1625 /** |
| 1626 * Loads shaders from script tags, creates a program, attaches the shaders and |
| 1627 * links. |
| 1628 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. |
| 1629 * @param {string} vertexScriptId The id of the script tag that contains the |
| 1630 * vertex shader. |
| 1631 * @param {string} fragmentScriptId The id of the script tag that contains the |
| 1632 * fragment shader. |
| 1633 * @param {function(string): void) opt_errorCallback callback for errors. |
| 1634 * @return {!WebGLProgram} The created program. |
| 1635 */ |
| 1636 var loadProgramFromScript = function loadProgramFromScript( |
| 1637 gl, vertexScriptId, fragmentScriptId, opt_errorCallback) { |
| 1638 var program = gl.createProgram(); |
| 1639 gl.attachShader( |
| 1640 program, |
| 1641 loadShaderFromScript( |
| 1642 gl, vertexScriptId, gl.VERTEX_SHADER, opt_errorCallback)); |
| 1643 gl.attachShader( |
| 1644 program, |
| 1645 loadShaderFromScript( |
| 1646 gl, fragmentScriptId, gl.FRAGMENT_SHADER, opt_errorCallback)); |
| 1647 linkProgram(gl, program, opt_errorCallback); |
| 1648 return program; |
| 1649 }; |
| 1650 |
| 1651 /** |
| 1652 * Loads shaders from source, creates a program, attaches the shaders and |
| 1653 * links. |
| 1654 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. |
| 1655 * @param {!WebGLShader} vertexShader The vertex shader. |
| 1656 * @param {!WebGLShader} fragmentShader The fragment shader. |
| 1657 * @param {function(string): void) opt_errorCallback callback for errors. |
| 1658 * @return {!WebGLProgram} The created program. |
| 1659 */ |
| 1660 var createProgram = function(gl, vertexShader, fragmentShader, opt_errorCallback
) { |
| 1661 var program = gl.createProgram(); |
| 1662 gl.attachShader(program, vertexShader); |
| 1663 gl.attachShader(program, fragmentShader); |
| 1664 linkProgram(gl, program, opt_errorCallback); |
| 1665 return program; |
| 1666 }; |
| 1667 |
| 1668 /** |
| 1669 * Loads shaders from source, creates a program, attaches the shaders and |
| 1670 * links. |
| 1671 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. |
| 1672 * @param {string} vertexShader The vertex shader source. |
| 1673 * @param {string} fragmentShader The fragment shader source. |
| 1674 * @param {function(string): void) opt_errorCallback callback for errors. |
| 1675 * @return {!WebGLProgram} The created program. |
| 1676 */ |
| 1677 var loadProgram = function( |
| 1678 gl, vertexShader, fragmentShader, opt_errorCallback) { |
| 1679 var program; |
| 1680 var vs = loadShader( |
| 1681 gl, vertexShader, gl.VERTEX_SHADER, opt_errorCallback); |
| 1682 var fs = loadShader( |
| 1683 gl, fragmentShader, gl.FRAGMENT_SHADER, opt_errorCallback); |
| 1684 if (vs && fs) { |
| 1685 program = createProgram(gl, vs, fs, opt_errorCallback) |
| 1686 } |
| 1687 if (vs) { |
| 1688 gl.deleteShader(vs); |
| 1689 } |
| 1690 if (fs) { |
| 1691 gl.deleteShader(fs); |
| 1692 } |
| 1693 return program; |
| 1694 }; |
| 1695 |
| 1696 /** |
| 1697 * Loads shaders from source, creates a program, attaches the shaders and |
| 1698 * links but expects error. |
| 1699 * |
| 1700 * GLSL 1.0.17 10.27 effectively says that compileShader can |
| 1701 * always succeed as long as linkProgram fails so we can't |
| 1702 * rely on compileShader failing. This function expects |
| 1703 * one of the shader to fail OR linking to fail. |
| 1704 * |
| 1705 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. |
| 1706 * @param {string} vertexShaderScriptId The vertex shader. |
| 1707 * @param {string} fragmentShaderScriptId The fragment shader. |
| 1708 * @return {WebGLProgram} The created program. |
| 1709 */ |
| 1710 var loadProgramFromScriptExpectError = function( |
| 1711 gl, vertexShaderScriptId, fragmentShaderScriptId) { |
| 1712 var vertexShader = loadShaderFromScript(gl, vertexShaderScriptId); |
| 1713 if (!vertexShader) { |
| 1714 return null; |
| 1715 } |
| 1716 var fragmentShader = loadShaderFromScript(gl, fragmentShaderScriptId); |
| 1717 if (!fragmentShader) { |
| 1718 return null; |
| 1719 } |
| 1720 var linkSuccess = true; |
| 1721 var program = gl.createProgram(); |
| 1722 gl.attachShader(program, vertexShader); |
| 1723 gl.attachShader(program, fragmentShader); |
| 1724 linkSuccess = true; |
| 1725 linkProgram(gl, program, function() { |
| 1726 linkSuccess = false; |
| 1727 }); |
| 1728 return linkSuccess ? program : null; |
| 1729 }; |
| 1730 |
| 1731 |
| 1732 var getActiveMap = function(gl, program, typeInfo) { |
| 1733 var numVariables = gl.getProgramParameter(program, gl[typeInfo.param]); |
| 1734 var variables = {}; |
| 1735 for (var ii = 0; ii < numVariables; ++ii) { |
| 1736 var info = gl[typeInfo.activeFn](program, ii); |
| 1737 variables[info.name] = { |
| 1738 name: info.name, |
| 1739 size: info.size, |
| 1740 type: info.type, |
| 1741 location: gl[typeInfo.locFn](program, info.name) |
| 1742 }; |
| 1743 } |
| 1744 return variables; |
| 1745 }; |
| 1746 |
| 1747 /** |
| 1748 * Returns a map of attrib names to info about those |
| 1749 * attribs. |
| 1750 * |
| 1751 * eg: |
| 1752 * { "attrib1Name": |
| 1753 * { |
| 1754 * name: "attrib1Name", |
| 1755 * size: 1, |
| 1756 * type: gl.FLOAT_MAT2, |
| 1757 * location: 0 |
| 1758 * }, |
| 1759 * "attrib2Name[0]": |
| 1760 * { |
| 1761 * name: "attrib2Name[0]", |
| 1762 * size: 4, |
| 1763 * type: gl.FLOAT, |
| 1764 * location: 1 |
| 1765 * }, |
| 1766 * } |
| 1767 * |
| 1768 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. |
| 1769 * @param {WebGLProgram} The program to query for attribs. |
| 1770 * @return the map. |
| 1771 */ |
| 1772 var getAttribMap = function(gl, program) { |
| 1773 return getActiveMap(gl, program, { |
| 1774 param: "ACTIVE_ATTRIBUTES", |
| 1775 activeFn: "getActiveAttrib", |
| 1776 locFn: "getAttribLocation" |
| 1777 }); |
| 1778 }; |
| 1779 |
| 1780 /** |
| 1781 * Returns a map of uniform names to info about those uniforms. |
| 1782 * |
| 1783 * eg: |
| 1784 * { "uniform1Name": |
| 1785 * { |
| 1786 * name: "uniform1Name", |
| 1787 * size: 1, |
| 1788 * type: gl.FLOAT_MAT2, |
| 1789 * location: WebGLUniformLocation |
| 1790 * }, |
| 1791 * "uniform2Name[0]": |
| 1792 * { |
| 1793 * name: "uniform2Name[0]", |
| 1794 * size: 4, |
| 1795 * type: gl.FLOAT, |
| 1796 * location: WebGLUniformLocation |
| 1797 * }, |
| 1798 * } |
| 1799 * |
| 1800 * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. |
| 1801 * @param {WebGLProgram} The program to query for uniforms. |
| 1802 * @return the map. |
| 1803 */ |
| 1804 var getUniformMap = function(gl, program) { |
| 1805 return getActiveMap(gl, program, { |
| 1806 param: "ACTIVE_UNIFORMS", |
| 1807 activeFn: "getActiveUniform", |
| 1808 locFn: "getUniformLocation" |
| 1809 }); |
| 1810 }; |
| 1811 |
| 1812 var basePath; |
| 1813 var getBasePath = function() { |
| 1814 if (!basePath) { |
| 1815 var expectedBase = "webgl-test-utils.js"; |
| 1816 var scripts = document.getElementsByTagName('script'); |
| 1817 for (var script, i = 0; script = scripts[i]; i++) { |
| 1818 var src = script.src; |
| 1819 var l = src.length; |
| 1820 if (src.substr(l - expectedBase.length) == expectedBase) { |
| 1821 basePath = src.substr(0, l - expectedBase.length); |
| 1822 } |
| 1823 } |
| 1824 } |
| 1825 return basePath; |
| 1826 }; |
| 1827 |
| 1828 var loadStandardVertexShader = function(gl) { |
| 1829 return loadShaderFromFile( |
| 1830 gl, getBasePath() + "vertexShader.vert", gl.VERTEX_SHADER); |
| 1831 }; |
| 1832 |
| 1833 var loadStandardFragmentShader = function(gl) { |
| 1834 return loadShaderFromFile( |
| 1835 gl, getBasePath() + "fragmentShader.frag", gl.FRAGMENT_SHADER); |
| 1836 }; |
| 1837 |
| 1838 /** |
| 1839 * Loads an image asynchronously. |
| 1840 * @param {string} url URL of image to load. |
| 1841 * @param {!function(!Element): void} callback Function to call |
| 1842 * with loaded image. |
| 1843 */ |
| 1844 var loadImageAsync = function(url, callback) { |
| 1845 var img = document.createElement('img'); |
| 1846 img.onload = function() { |
| 1847 callback(img); |
| 1848 }; |
| 1849 img.src = url; |
| 1850 }; |
| 1851 |
| 1852 /** |
| 1853 * Loads an array of images. |
| 1854 * @param {!Array.<string>} urls URLs of images to load. |
| 1855 * @param {!function(!{string, img}): void} callback. Callback |
| 1856 * that gets passed map of urls to img tags. |
| 1857 */ |
| 1858 var loadImagesAsync = function(urls, callback) { |
| 1859 var count = 1; |
| 1860 var images = { }; |
| 1861 function countDown() { |
| 1862 --count; |
| 1863 if (count == 0) { |
| 1864 callback(images); |
| 1865 } |
| 1866 } |
| 1867 function imageLoaded(url) { |
| 1868 return function(img) { |
| 1869 images[url] = img; |
| 1870 countDown(); |
| 1871 } |
| 1872 } |
| 1873 for (var ii = 0; ii < urls.length; ++ii) { |
| 1874 ++count; |
| 1875 loadImageAsync(urls[ii], imageLoaded(urls[ii])); |
| 1876 } |
| 1877 countDown(); |
| 1878 }; |
| 1879 |
| 1880 /** |
| 1881 * Returns a map of key=value values from url. |
| 1882 * @return {!Object.<string, number>} map of keys to values. |
| 1883 */ |
| 1884 var getUrlArguments = function() { |
| 1885 var args = {}; |
| 1886 try { |
| 1887 var s = window.location.href; |
| 1888 var q = s.indexOf("?"); |
| 1889 var e = s.indexOf("#"); |
| 1890 if (e < 0) { |
| 1891 e = s.length; |
| 1892 } |
| 1893 var query = s.substring(q + 1, e); |
| 1894 var pairs = query.split("&"); |
| 1895 for (var ii = 0; ii < pairs.length; ++ii) { |
| 1896 var keyValue = pairs[ii].split("="); |
| 1897 var key = keyValue[0]; |
| 1898 var value = decodeURIComponent(keyValue[1]); |
| 1899 args[key] = value; |
| 1900 } |
| 1901 } catch (e) { |
| 1902 throw "could not parse url"; |
| 1903 } |
| 1904 return args; |
| 1905 }; |
| 1906 |
| 1907 /** |
| 1908 * Makes an image from a canvas. |
| 1909 * @param {!HTMLCanvas} canvas Canvas to make image from. |
| 1910 * @return {!Image} The created image. |
| 1911 */ |
| 1912 var makeImage = function(canvas) { |
| 1913 var img = document.createElement('img'); |
| 1914 img.src = canvas.toDataURL(); |
| 1915 return img; |
| 1916 }; |
| 1917 |
| 1918 /** |
| 1919 * Inserts an image with a caption into 'element'. |
| 1920 * @param {!HTMLElement} element Element to append image to. |
| 1921 * @param {string} caption caption to associate with image. |
| 1922 * @param {!Image) img image to insert. |
| 1923 */ |
| 1924 var insertImage = function(element, caption, img) { |
| 1925 var div = document.createElement("div"); |
| 1926 div.appendChild(img); |
| 1927 var label = document.createElement("div"); |
| 1928 label.appendChild(document.createTextNode(caption)); |
| 1929 div.appendChild(label); |
| 1930 element.appendChild(div); |
| 1931 }; |
| 1932 |
| 1933 /** |
| 1934 * Inserts a 'label' that when clicked expands to the pre |
| 1935 * formatted text supplied by 'source'. |
| 1936 * @param {!HTMLElement} element element to append label to. |
| 1937 * @param {string} label label for anchor. |
| 1938 * @param {string} source preformatted text to expand to. |
| 1939 * @param {string} opt_url url of source. If provided a 2nd link |
| 1940 * will be added. |
| 1941 */ |
| 1942 var addShaderSource = function(element, label, source, opt_url) { |
| 1943 var div = document.createElement("div"); |
| 1944 var s = document.createElement("pre"); |
| 1945 s.className = "shader-source"; |
| 1946 s.style.display = "none"; |
| 1947 var ol = document.createElement("ol"); |
| 1948 //s.appendChild(document.createTextNode(source)); |
| 1949 var lines = source.split("\n"); |
| 1950 for (var ii = 0; ii < lines.length; ++ii) { |
| 1951 var line = lines[ii]; |
| 1952 var li = document.createElement("li"); |
| 1953 li.appendChild(document.createTextNode(line)); |
| 1954 ol.appendChild(li); |
| 1955 } |
| 1956 s.appendChild(ol); |
| 1957 var l = document.createElement("a"); |
| 1958 l.href = "show-shader-source"; |
| 1959 l.appendChild(document.createTextNode(label)); |
| 1960 l.addEventListener('click', function(event) { |
| 1961 if (event.preventDefault) { |
| 1962 event.preventDefault(); |
| 1963 } |
| 1964 s.style.display = (s.style.display == 'none') ? 'block' : 'none'; |
| 1965 return false; |
| 1966 }, false); |
| 1967 div.appendChild(l); |
| 1968 if (opt_url) { |
| 1969 var u = document.createElement("a"); |
| 1970 u.href = opt_url; |
| 1971 div.appendChild(document.createTextNode(" ")); |
| 1972 u.appendChild(document.createTextNode("(" + opt_url + ")")); |
| 1973 div.appendChild(u); |
| 1974 } |
| 1975 div.appendChild(s); |
| 1976 element.appendChild(div); |
| 1977 }; |
| 1978 |
| 1979 // Add your prefix here. |
| 1980 var browserPrefixes = [ |
| 1981 "", |
| 1982 "MOZ_", |
| 1983 "OP_", |
| 1984 "WEBKIT_" |
| 1985 ]; |
| 1986 |
| 1987 /** |
| 1988 * Given an extension name like WEBGL_compressed_texture_s3tc |
| 1989 * returns the name of the supported version extension, like |
| 1990 * WEBKIT_WEBGL_compressed_teture_s3tc |
| 1991 * @param {string} name Name of extension to look for. |
| 1992 * @return {string} name of extension found or undefined if not |
| 1993 * found. |
| 1994 */ |
| 1995 var getSupportedExtensionWithKnownPrefixes = function(gl, name) { |
| 1996 var supported = gl.getSupportedExtensions(); |
| 1997 for (var ii = 0; ii < browserPrefixes.length; ++ii) { |
| 1998 var prefixedName = browserPrefixes[ii] + name; |
| 1999 if (supported.indexOf(prefixedName) >= 0) { |
| 2000 return prefixedName; |
| 2001 } |
| 2002 } |
| 2003 }; |
| 2004 |
| 2005 /** |
| 2006 * Given an extension name like WEBGL_compressed_texture_s3tc |
| 2007 * returns the supported version extension, like |
| 2008 * WEBKIT_WEBGL_compressed_teture_s3tc |
| 2009 * @param {string} name Name of extension to look for. |
| 2010 * @return {WebGLExtension} The extension or undefined if not |
| 2011 * found. |
| 2012 */ |
| 2013 var getExtensionWithKnownPrefixes = function(gl, name) { |
| 2014 for (var ii = 0; ii < browserPrefixes.length; ++ii) { |
| 2015 var prefixedName = browserPrefixes[ii] + name; |
| 2016 var ext = gl.getExtension(prefixedName); |
| 2017 if (ext) { |
| 2018 return ext; |
| 2019 } |
| 2020 } |
| 2021 }; |
| 2022 |
| 2023 |
| 2024 var replaceRE = /\$\((\w+)\)/g; |
| 2025 |
| 2026 /** |
| 2027 * Replaces strings with property values. |
| 2028 * Given a string like "hello $(first) $(last)" and an object |
| 2029 * like {first:"John", last:"Smith"} will return |
| 2030 * "hello John Smith". |
| 2031 * @param {string} str String to do replacements in. |
| 2032 * @param {...} 1 or more objects containing properties. |
| 2033 */ |
| 2034 var replaceParams = function(str) { |
| 2035 var args = arguments; |
| 2036 return str.replace(replaceRE, function(str, p1, offset, s) { |
| 2037 for (var ii = 1; ii < args.length; ++ii) { |
| 2038 if (args[ii][p1] !== undefined) { |
| 2039 return args[ii][p1]; |
| 2040 } |
| 2041 } |
| 2042 throw "unknown string param '" + p1 + "'"; |
| 2043 }); |
| 2044 }; |
| 2045 |
| 2046 var upperCaseFirstLetter = function(str) { |
| 2047 return str.substring(0, 1).toUpperCase() + str.substring(1); |
| 2048 }; |
| 2049 |
| 2050 /** |
| 2051 * Gets a prefixed property. For example, |
| 2052 * |
| 2053 * var fn = getPrefixedProperty( |
| 2054 * window, |
| 2055 * "requestAnimationFrame"); |
| 2056 * |
| 2057 * Will return either: |
| 2058 * "window.requestAnimationFrame", |
| 2059 * "window.oRequestAnimationFrame", |
| 2060 * "window.msRequestAnimationFrame", |
| 2061 * "window.mozRequestAnimationFrame", |
| 2062 * "window.webKitRequestAnimationFrame", |
| 2063 * undefined |
| 2064 * |
| 2065 * the non-prefixed function is tried first. |
| 2066 */ |
| 2067 var propertyPrefixes = ["", "moz", "ms", "o", "webkit"]; |
| 2068 var getPrefixedProperty = function(obj, propertyName) { |
| 2069 for (var ii = 0; ii < propertyPrefixes.length; ++ii) { |
| 2070 var prefix = propertyPrefixes[ii]; |
| 2071 var name = prefix + propertyName; |
| 2072 console.log(name); |
| 2073 var property = obj[name]; |
| 2074 if (property) { |
| 2075 return property; |
| 2076 } |
| 2077 if (ii == 0) { |
| 2078 propertyName = upperCaseFirstLetter(propertyName); |
| 2079 } |
| 2080 } |
| 2081 return undefined; |
| 2082 }; |
| 2083 |
| 2084 /** |
| 2085 * Provides requestAnimationFrame in a cross browser way. |
| 2086 */ |
| 2087 var requestAnimFrame = getPrefixedProperty(window, "requestAnimationFrame") || |
| 2088 function(callback, element) { |
| 2089 return window.setTimeout(callback, 1000 / 70); |
| 2090 }; |
| 2091 |
| 2092 /** |
| 2093 * Provides cancelAnimationFrame in a cross browser way. |
| 2094 */ |
| 2095 var cancelAnimFrame = getPrefixedProperty(window, "cancelAnimationFrame") || |
| 2096 window.clearTimeout; |
| 2097 |
| 2098 /** |
| 2099 * Provides requestFullScreen in a cross browser way. |
| 2100 */ |
| 2101 var requestFullScreen = function(element) { |
| 2102 var fn = getPrefixedProperty(element, "requestFullScreen"); |
| 2103 if (fn) { |
| 2104 fn.call(element); |
| 2105 } |
| 2106 }; |
| 2107 |
| 2108 /** |
| 2109 * Provides cancelFullScreen in a cross browser way. |
| 2110 */ |
| 2111 var cancelFullScreen = function() { |
| 2112 var fn = getPrefixedProperty(document, "cancelFullScreen"); |
| 2113 if (fn) { |
| 2114 fn.call(document); |
| 2115 } |
| 2116 }; |
| 2117 |
| 2118 var fullScreenStateName; |
| 2119 (function() { |
| 2120 var fullScreenStateNames = [ |
| 2121 "isFullScreen", |
| 2122 "fullScreen", |
| 2123 ]; |
| 2124 for (var ii = 0; ii < fullScreenStateNames.length; ++ii) { |
| 2125 var propertyName = fullScreenStateNames[ii]; |
| 2126 for (var jj = 0; jj < propertyPrefixes.length; ++jj) { |
| 2127 var prefix = propertyPrefixes[jj]; |
| 2128 if (prefix.length) { |
| 2129 propertyName = upperCaseFirstLetter(propertyName); |
| 2130 fullScreenStateName = prefix + propertyName; |
| 2131 if (document[fullScreenStateName] !== undefined) { |
| 2132 return; |
| 2133 } |
| 2134 } |
| 2135 } |
| 2136 fullScreenStateName = undefined; |
| 2137 } |
| 2138 }()); |
| 2139 |
| 2140 /** |
| 2141 * @return {boolean} True if fullscreen mode is active. |
| 2142 */ |
| 2143 var getFullScreenState = function() { |
| 2144 console.log("fullscreenstatename:" + fullScreenStateName); |
| 2145 console.log(document[fullScreenStateName]); |
| 2146 return document[fullScreenStateName]; |
| 2147 }; |
| 2148 |
| 2149 /** |
| 2150 * @param {!HTMLElement} element The element to go fullscreen. |
| 2151 * @param {!function(boolean)} callback A function that will be called |
| 2152 * when entering/exiting fullscreen. It is passed true if |
| 2153 * entering fullscreen, false if exiting. |
| 2154 */ |
| 2155 var onFullScreenChange = function(element, callback) { |
| 2156 propertyPrefixes.forEach(function(prefix) { |
| 2157 var eventName = prefix + "fullscreenchange"; |
| 2158 console.log("addevent: " + eventName); |
| 2159 document.addEventListener(eventName, function(event) { |
| 2160 console.log("event: " + eventName); |
| 2161 callback(getFullScreenState()); |
| 2162 }); |
| 2163 }); |
| 2164 }; |
| 2165 |
| 2166 /** |
| 2167 * @param {!string} buttonId The id of the button that will toggle fullscreen |
| 2168 * mode. |
| 2169 * @param {!string} fullscreenId The id of the element to go fullscreen. |
| 2170 * @param {!function(boolean)} callback A function that will be called |
| 2171 * when entering/exiting fullscreen. It is passed true if |
| 2172 * entering fullscreen, false if exiting. |
| 2173 * @return {boolean} True if fullscreen mode is supported. |
| 2174 */ |
| 2175 var setupFullscreen = function(buttonId, fullscreenId, callback) { |
| 2176 if (!fullScreenStateName) { |
| 2177 return false; |
| 2178 } |
| 2179 |
| 2180 var fullscreenElement = document.getElementById(fullscreenId); |
| 2181 onFullScreenChange(fullscreenElement, callback); |
| 2182 |
| 2183 var toggleFullScreen = function(event) { |
| 2184 if (getFullScreenState()) { |
| 2185 cancelFullScreen(fullscreenElement); |
| 2186 } else { |
| 2187 requestFullScreen(fullscreenElement); |
| 2188 } |
| 2189 event.preventDefault(); |
| 2190 return false; |
| 2191 }; |
| 2192 |
| 2193 var buttonElement = document.getElementById(buttonId); |
| 2194 buttonElement.addEventListener('click', toggleFullScreen); |
| 2195 |
| 2196 return true; |
| 2197 }; |
| 2198 |
| 2199 /** |
| 2200 * Waits for the browser to composite the canvas associated with |
| 2201 * the WebGL context passed in. |
| 2202 * @param {WebGLRenderingContext} gl The WebGLRenderingContext to use. |
| 2203 * @param {function()} callback A function to call after compositing has taken |
| 2204 * place. |
| 2205 */ |
| 2206 var waitForComposite = function(gl, callback) { |
| 2207 var frames = 5; |
| 2208 var countDown = function() { |
| 2209 if (frames == 0) { |
| 2210 callback(); |
| 2211 } else { |
| 2212 --frames; |
| 2213 requestAnimFrame(countDown); |
| 2214 } |
| 2215 }; |
| 2216 countDown(); |
| 2217 }; |
| 2218 |
| 2219 /** |
| 2220 * Runs an array of functions, yielding to the browser between each step. |
| 2221 * If you want to know when all the steps are finished add a last step. |
| 2222 * @param {!Array.<function(): void>} steps. Array of functions. |
| 2223 */ |
| 2224 var runSteps = function(steps) { |
| 2225 if (!steps.length) { |
| 2226 return; |
| 2227 } |
| 2228 |
| 2229 // copy steps so they can't be modifed. |
| 2230 var stepsToRun = steps.slice(); |
| 2231 var currentStep = 0; |
| 2232 var runNextStep = function() { |
| 2233 stepsToRun[currentStep++](); |
| 2234 if (currentStep < stepsToRun.length) { |
| 2235 setTimeout(runNextStep, 1); |
| 2236 } |
| 2237 }; |
| 2238 runNextStep(); |
| 2239 }; |
| 2240 |
| 2241 /** |
| 2242 * Starts playing a video and waits for it to be consumable. |
| 2243 * @param {!HTMLVideoElement} video An HTML5 Video element. |
| 2244 * @param {!function(!HTMLVideoElement): void>} callback Function to call when |
| 2245 * video is ready. |
| 2246 */ |
| 2247 var startPlayingAndWaitForVideo = function(video, callback) { |
| 2248 var gotPlaying = false; |
| 2249 var gotTimeUpdate = false; |
| 2250 |
| 2251 var maybeCallCallback = function() { |
| 2252 if (gotPlaying && gotTimeUpdate && callback) { |
| 2253 callback(video); |
| 2254 callback = undefined; |
| 2255 video.removeEventListener('playing', playingListener, true); |
| 2256 video.removeEventListener('timeupdate', timeupdateListener, true); |
| 2257 } |
| 2258 }; |
| 2259 |
| 2260 var playingListener = function() { |
| 2261 gotPlaying = true; |
| 2262 maybeCallCallback(); |
| 2263 }; |
| 2264 |
| 2265 var timeupdateListener = function() { |
| 2266 // Checking to make sure the current time has advanced beyond |
| 2267 // the start time seems to be a reliable heuristic that the |
| 2268 // video element has data that can be consumed. |
| 2269 if (video.currentTime > 0.0) { |
| 2270 gotTimeUpdate = true; |
| 2271 maybeCallCallback(); |
| 2272 } |
| 2273 }; |
| 2274 |
| 2275 video.addEventListener('playing', playingListener, true); |
| 2276 video.addEventListener('timeupdate', timeupdateListener, true); |
| 2277 video.loop = true; |
| 2278 video.play(); |
| 2279 }; |
| 2280 |
| 2281 return { |
| 2282 addShaderSource: addShaderSource, |
| 2283 cancelAnimFrame: cancelAnimFrame, |
| 2284 create3DContext: create3DContext, |
| 2285 create3DContextWithWrapperThatThrowsOnGLError: |
| 2286 create3DContextWithWrapperThatThrowsOnGLError, |
| 2287 checkAreaInAndOut: checkAreaInAndOut, |
| 2288 checkCanvas: checkCanvas, |
| 2289 checkCanvasRect: checkCanvasRect, |
| 2290 checkCanvasRectColor: checkCanvasRectColor, |
| 2291 clipToRange: clipToRange, |
| 2292 createColoredTexture: createColoredTexture, |
| 2293 createProgram: createProgram, |
| 2294 clearAndDrawUnitQuad: clearAndDrawUnitQuad, |
| 2295 clearAndDrawIndexedQuad: clearAndDrawIndexedQuad, |
| 2296 drawUnitQuad: drawUnitQuad, |
| 2297 drawIndexedQuad: drawIndexedQuad, |
| 2298 drawUByteColorQuad: drawUByteColorQuad, |
| 2299 drawFloatColorQuad: drawFloatColorQuad, |
| 2300 endsWith: endsWith, |
| 2301 fillTexture: fillTexture, |
| 2302 getBytesPerComponent: getBytesPerComponent, |
| 2303 getExtensionWithKnownPrefixes: getExtensionWithKnownPrefixes, |
| 2304 getFileListAsync: getFileListAsync, |
| 2305 getLastError: getLastError, |
| 2306 getPrefixedProperty: getPrefixedProperty, |
| 2307 getScript: getScript, |
| 2308 getSupportedExtensionWithKnownPrefixes: getSupportedExtensionWithKnownPrefixes
, |
| 2309 getUrlArguments: getUrlArguments, |
| 2310 getAttribMap: getAttribMap, |
| 2311 getUniformMap: getUniformMap, |
| 2312 glEnumToString: glEnumToString, |
| 2313 glErrorShouldBe: glErrorShouldBe, |
| 2314 glTypeToArrayBufferType: glTypeToArrayBufferType, |
| 2315 hasAttributeCaseInsensitive: hasAttributeCaseInsensitive, |
| 2316 insertImage: insertImage, |
| 2317 loadImageAsync: loadImageAsync, |
| 2318 loadImagesAsync: loadImagesAsync, |
| 2319 loadProgram: loadProgram, |
| 2320 loadProgramFromFile: loadProgramFromFile, |
| 2321 loadProgramFromScript: loadProgramFromScript, |
| 2322 loadProgramFromScriptExpectError: loadProgramFromScriptExpectError, |
| 2323 loadShader: loadShader, |
| 2324 loadShaderFromFile: loadShaderFromFile, |
| 2325 loadShaderFromScript: loadShaderFromScript, |
| 2326 loadStandardProgram: loadStandardProgram, |
| 2327 loadStandardVertexShader: loadStandardVertexShader, |
| 2328 loadStandardFragmentShader: loadStandardFragmentShader, |
| 2329 loadTextFileAsync: loadTextFileAsync, |
| 2330 loadTexture: loadTexture, |
| 2331 log: log, |
| 2332 loggingOff: loggingOff, |
| 2333 makeImage: makeImage, |
| 2334 error: error, |
| 2335 shallowCopyObject: shallowCopyObject, |
| 2336 setupColorQuad: setupColorQuad, |
| 2337 setupProgram: setupProgram, |
| 2338 setupQuad: setupQuad, |
| 2339 setupIndexedQuad: setupIndexedQuad, |
| 2340 setupIndexedQuadWithOptions: setupIndexedQuadWithOptions, |
| 2341 setupSimpleColorFragmentShader: setupSimpleColorFragmentShader, |
| 2342 setupSimpleColorVertexShader: setupSimpleColorVertexShader, |
| 2343 setupSimpleColorProgram: setupSimpleColorProgram, |
| 2344 setupSimpleTextureFragmentShader: setupSimpleTextureFragmentShader, |
| 2345 setupSimpleTextureProgram: setupSimpleTextureProgram, |
| 2346 setupSimpleTextureVertexShader: setupSimpleTextureVertexShader, |
| 2347 setupSimpleVertexColorFragmentShader: setupSimpleVertexColorFragmentShader, |
| 2348 setupSimpleVertexColorProgram: setupSimpleVertexColorProgram, |
| 2349 setupSimpleVertexColorVertexShader: setupSimpleVertexColorVertexShader, |
| 2350 setupNoTexCoordTextureProgram: setupNoTexCoordTextureProgram, |
| 2351 setupNoTexCoordTextureVertexShader: setupNoTexCoordTextureVertexShader, |
| 2352 setupTexturedQuad: setupTexturedQuad, |
| 2353 setupTexturedQuadWithTexCoords: setupTexturedQuadWithTexCoords, |
| 2354 setupUnitQuad: setupUnitQuad, |
| 2355 setupUnitQuadWithTexCoords: setupUnitQuadWithTexCoords, |
| 2356 setFloatDrawColor: setFloatDrawColor, |
| 2357 setUByteDrawColor: setUByteDrawColor, |
| 2358 startPlayingAndWaitForVideo: startPlayingAndWaitForVideo, |
| 2359 startsWith: startsWith, |
| 2360 shouldGenerateGLError: shouldGenerateGLError, |
| 2361 readFile: readFile, |
| 2362 readFileList: readFileList, |
| 2363 replaceParams: replaceParams, |
| 2364 requestAnimFrame: requestAnimFrame, |
| 2365 runSteps: runSteps, |
| 2366 waitForComposite: waitForComposite, |
| 2367 |
| 2368 // fullscreen api |
| 2369 setupFullscreen: setupFullscreen, |
| 2370 |
| 2371 none: false |
| 2372 }; |
| 2373 |
| 2374 }()); |
OLD | NEW |