1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/canvas/test/webgl-conformance/conformance/resources/webgl-test.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,805 @@ 1.4 +/* 1.5 +Copyright (C) 2011 Apple Computer, Inc. All rights reserved. 1.6 + 1.7 +Redistribution and use in source and binary forms, with or without 1.8 +modification, are permitted provided that the following conditions 1.9 +are met: 1.10 +1. Redistributions of source code must retain the above copyright 1.11 + notice, this list of conditions and the following disclaimer. 1.12 +2. Redistributions in binary form must reproduce the above copyright 1.13 + notice, this list of conditions and the following disclaimer in the 1.14 + documentation and/or other materials provided with the distribution. 1.15 + 1.16 +THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY 1.17 +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1.18 +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 1.19 +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR 1.20 +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 1.21 +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 1.22 +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 1.23 +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 1.24 +OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 1.25 +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 1.26 +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 1.27 +*/ 1.28 + 1.29 +function webglTestLog(msg) { 1.30 + if (window.console && window.console.log) { 1.31 + window.console.log(msg); 1.32 + } 1.33 + if (document.getElementById("console")) { 1.34 + var log = document.getElementById("console"); 1.35 + log.innerHTML += msg + "<br>"; 1.36 + } 1.37 +} 1.38 + 1.39 +// 1.40 +// create3DContext 1.41 +// 1.42 +// Returns the WebGLRenderingContext for any known implementation. 1.43 +// 1.44 +function create3DContext(canvas, attributes) 1.45 +{ 1.46 + if (!canvas) 1.47 + canvas = document.createElement("canvas"); 1.48 + var names = ["webgl", "experimental-webgl"]; 1.49 + var context = null; 1.50 + for (var i = 0; i < names.length; ++i) { 1.51 + try { 1.52 + context = canvas.getContext(names[i], attributes); 1.53 + } catch (e) { 1.54 + } 1.55 + if (context) { 1.56 + break; 1.57 + } 1.58 + } 1.59 + if (!context) { 1.60 + throw "Unable to fetch WebGL rendering context for Canvas"; 1.61 + } 1.62 + return context; 1.63 +} 1.64 + 1.65 +function createGLErrorWrapper(context, fname) { 1.66 + return function() { 1.67 + var rv = context[fname].apply(context, arguments); 1.68 + var err = context.getError(); 1.69 + if (err != 0) 1.70 + throw "GL error " + err + " in " + fname; 1.71 + return rv; 1.72 + }; 1.73 +} 1.74 + 1.75 +function create3DContextWithWrapperThatThrowsOnGLError(canvas, attributes) { 1.76 + var context = create3DContext(canvas, attributes); 1.77 + // Thanks to Ilmari Heikkinen for the idea on how to implement this so elegantly. 1.78 + var wrap = {}; 1.79 + for (var i in context) { 1.80 + try { 1.81 + if (typeof context[i] == 'function') { 1.82 + wrap[i] = createGLErrorWrapper(context, i); 1.83 + } else { 1.84 + wrap[i] = context[i]; 1.85 + } 1.86 + } catch (e) { 1.87 + webglTestLog("createContextWrapperThatThrowsOnGLError: Error accessing " + i); 1.88 + } 1.89 + } 1.90 + wrap.getError = function() { 1.91 + return context.getError(); 1.92 + }; 1.93 + return wrap; 1.94 +} 1.95 + 1.96 +function getGLErrorAsString(ctx, err) { 1.97 + if (err === ctx.NO_ERROR) { 1.98 + return "NO_ERROR"; 1.99 + } 1.100 + for (var name in ctx) { 1.101 + if (ctx[name] === err) { 1.102 + return name; 1.103 + } 1.104 + } 1.105 + return "0x" + err.toString(16); 1.106 +} 1.107 + 1.108 +// Pass undefined for glError to test that it at least throws some error 1.109 +function shouldGenerateGLError(ctx, glErrors, evalStr) { 1.110 + if (!glErrors.length) { 1.111 + glErrors = [glErrors]; 1.112 + } 1.113 + var exception; 1.114 + try { 1.115 + eval(evalStr); 1.116 + } catch (e) { 1.117 + exception = e; 1.118 + } 1.119 + if (exception) { 1.120 + testFailed(evalStr + " threw exception " + exception); 1.121 + } else { 1.122 + var err = ctx.getError(); 1.123 + if (glErrors.indexOf(err) < 0) { 1.124 + var errStrs = []; 1.125 + for (var ii = 0; ii < glErrors.length; ++ii) { 1.126 + errStrs.push(getGLErrorAsString(ctx, glErrors[ii])); 1.127 + } 1.128 + testFailed(evalStr + " expected: " + errStrs.join(" or ") + ". Was " + getGLErrorAsString(ctx, err) + "."); 1.129 + } else { 1.130 + testPassed(evalStr + " generated expected GL error: " + getGLErrorAsString(ctx, err) + "."); 1.131 + } 1.132 + } 1.133 +} 1.134 + 1.135 +/** 1.136 + * Tests that the first error GL returns is the specified error. 1.137 + * @param {!WebGLContext} gl The WebGLContext to use. 1.138 + * @param {number|!Array.<number>} glError The expected gl 1.139 + * error. Multiple errors can be passed in using an 1.140 + * array. 1.141 + * @param {string} opt_msg Optional additional message. 1.142 + */ 1.143 +function glErrorShouldBe(gl, glErrors, opt_msg) { 1.144 + if (!glErrors.length) { 1.145 + glErrors = [glErrors]; 1.146 + } 1.147 + opt_msg = opt_msg || ""; 1.148 + var err = gl.getError(); 1.149 + var ndx = glErrors.indexOf(err); 1.150 + if (ndx < 0) { 1.151 + if (glErrors.length == 1) { 1.152 + testFailed("getError expected: " + getGLErrorAsString(gl, glErrors[0]) + 1.153 + ". Was " + getGLErrorAsString(gl, err) + " : " + opt_msg); 1.154 + } else { 1.155 + var errs = []; 1.156 + for (var ii = 0; ii < glErrors.length; ++ii) { 1.157 + errs.push(getGLErrorAsString(gl, glErrors[ii])); 1.158 + } 1.159 + testFailed("getError expected one of: [" + errs.join(", ") + 1.160 + "]. Was " + getGLErrorAsString(gl, err) + " : " + opt_msg); 1.161 + } 1.162 + } else { 1.163 + testPassed("getError was expected value: " + 1.164 + getGLErrorAsString(gl, err) + " : " + opt_msg); 1.165 + } 1.166 +}; 1.167 + 1.168 +// 1.169 +// createProgram 1.170 +// 1.171 +// Create and return a program object, attaching each of the given shaders. 1.172 +// 1.173 +// If attribs are given, bind an attrib with that name at that index. 1.174 +// 1.175 +function createProgram(gl, vshaders, fshaders, attribs) 1.176 +{ 1.177 + if (typeof(vshaders) == "string") 1.178 + vshaders = [vshaders]; 1.179 + if (typeof(fshaders) == "string") 1.180 + fshaders = [fshaders]; 1.181 + 1.182 + var shaders = []; 1.183 + var i; 1.184 + 1.185 + for (i = 0; i < vshaders.length; ++i) { 1.186 + var shader = loadShader(gl, vshaders[i], gl.VERTEX_SHADER); 1.187 + if (!shader) 1.188 + return null; 1.189 + shaders.push(shader); 1.190 + } 1.191 + 1.192 + for (i = 0; i < fshaders.length; ++i) { 1.193 + var shader = loadShader(gl, fshaders[i], gl.FRAGMENT_SHADER); 1.194 + if (!shader) 1.195 + return null; 1.196 + shaders.push(shader); 1.197 + } 1.198 + 1.199 + var prog = gl.createProgram(); 1.200 + for (i = 0; i < shaders.length; ++i) { 1.201 + gl.attachShader(prog, shaders[i]); 1.202 + } 1.203 + 1.204 + if (attribs) { 1.205 + for (var i = 0; i < attribs.length; ++i) { 1.206 + gl.bindAttribLocation(prog, i, attribs[i]); 1.207 + } 1.208 + } 1.209 + 1.210 + gl.linkProgram(prog); 1.211 + 1.212 + // Check the link status 1.213 + var linked = gl.getProgramParameter(prog, gl.LINK_STATUS); 1.214 + if (!linked) { 1.215 + // something went wrong with the link 1.216 + var error = gl.getProgramInfoLog(prog); 1.217 + webglTestLog("Error in program linking:" + error); 1.218 + 1.219 + gl.deleteProgram(prog); 1.220 + for (i = 0; i < shaders.length; ++i) 1.221 + gl.deleteShader(shaders[i]); 1.222 + return null; 1.223 + } 1.224 + 1.225 + return prog; 1.226 +} 1.227 + 1.228 +// 1.229 +// initWebGL 1.230 +// 1.231 +// Initialize the Canvas element with the passed name as a WebGL object and return the 1.232 +// WebGLRenderingContext. 1.233 +// 1.234 +// Load shaders with the passed names and create a program with them. Return this program 1.235 +// in the 'program' property of the returned context. 1.236 +// 1.237 +// For each string in the passed attribs array, bind an attrib with that name at that index. 1.238 +// Once the attribs are bound, link the program and then use it. 1.239 +// 1.240 +// Set the clear color to the passed array (4 values) and set the clear depth to the passed value. 1.241 +// Enable depth testing and blending with a blend func of (SRC_ALPHA, ONE_MINUS_SRC_ALPHA) 1.242 +// 1.243 +function initWebGL(canvasName, vshader, fshader, attribs, clearColor, clearDepth, contextAttribs) 1.244 +{ 1.245 + var canvas = document.getElementById(canvasName); 1.246 + var gl = create3DContext(canvas, contextAttribs); 1.247 + if (!gl) { 1.248 + alert("No WebGL context found"); 1.249 + return null; 1.250 + } 1.251 + 1.252 + // Create the program object 1.253 + gl.program = createProgram(gl, vshader, fshader, attribs); 1.254 + if (!gl.program) 1.255 + return null; 1.256 + 1.257 + gl.useProgram(gl.program); 1.258 + 1.259 + gl.clearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]); 1.260 + gl.clearDepth(clearDepth); 1.261 + 1.262 + gl.enable(gl.DEPTH_TEST); 1.263 + gl.enable(gl.BLEND); 1.264 + gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); 1.265 + 1.266 + return gl; 1.267 +} 1.268 + 1.269 +// 1.270 +// getShaderSource 1.271 +// 1.272 +// Load the source from the passed shader file. 1.273 +// 1.274 +function getShaderSource(file) 1.275 +{ 1.276 + var xhr = new XMLHttpRequest(); 1.277 + xhr.open("GET", file, false); 1.278 + xhr.send(); 1.279 + return xhr.responseText; 1.280 +} 1.281 + 1.282 + 1.283 +// 1.284 +// loadShader 1.285 +// 1.286 +// 'shader' is either the id of a <script> element containing the shader source 1.287 +// string, the shader string itself, or the URL of a file containing the shader 1.288 +// source. Load this shader and return the WebGLShader object corresponding to 1.289 +// it. 1.290 +// 1.291 +function loadShader(ctx, shaderId, shaderType, isFile) 1.292 +{ 1.293 + var shaderSource = ""; 1.294 + 1.295 + if (isFile) 1.296 + shaderSource = getShaderSource(shaderId); 1.297 + else { 1.298 + var shaderScript = document.getElementById(shaderId); 1.299 + if (!shaderScript) { 1.300 + shaderSource = shaderId; 1.301 + } else { 1.302 + if (shaderScript.type == "x-shader/x-vertex") { 1.303 + shaderType = ctx.VERTEX_SHADER; 1.304 + } else if (shaderScript.type == "x-shader/x-fragment") { 1.305 + shaderType = ctx.FRAGMENT_SHADER; 1.306 + } else if (shaderType != ctx.VERTEX_SHADER && shaderType != ctx.FRAGMENT_SHADER) { 1.307 + webglTestLog("*** Error: unknown shader type"); 1.308 + return null; 1.309 + } 1.310 + 1.311 + shaderSource = shaderScript.text; 1.312 + } 1.313 + } 1.314 + 1.315 + // Create the shader object 1.316 + var shader = ctx.createShader(shaderType); 1.317 + if (shader == null) { 1.318 + webglTestLog("*** Error: unable to create shader '"+shaderId+"'"); 1.319 + return null; 1.320 + } 1.321 + 1.322 + // Load the shader source 1.323 + ctx.shaderSource(shader, shaderSource); 1.324 + 1.325 + // Compile the shader 1.326 + ctx.compileShader(shader); 1.327 + 1.328 + // Check the compile status 1.329 + var compiled = ctx.getShaderParameter(shader, ctx.COMPILE_STATUS); 1.330 + if (!compiled) { 1.331 + // Something went wrong during compilation; get the error 1.332 + var error = ctx.getShaderInfoLog(shader); 1.333 + webglTestLog("*** Error compiling shader '"+shader+"':"+error); 1.334 + ctx.deleteShader(shader); 1.335 + return null; 1.336 + } 1.337 + 1.338 + return shader; 1.339 +} 1.340 + 1.341 +function loadShaderFromFile(ctx, file, type) 1.342 +{ 1.343 + return loadShader(ctx, file, type, true); 1.344 +} 1.345 + 1.346 +function loadShaderFromScript(ctx, script) 1.347 +{ 1.348 + return loadShader(ctx, script, 0, false); 1.349 +} 1.350 + 1.351 +function loadStandardProgram(context) { 1.352 + var program = context.createProgram(); 1.353 + context.attachShader(program, loadStandardVertexShader(context)); 1.354 + context.attachShader(program, loadStandardFragmentShader(context)); 1.355 + context.linkProgram(program); 1.356 + return program; 1.357 +} 1.358 + 1.359 +function loadProgram(context, vertexShaderPath, fragmentShaderPath, isFile) { 1.360 + isFile = (isFile === undefined) ? true : isFile; 1.361 + var program = context.createProgram(); 1.362 + context.attachShader(program, loadShader(context, vertexShaderPath, context.VERTEX_SHADER, isFile)); 1.363 + context.attachShader(program, loadShader(context, fragmentShaderPath, context.FRAGMENT_SHADER, isFile)); 1.364 + context.linkProgram(program); 1.365 + return program; 1.366 +} 1.367 + 1.368 +var getBasePathForResources = function() { 1.369 + var expectedBase = "webgl-test.js"; 1.370 + var scripts = document.getElementsByTagName('script'); 1.371 + for (var script, i = 0; script = scripts[i]; i++) { 1.372 + var src = script.src; 1.373 + var l = src.length; 1.374 + if (src.substr(l - expectedBase.length) == expectedBase) { 1.375 + return src.substr(0, l - expectedBase.length); 1.376 + } 1.377 + } 1.378 + throw 'oops'; 1.379 +}; 1.380 + 1.381 + 1.382 +function loadStandardVertexShader(context) { 1.383 + return loadShader( 1.384 + context, 1.385 + getBasePathForResources() + "vertexShader.vert", 1.386 + context.VERTEX_SHADER, 1.387 + true); 1.388 +} 1.389 + 1.390 +function loadStandardFragmentShader(context) { 1.391 + return loadShader( 1.392 + context, 1.393 + getBasePathForResources() + "fragmentShader.frag", 1.394 + context.FRAGMENT_SHADER, 1.395 + true); 1.396 +} 1.397 + 1.398 +// 1.399 +// makeBox 1.400 +// 1.401 +// Create a box with vertices, normals and texCoords. Create VBOs for each as well as the index array. 1.402 +// Return an object with the following properties: 1.403 +// 1.404 +// normalObject WebGLBuffer object for normals 1.405 +// texCoordObject WebGLBuffer object for texCoords 1.406 +// vertexObject WebGLBuffer object for vertices 1.407 +// indexObject WebGLBuffer object for indices 1.408 +// numIndices The number of indices in the indexObject 1.409 +// 1.410 +function makeBox(ctx) 1.411 +{ 1.412 + // box 1.413 + // v6----- v5 1.414 + // /| /| 1.415 + // v1------v0| 1.416 + // | | | | 1.417 + // | |v7---|-|v4 1.418 + // |/ |/ 1.419 + // v2------v3 1.420 + // 1.421 + // vertex coords array 1.422 + var vertices = new Float32Array( 1.423 + [ 1, 1, 1, -1, 1, 1, -1,-1, 1, 1,-1, 1, // v0-v1-v2-v3 front 1.424 + 1, 1, 1, 1,-1, 1, 1,-1,-1, 1, 1,-1, // v0-v3-v4-v5 right 1.425 + 1, 1, 1, 1, 1,-1, -1, 1,-1, -1, 1, 1, // v0-v5-v6-v1 top 1.426 + -1, 1, 1, -1, 1,-1, -1,-1,-1, -1,-1, 1, // v1-v6-v7-v2 left 1.427 + -1,-1,-1, 1,-1,-1, 1,-1, 1, -1,-1, 1, // v7-v4-v3-v2 bottom 1.428 + 1,-1,-1, -1,-1,-1, -1, 1,-1, 1, 1,-1 ] // v4-v7-v6-v5 back 1.429 + ); 1.430 + 1.431 + // normal array 1.432 + var normals = new Float32Array( 1.433 + [ 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, // v0-v1-v2-v3 front 1.434 + 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, // v0-v3-v4-v5 right 1.435 + 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, // v0-v5-v6-v1 top 1.436 + -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, // v1-v6-v7-v2 left 1.437 + 0,-1, 0, 0,-1, 0, 0,-1, 0, 0,-1, 0, // v7-v4-v3-v2 bottom 1.438 + 0, 0,-1, 0, 0,-1, 0, 0,-1, 0, 0,-1 ] // v4-v7-v6-v5 back 1.439 + ); 1.440 + 1.441 + 1.442 + // texCoord array 1.443 + var texCoords = new Float32Array( 1.444 + [ 1, 1, 0, 1, 0, 0, 1, 0, // v0-v1-v2-v3 front 1.445 + 0, 1, 0, 0, 1, 0, 1, 1, // v0-v3-v4-v5 right 1.446 + 1, 0, 1, 1, 0, 1, 0, 0, // v0-v5-v6-v1 top 1.447 + 1, 1, 0, 1, 0, 0, 1, 0, // v1-v6-v7-v2 left 1.448 + 0, 0, 1, 0, 1, 1, 0, 1, // v7-v4-v3-v2 bottom 1.449 + 0, 0, 1, 0, 1, 1, 0, 1 ] // v4-v7-v6-v5 back 1.450 + ); 1.451 + 1.452 + // index array 1.453 + var indices = new Uint8Array( 1.454 + [ 0, 1, 2, 0, 2, 3, // front 1.455 + 4, 5, 6, 4, 6, 7, // right 1.456 + 8, 9,10, 8,10,11, // top 1.457 + 12,13,14, 12,14,15, // left 1.458 + 16,17,18, 16,18,19, // bottom 1.459 + 20,21,22, 20,22,23 ] // back 1.460 + ); 1.461 + 1.462 + var retval = { }; 1.463 + 1.464 + retval.normalObject = ctx.createBuffer(); 1.465 + ctx.bindBuffer(ctx.ARRAY_BUFFER, retval.normalObject); 1.466 + ctx.bufferData(ctx.ARRAY_BUFFER, normals, ctx.STATIC_DRAW); 1.467 + 1.468 + retval.texCoordObject = ctx.createBuffer(); 1.469 + ctx.bindBuffer(ctx.ARRAY_BUFFER, retval.texCoordObject); 1.470 + ctx.bufferData(ctx.ARRAY_BUFFER, texCoords, ctx.STATIC_DRAW); 1.471 + 1.472 + retval.vertexObject = ctx.createBuffer(); 1.473 + ctx.bindBuffer(ctx.ARRAY_BUFFER, retval.vertexObject); 1.474 + ctx.bufferData(ctx.ARRAY_BUFFER, vertices, ctx.STATIC_DRAW); 1.475 + 1.476 + ctx.bindBuffer(ctx.ARRAY_BUFFER, 0); 1.477 + 1.478 + retval.indexObject = ctx.createBuffer(); 1.479 + ctx.bindBuffer(ctx.ELEMENT_ARRAY_BUFFER, retval.indexObject); 1.480 + ctx.bufferData(ctx.ELEMENT_ARRAY_BUFFER, indices, ctx.STATIC_DRAW); 1.481 + ctx.bindBuffer(ctx.ELEMENT_ARRAY_BUFFER, 0); 1.482 + 1.483 + retval.numIndices = indices.length; 1.484 + 1.485 + return retval; 1.486 +} 1.487 + 1.488 +// 1.489 +// makeSphere 1.490 +// 1.491 +// Create a sphere with the passed number of latitude and longitude bands and the passed radius. 1.492 +// Sphere has vertices, normals and texCoords. Create VBOs for each as well as the index array. 1.493 +// Return an object with the following properties: 1.494 +// 1.495 +// normalObject WebGLBuffer object for normals 1.496 +// texCoordObject WebGLBuffer object for texCoords 1.497 +// vertexObject WebGLBuffer object for vertices 1.498 +// indexObject WebGLBuffer object for indices 1.499 +// numIndices The number of indices in the indexObject 1.500 +// 1.501 +function makeSphere(ctx, radius, lats, longs) 1.502 +{ 1.503 + var geometryData = [ ]; 1.504 + var normalData = [ ]; 1.505 + var texCoordData = [ ]; 1.506 + var indexData = [ ]; 1.507 + 1.508 + for (var latNumber = 0; latNumber <= lats; ++latNumber) { 1.509 + for (var longNumber = 0; longNumber <= longs; ++longNumber) { 1.510 + var theta = latNumber * Math.PI / lats; 1.511 + var phi = longNumber * 2 * Math.PI / longs; 1.512 + var sinTheta = Math.sin(theta); 1.513 + var sinPhi = Math.sin(phi); 1.514 + var cosTheta = Math.cos(theta); 1.515 + var cosPhi = Math.cos(phi); 1.516 + 1.517 + var x = cosPhi * sinTheta; 1.518 + var y = cosTheta; 1.519 + var z = sinPhi * sinTheta; 1.520 + var u = 1-(longNumber/longs); 1.521 + var v = latNumber/lats; 1.522 + 1.523 + normalData.push(x); 1.524 + normalData.push(y); 1.525 + normalData.push(z); 1.526 + texCoordData.push(u); 1.527 + texCoordData.push(v); 1.528 + geometryData.push(radius * x); 1.529 + geometryData.push(radius * y); 1.530 + geometryData.push(radius * z); 1.531 + } 1.532 + } 1.533 + 1.534 + longs += 1; 1.535 + for (var latNumber = 0; latNumber < lats; ++latNumber) { 1.536 + for (var longNumber = 0; longNumber < longs; ++longNumber) { 1.537 + var first = (latNumber * longs) + (longNumber % longs); 1.538 + var second = first + longs; 1.539 + indexData.push(first); 1.540 + indexData.push(second); 1.541 + indexData.push(first+1); 1.542 + 1.543 + indexData.push(second); 1.544 + indexData.push(second+1); 1.545 + indexData.push(first+1); 1.546 + } 1.547 + } 1.548 + 1.549 + var retval = { }; 1.550 + 1.551 + retval.normalObject = ctx.createBuffer(); 1.552 + ctx.bindBuffer(ctx.ARRAY_BUFFER, retval.normalObject); 1.553 + ctx.bufferData(ctx.ARRAY_BUFFER, new Float32Array(normalData), ctx.STATIC_DRAW); 1.554 + 1.555 + retval.texCoordObject = ctx.createBuffer(); 1.556 + ctx.bindBuffer(ctx.ARRAY_BUFFER, retval.texCoordObject); 1.557 + ctx.bufferData(ctx.ARRAY_BUFFER, new Float32Array(texCoordData), ctx.STATIC_DRAW); 1.558 + 1.559 + retval.vertexObject = ctx.createBuffer(); 1.560 + ctx.bindBuffer(ctx.ARRAY_BUFFER, retval.vertexObject); 1.561 + ctx.bufferData(ctx.ARRAY_BUFFER, new Float32Array(geometryData), ctx.STATIC_DRAW); 1.562 + 1.563 + retval.numIndices = indexData.length; 1.564 + retval.indexObject = ctx.createBuffer(); 1.565 + ctx.bindBuffer(ctx.ELEMENT_ARRAY_BUFFER, retval.indexObject); 1.566 + ctx.bufferData(ctx.ELEMENT_ARRAY_BUFFER, new Uint16Array(indexData), ctx.STREAM_DRAW); 1.567 + 1.568 + return retval; 1.569 +} 1.570 + 1.571 +// 1.572 +// loadObj 1.573 +// 1.574 +// Load a .obj file from the passed URL. Return an object with a 'loaded' property set to false. 1.575 +// When the object load is complete, the 'loaded' property becomes true and the following 1.576 +// properties are set: 1.577 +// 1.578 +// normalObject WebGLBuffer object for normals 1.579 +// texCoordObject WebGLBuffer object for texCoords 1.580 +// vertexObject WebGLBuffer object for vertices 1.581 +// indexObject WebGLBuffer object for indices 1.582 +// numIndices The number of indices in the indexObject 1.583 +// 1.584 +function loadObj(ctx, url) 1.585 +{ 1.586 + var obj = { loaded : false }; 1.587 + obj.ctx = ctx; 1.588 + var req = new XMLHttpRequest(); 1.589 + req.obj = obj; 1.590 + req.onreadystatechange = function () { processLoadObj(req) }; 1.591 + req.open("GET", url, true); 1.592 + req.send(null); 1.593 + return obj; 1.594 +} 1.595 + 1.596 +function processLoadObj(req) 1.597 +{ 1.598 + webglTestLog("req="+req) 1.599 + // only if req shows "complete" 1.600 + if (req.readyState == 4) { 1.601 + doLoadObj(req.obj, req.responseText); 1.602 + } 1.603 +} 1.604 + 1.605 +function doLoadObj(obj, text) 1.606 +{ 1.607 + vertexArray = [ ]; 1.608 + normalArray = [ ]; 1.609 + textureArray = [ ]; 1.610 + indexArray = [ ]; 1.611 + 1.612 + var vertex = [ ]; 1.613 + var normal = [ ]; 1.614 + var texture = [ ]; 1.615 + var facemap = { }; 1.616 + var index = 0; 1.617 + 1.618 + var lines = text.split("\n"); 1.619 + for (var lineIndex in lines) { 1.620 + var line = lines[lineIndex].replace(/[ \t]+/g, " ").replace(/\s\s*$/, ""); 1.621 + 1.622 + // ignore comments 1.623 + if (line[0] == "#") 1.624 + continue; 1.625 + 1.626 + var array = line.split(" "); 1.627 + if (array[0] == "v") { 1.628 + // vertex 1.629 + vertex.push(parseFloat(array[1])); 1.630 + vertex.push(parseFloat(array[2])); 1.631 + vertex.push(parseFloat(array[3])); 1.632 + } 1.633 + else if (array[0] == "vt") { 1.634 + // normal 1.635 + texture.push(parseFloat(array[1])); 1.636 + texture.push(parseFloat(array[2])); 1.637 + } 1.638 + else if (array[0] == "vn") { 1.639 + // normal 1.640 + normal.push(parseFloat(array[1])); 1.641 + normal.push(parseFloat(array[2])); 1.642 + normal.push(parseFloat(array[3])); 1.643 + } 1.644 + else if (array[0] == "f") { 1.645 + // face 1.646 + if (array.length != 4) { 1.647 + webglTestLog("*** Error: face '"+line+"' not handled"); 1.648 + continue; 1.649 + } 1.650 + 1.651 + for (var i = 1; i < 4; ++i) { 1.652 + if (!(array[i] in facemap)) { 1.653 + // add a new entry to the map and arrays 1.654 + var f = array[i].split("/"); 1.655 + var vtx, nor, tex; 1.656 + 1.657 + if (f.length == 1) { 1.658 + vtx = parseInt(f[0]) - 1; 1.659 + nor = vtx; 1.660 + tex = vtx; 1.661 + } 1.662 + else if (f.length = 3) { 1.663 + vtx = parseInt(f[0]) - 1; 1.664 + tex = parseInt(f[1]) - 1; 1.665 + nor = parseInt(f[2]) - 1; 1.666 + } 1.667 + else { 1.668 + webglTestLog("*** Error: did not understand face '"+array[i]+"'"); 1.669 + return null; 1.670 + } 1.671 + 1.672 + // do the vertices 1.673 + var x = 0; 1.674 + var y = 0; 1.675 + var z = 0; 1.676 + if (vtx * 3 + 2 < vertex.length) { 1.677 + x = vertex[vtx*3]; 1.678 + y = vertex[vtx*3+1]; 1.679 + z = vertex[vtx*3+2]; 1.680 + } 1.681 + vertexArray.push(x); 1.682 + vertexArray.push(y); 1.683 + vertexArray.push(z); 1.684 + 1.685 + // do the textures 1.686 + x = 0; 1.687 + y = 0; 1.688 + if (tex * 2 + 1 < texture.length) { 1.689 + x = texture[tex*2]; 1.690 + y = texture[tex*2+1]; 1.691 + } 1.692 + textureArray.push(x); 1.693 + textureArray.push(y); 1.694 + 1.695 + // do the normals 1.696 + x = 0; 1.697 + y = 0; 1.698 + z = 1; 1.699 + if (nor * 3 + 2 < normal.length) { 1.700 + x = normal[nor*3]; 1.701 + y = normal[nor*3+1]; 1.702 + z = normal[nor*3+2]; 1.703 + } 1.704 + normalArray.push(x); 1.705 + normalArray.push(y); 1.706 + normalArray.push(z); 1.707 + 1.708 + facemap[array[i]] = index++; 1.709 + } 1.710 + 1.711 + indexArray.push(facemap[array[i]]); 1.712 + } 1.713 + } 1.714 + } 1.715 + 1.716 + // set the VBOs 1.717 + obj.normalObject = obj.ctx.createBuffer(); 1.718 + obj.ctx.bindBuffer(obj.ctx.ARRAY_BUFFER, obj.normalObject); 1.719 + obj.ctx.bufferData(obj.ctx.ARRAY_BUFFER, new Float32Array(normalArray), obj.ctx.STATIC_DRAW); 1.720 + 1.721 + obj.texCoordObject = obj.ctx.createBuffer(); 1.722 + obj.ctx.bindBuffer(obj.ctx.ARRAY_BUFFER, obj.texCoordObject); 1.723 + obj.ctx.bufferData(obj.ctx.ARRAY_BUFFER, new Float32Array(textureArray), obj.ctx.STATIC_DRAW); 1.724 + 1.725 + obj.vertexObject = obj.ctx.createBuffer(); 1.726 + obj.ctx.bindBuffer(obj.ctx.ARRAY_BUFFER, obj.vertexObject); 1.727 + obj.ctx.bufferData(obj.ctx.ARRAY_BUFFER, new Float32Array(vertexArray), obj.ctx.STATIC_DRAW); 1.728 + 1.729 + obj.numIndices = indexArray.length; 1.730 + obj.indexObject = obj.ctx.createBuffer(); 1.731 + obj.ctx.bindBuffer(obj.ctx.ELEMENT_ARRAY_BUFFER, obj.indexObject); 1.732 + obj.ctx.bufferData(obj.ctx.ELEMENT_ARRAY_BUFFER, new Uint16Array(indexArray), obj.ctx.STREAM_DRAW); 1.733 + 1.734 + obj.loaded = true; 1.735 +} 1.736 + 1.737 +// 1.738 +// loadImageTexture 1.739 +// 1.740 +// Load the image at the passed url, place it in a new WebGLTexture object and return the WebGLTexture. 1.741 +// 1.742 +function loadImageTexture(ctx, url) 1.743 +{ 1.744 + var texture = ctx.createTexture(); 1.745 + texture.image = new Image(); 1.746 + texture.image.onload = function() { doLoadImageTexture(ctx, texture.image, texture) } 1.747 + texture.image.src = url; 1.748 + return texture; 1.749 +} 1.750 + 1.751 +function doLoadImageTexture(ctx, image, texture) 1.752 +{ 1.753 + ctx.enable(ctx.TEXTURE_2D); 1.754 + ctx.bindTexture(ctx.TEXTURE_2D, texture); 1.755 + ctx.texImage2D(ctx.TEXTURE_2D, 0, ctx.RGBA, ctx.RGBA, ctx.UNSIGNED_BYTE, image); 1.756 + ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_MAG_FILTER, ctx.LINEAR); 1.757 + ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_MIN_FILTER, ctx.LINEAR_MIPMAP_LINEAR); 1.758 + ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_WRAP_S, ctx.CLAMP_TO_EDGE); 1.759 + ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_WRAP_T, ctx.CLAMP_TO_EDGE); 1.760 + ctx.generateMipmap(ctx.TEXTURE_2D) 1.761 + ctx.bindTexture(ctx.TEXTURE_2D, 0); 1.762 +} 1.763 + 1.764 +// 1.765 +// Framerate object 1.766 +// 1.767 +// This object keeps track of framerate and displays it as the innerHTML text of the 1.768 +// HTML element with the passed id. Once created you call snapshot at the end 1.769 +// of every rendering cycle. Every 500ms the framerate is updated in the HTML element. 1.770 +// 1.771 +Framerate = function(id) 1.772 +{ 1.773 + this.numFramerates = 10; 1.774 + this.framerateUpdateInterval = 500; 1.775 + this.id = id; 1.776 + 1.777 + this.renderTime = -1; 1.778 + this.framerates = [ ]; 1.779 + self = this; 1.780 + var fr = function() { self.updateFramerate() } 1.781 + setInterval(fr, this.framerateUpdateInterval); 1.782 +} 1.783 + 1.784 +Framerate.prototype.updateFramerate = function() 1.785 +{ 1.786 + var tot = 0; 1.787 + for (var i = 0; i < this.framerates.length; ++i) 1.788 + tot += this.framerates[i]; 1.789 + 1.790 + var framerate = tot / this.framerates.length; 1.791 + framerate = Math.round(framerate); 1.792 + document.getElementById(this.id).innerHTML = "Framerate:"+framerate+"fps"; 1.793 +} 1.794 + 1.795 +Framerate.prototype.snapshot = function() 1.796 +{ 1.797 + if (this.renderTime < 0) 1.798 + this.renderTime = new Date().getTime(); 1.799 + else { 1.800 + var newTime = new Date().getTime(); 1.801 + var t = newTime - this.renderTime; 1.802 + var framerate = 1000/t; 1.803 + this.framerates.push(framerate); 1.804 + while (this.framerates.length > this.numFramerates) 1.805 + this.framerates.shift(); 1.806 + this.renderTime = newTime; 1.807 + } 1.808 +}