content/canvas/test/webgl-conformance/conformance/resources/webgl-test.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /*
michael@0 2 Copyright (C) 2011 Apple Computer, Inc. All rights reserved.
michael@0 3
michael@0 4 Redistribution and use in source and binary forms, with or without
michael@0 5 modification, are permitted provided that the following conditions
michael@0 6 are met:
michael@0 7 1. Redistributions of source code must retain the above copyright
michael@0 8 notice, this list of conditions and the following disclaimer.
michael@0 9 2. Redistributions in binary form must reproduce the above copyright
michael@0 10 notice, this list of conditions and the following disclaimer in the
michael@0 11 documentation and/or other materials provided with the distribution.
michael@0 12
michael@0 13 THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
michael@0 14 EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
michael@0 15 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
michael@0 16 PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
michael@0 17 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
michael@0 18 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
michael@0 19 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
michael@0 20 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
michael@0 21 OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
michael@0 22 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
michael@0 23 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
michael@0 24 */
michael@0 25
michael@0 26 function webglTestLog(msg) {
michael@0 27 if (window.console && window.console.log) {
michael@0 28 window.console.log(msg);
michael@0 29 }
michael@0 30 if (document.getElementById("console")) {
michael@0 31 var log = document.getElementById("console");
michael@0 32 log.innerHTML += msg + "<br>";
michael@0 33 }
michael@0 34 }
michael@0 35
michael@0 36 //
michael@0 37 // create3DContext
michael@0 38 //
michael@0 39 // Returns the WebGLRenderingContext for any known implementation.
michael@0 40 //
michael@0 41 function create3DContext(canvas, attributes)
michael@0 42 {
michael@0 43 if (!canvas)
michael@0 44 canvas = document.createElement("canvas");
michael@0 45 var names = ["webgl", "experimental-webgl"];
michael@0 46 var context = null;
michael@0 47 for (var i = 0; i < names.length; ++i) {
michael@0 48 try {
michael@0 49 context = canvas.getContext(names[i], attributes);
michael@0 50 } catch (e) {
michael@0 51 }
michael@0 52 if (context) {
michael@0 53 break;
michael@0 54 }
michael@0 55 }
michael@0 56 if (!context) {
michael@0 57 throw "Unable to fetch WebGL rendering context for Canvas";
michael@0 58 }
michael@0 59 return context;
michael@0 60 }
michael@0 61
michael@0 62 function createGLErrorWrapper(context, fname) {
michael@0 63 return function() {
michael@0 64 var rv = context[fname].apply(context, arguments);
michael@0 65 var err = context.getError();
michael@0 66 if (err != 0)
michael@0 67 throw "GL error " + err + " in " + fname;
michael@0 68 return rv;
michael@0 69 };
michael@0 70 }
michael@0 71
michael@0 72 function create3DContextWithWrapperThatThrowsOnGLError(canvas, attributes) {
michael@0 73 var context = create3DContext(canvas, attributes);
michael@0 74 // Thanks to Ilmari Heikkinen for the idea on how to implement this so elegantly.
michael@0 75 var wrap = {};
michael@0 76 for (var i in context) {
michael@0 77 try {
michael@0 78 if (typeof context[i] == 'function') {
michael@0 79 wrap[i] = createGLErrorWrapper(context, i);
michael@0 80 } else {
michael@0 81 wrap[i] = context[i];
michael@0 82 }
michael@0 83 } catch (e) {
michael@0 84 webglTestLog("createContextWrapperThatThrowsOnGLError: Error accessing " + i);
michael@0 85 }
michael@0 86 }
michael@0 87 wrap.getError = function() {
michael@0 88 return context.getError();
michael@0 89 };
michael@0 90 return wrap;
michael@0 91 }
michael@0 92
michael@0 93 function getGLErrorAsString(ctx, err) {
michael@0 94 if (err === ctx.NO_ERROR) {
michael@0 95 return "NO_ERROR";
michael@0 96 }
michael@0 97 for (var name in ctx) {
michael@0 98 if (ctx[name] === err) {
michael@0 99 return name;
michael@0 100 }
michael@0 101 }
michael@0 102 return "0x" + err.toString(16);
michael@0 103 }
michael@0 104
michael@0 105 // Pass undefined for glError to test that it at least throws some error
michael@0 106 function shouldGenerateGLError(ctx, glErrors, evalStr) {
michael@0 107 if (!glErrors.length) {
michael@0 108 glErrors = [glErrors];
michael@0 109 }
michael@0 110 var exception;
michael@0 111 try {
michael@0 112 eval(evalStr);
michael@0 113 } catch (e) {
michael@0 114 exception = e;
michael@0 115 }
michael@0 116 if (exception) {
michael@0 117 testFailed(evalStr + " threw exception " + exception);
michael@0 118 } else {
michael@0 119 var err = ctx.getError();
michael@0 120 if (glErrors.indexOf(err) < 0) {
michael@0 121 var errStrs = [];
michael@0 122 for (var ii = 0; ii < glErrors.length; ++ii) {
michael@0 123 errStrs.push(getGLErrorAsString(ctx, glErrors[ii]));
michael@0 124 }
michael@0 125 testFailed(evalStr + " expected: " + errStrs.join(" or ") + ". Was " + getGLErrorAsString(ctx, err) + ".");
michael@0 126 } else {
michael@0 127 testPassed(evalStr + " generated expected GL error: " + getGLErrorAsString(ctx, err) + ".");
michael@0 128 }
michael@0 129 }
michael@0 130 }
michael@0 131
michael@0 132 /**
michael@0 133 * Tests that the first error GL returns is the specified error.
michael@0 134 * @param {!WebGLContext} gl The WebGLContext to use.
michael@0 135 * @param {number|!Array.<number>} glError The expected gl
michael@0 136 * error. Multiple errors can be passed in using an
michael@0 137 * array.
michael@0 138 * @param {string} opt_msg Optional additional message.
michael@0 139 */
michael@0 140 function glErrorShouldBe(gl, glErrors, opt_msg) {
michael@0 141 if (!glErrors.length) {
michael@0 142 glErrors = [glErrors];
michael@0 143 }
michael@0 144 opt_msg = opt_msg || "";
michael@0 145 var err = gl.getError();
michael@0 146 var ndx = glErrors.indexOf(err);
michael@0 147 if (ndx < 0) {
michael@0 148 if (glErrors.length == 1) {
michael@0 149 testFailed("getError expected: " + getGLErrorAsString(gl, glErrors[0]) +
michael@0 150 ". Was " + getGLErrorAsString(gl, err) + " : " + opt_msg);
michael@0 151 } else {
michael@0 152 var errs = [];
michael@0 153 for (var ii = 0; ii < glErrors.length; ++ii) {
michael@0 154 errs.push(getGLErrorAsString(gl, glErrors[ii]));
michael@0 155 }
michael@0 156 testFailed("getError expected one of: [" + errs.join(", ") +
michael@0 157 "]. Was " + getGLErrorAsString(gl, err) + " : " + opt_msg);
michael@0 158 }
michael@0 159 } else {
michael@0 160 testPassed("getError was expected value: " +
michael@0 161 getGLErrorAsString(gl, err) + " : " + opt_msg);
michael@0 162 }
michael@0 163 };
michael@0 164
michael@0 165 //
michael@0 166 // createProgram
michael@0 167 //
michael@0 168 // Create and return a program object, attaching each of the given shaders.
michael@0 169 //
michael@0 170 // If attribs are given, bind an attrib with that name at that index.
michael@0 171 //
michael@0 172 function createProgram(gl, vshaders, fshaders, attribs)
michael@0 173 {
michael@0 174 if (typeof(vshaders) == "string")
michael@0 175 vshaders = [vshaders];
michael@0 176 if (typeof(fshaders) == "string")
michael@0 177 fshaders = [fshaders];
michael@0 178
michael@0 179 var shaders = [];
michael@0 180 var i;
michael@0 181
michael@0 182 for (i = 0; i < vshaders.length; ++i) {
michael@0 183 var shader = loadShader(gl, vshaders[i], gl.VERTEX_SHADER);
michael@0 184 if (!shader)
michael@0 185 return null;
michael@0 186 shaders.push(shader);
michael@0 187 }
michael@0 188
michael@0 189 for (i = 0; i < fshaders.length; ++i) {
michael@0 190 var shader = loadShader(gl, fshaders[i], gl.FRAGMENT_SHADER);
michael@0 191 if (!shader)
michael@0 192 return null;
michael@0 193 shaders.push(shader);
michael@0 194 }
michael@0 195
michael@0 196 var prog = gl.createProgram();
michael@0 197 for (i = 0; i < shaders.length; ++i) {
michael@0 198 gl.attachShader(prog, shaders[i]);
michael@0 199 }
michael@0 200
michael@0 201 if (attribs) {
michael@0 202 for (var i = 0; i < attribs.length; ++i) {
michael@0 203 gl.bindAttribLocation(prog, i, attribs[i]);
michael@0 204 }
michael@0 205 }
michael@0 206
michael@0 207 gl.linkProgram(prog);
michael@0 208
michael@0 209 // Check the link status
michael@0 210 var linked = gl.getProgramParameter(prog, gl.LINK_STATUS);
michael@0 211 if (!linked) {
michael@0 212 // something went wrong with the link
michael@0 213 var error = gl.getProgramInfoLog(prog);
michael@0 214 webglTestLog("Error in program linking:" + error);
michael@0 215
michael@0 216 gl.deleteProgram(prog);
michael@0 217 for (i = 0; i < shaders.length; ++i)
michael@0 218 gl.deleteShader(shaders[i]);
michael@0 219 return null;
michael@0 220 }
michael@0 221
michael@0 222 return prog;
michael@0 223 }
michael@0 224
michael@0 225 //
michael@0 226 // initWebGL
michael@0 227 //
michael@0 228 // Initialize the Canvas element with the passed name as a WebGL object and return the
michael@0 229 // WebGLRenderingContext.
michael@0 230 //
michael@0 231 // Load shaders with the passed names and create a program with them. Return this program
michael@0 232 // in the 'program' property of the returned context.
michael@0 233 //
michael@0 234 // For each string in the passed attribs array, bind an attrib with that name at that index.
michael@0 235 // Once the attribs are bound, link the program and then use it.
michael@0 236 //
michael@0 237 // Set the clear color to the passed array (4 values) and set the clear depth to the passed value.
michael@0 238 // Enable depth testing and blending with a blend func of (SRC_ALPHA, ONE_MINUS_SRC_ALPHA)
michael@0 239 //
michael@0 240 function initWebGL(canvasName, vshader, fshader, attribs, clearColor, clearDepth, contextAttribs)
michael@0 241 {
michael@0 242 var canvas = document.getElementById(canvasName);
michael@0 243 var gl = create3DContext(canvas, contextAttribs);
michael@0 244 if (!gl) {
michael@0 245 alert("No WebGL context found");
michael@0 246 return null;
michael@0 247 }
michael@0 248
michael@0 249 // Create the program object
michael@0 250 gl.program = createProgram(gl, vshader, fshader, attribs);
michael@0 251 if (!gl.program)
michael@0 252 return null;
michael@0 253
michael@0 254 gl.useProgram(gl.program);
michael@0 255
michael@0 256 gl.clearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]);
michael@0 257 gl.clearDepth(clearDepth);
michael@0 258
michael@0 259 gl.enable(gl.DEPTH_TEST);
michael@0 260 gl.enable(gl.BLEND);
michael@0 261 gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
michael@0 262
michael@0 263 return gl;
michael@0 264 }
michael@0 265
michael@0 266 //
michael@0 267 // getShaderSource
michael@0 268 //
michael@0 269 // Load the source from the passed shader file.
michael@0 270 //
michael@0 271 function getShaderSource(file)
michael@0 272 {
michael@0 273 var xhr = new XMLHttpRequest();
michael@0 274 xhr.open("GET", file, false);
michael@0 275 xhr.send();
michael@0 276 return xhr.responseText;
michael@0 277 }
michael@0 278
michael@0 279
michael@0 280 //
michael@0 281 // loadShader
michael@0 282 //
michael@0 283 // 'shader' is either the id of a <script> element containing the shader source
michael@0 284 // string, the shader string itself, or the URL of a file containing the shader
michael@0 285 // source. Load this shader and return the WebGLShader object corresponding to
michael@0 286 // it.
michael@0 287 //
michael@0 288 function loadShader(ctx, shaderId, shaderType, isFile)
michael@0 289 {
michael@0 290 var shaderSource = "";
michael@0 291
michael@0 292 if (isFile)
michael@0 293 shaderSource = getShaderSource(shaderId);
michael@0 294 else {
michael@0 295 var shaderScript = document.getElementById(shaderId);
michael@0 296 if (!shaderScript) {
michael@0 297 shaderSource = shaderId;
michael@0 298 } else {
michael@0 299 if (shaderScript.type == "x-shader/x-vertex") {
michael@0 300 shaderType = ctx.VERTEX_SHADER;
michael@0 301 } else if (shaderScript.type == "x-shader/x-fragment") {
michael@0 302 shaderType = ctx.FRAGMENT_SHADER;
michael@0 303 } else if (shaderType != ctx.VERTEX_SHADER && shaderType != ctx.FRAGMENT_SHADER) {
michael@0 304 webglTestLog("*** Error: unknown shader type");
michael@0 305 return null;
michael@0 306 }
michael@0 307
michael@0 308 shaderSource = shaderScript.text;
michael@0 309 }
michael@0 310 }
michael@0 311
michael@0 312 // Create the shader object
michael@0 313 var shader = ctx.createShader(shaderType);
michael@0 314 if (shader == null) {
michael@0 315 webglTestLog("*** Error: unable to create shader '"+shaderId+"'");
michael@0 316 return null;
michael@0 317 }
michael@0 318
michael@0 319 // Load the shader source
michael@0 320 ctx.shaderSource(shader, shaderSource);
michael@0 321
michael@0 322 // Compile the shader
michael@0 323 ctx.compileShader(shader);
michael@0 324
michael@0 325 // Check the compile status
michael@0 326 var compiled = ctx.getShaderParameter(shader, ctx.COMPILE_STATUS);
michael@0 327 if (!compiled) {
michael@0 328 // Something went wrong during compilation; get the error
michael@0 329 var error = ctx.getShaderInfoLog(shader);
michael@0 330 webglTestLog("*** Error compiling shader '"+shader+"':"+error);
michael@0 331 ctx.deleteShader(shader);
michael@0 332 return null;
michael@0 333 }
michael@0 334
michael@0 335 return shader;
michael@0 336 }
michael@0 337
michael@0 338 function loadShaderFromFile(ctx, file, type)
michael@0 339 {
michael@0 340 return loadShader(ctx, file, type, true);
michael@0 341 }
michael@0 342
michael@0 343 function loadShaderFromScript(ctx, script)
michael@0 344 {
michael@0 345 return loadShader(ctx, script, 0, false);
michael@0 346 }
michael@0 347
michael@0 348 function loadStandardProgram(context) {
michael@0 349 var program = context.createProgram();
michael@0 350 context.attachShader(program, loadStandardVertexShader(context));
michael@0 351 context.attachShader(program, loadStandardFragmentShader(context));
michael@0 352 context.linkProgram(program);
michael@0 353 return program;
michael@0 354 }
michael@0 355
michael@0 356 function loadProgram(context, vertexShaderPath, fragmentShaderPath, isFile) {
michael@0 357 isFile = (isFile === undefined) ? true : isFile;
michael@0 358 var program = context.createProgram();
michael@0 359 context.attachShader(program, loadShader(context, vertexShaderPath, context.VERTEX_SHADER, isFile));
michael@0 360 context.attachShader(program, loadShader(context, fragmentShaderPath, context.FRAGMENT_SHADER, isFile));
michael@0 361 context.linkProgram(program);
michael@0 362 return program;
michael@0 363 }
michael@0 364
michael@0 365 var getBasePathForResources = function() {
michael@0 366 var expectedBase = "webgl-test.js";
michael@0 367 var scripts = document.getElementsByTagName('script');
michael@0 368 for (var script, i = 0; script = scripts[i]; i++) {
michael@0 369 var src = script.src;
michael@0 370 var l = src.length;
michael@0 371 if (src.substr(l - expectedBase.length) == expectedBase) {
michael@0 372 return src.substr(0, l - expectedBase.length);
michael@0 373 }
michael@0 374 }
michael@0 375 throw 'oops';
michael@0 376 };
michael@0 377
michael@0 378
michael@0 379 function loadStandardVertexShader(context) {
michael@0 380 return loadShader(
michael@0 381 context,
michael@0 382 getBasePathForResources() + "vertexShader.vert",
michael@0 383 context.VERTEX_SHADER,
michael@0 384 true);
michael@0 385 }
michael@0 386
michael@0 387 function loadStandardFragmentShader(context) {
michael@0 388 return loadShader(
michael@0 389 context,
michael@0 390 getBasePathForResources() + "fragmentShader.frag",
michael@0 391 context.FRAGMENT_SHADER,
michael@0 392 true);
michael@0 393 }
michael@0 394
michael@0 395 //
michael@0 396 // makeBox
michael@0 397 //
michael@0 398 // Create a box with vertices, normals and texCoords. Create VBOs for each as well as the index array.
michael@0 399 // Return an object with the following properties:
michael@0 400 //
michael@0 401 // normalObject WebGLBuffer object for normals
michael@0 402 // texCoordObject WebGLBuffer object for texCoords
michael@0 403 // vertexObject WebGLBuffer object for vertices
michael@0 404 // indexObject WebGLBuffer object for indices
michael@0 405 // numIndices The number of indices in the indexObject
michael@0 406 //
michael@0 407 function makeBox(ctx)
michael@0 408 {
michael@0 409 // box
michael@0 410 // v6----- v5
michael@0 411 // /| /|
michael@0 412 // v1------v0|
michael@0 413 // | | | |
michael@0 414 // | |v7---|-|v4
michael@0 415 // |/ |/
michael@0 416 // v2------v3
michael@0 417 //
michael@0 418 // vertex coords array
michael@0 419 var vertices = new Float32Array(
michael@0 420 [ 1, 1, 1, -1, 1, 1, -1,-1, 1, 1,-1, 1, // v0-v1-v2-v3 front
michael@0 421 1, 1, 1, 1,-1, 1, 1,-1,-1, 1, 1,-1, // v0-v3-v4-v5 right
michael@0 422 1, 1, 1, 1, 1,-1, -1, 1,-1, -1, 1, 1, // v0-v5-v6-v1 top
michael@0 423 -1, 1, 1, -1, 1,-1, -1,-1,-1, -1,-1, 1, // v1-v6-v7-v2 left
michael@0 424 -1,-1,-1, 1,-1,-1, 1,-1, 1, -1,-1, 1, // v7-v4-v3-v2 bottom
michael@0 425 1,-1,-1, -1,-1,-1, -1, 1,-1, 1, 1,-1 ] // v4-v7-v6-v5 back
michael@0 426 );
michael@0 427
michael@0 428 // normal array
michael@0 429 var normals = new Float32Array(
michael@0 430 [ 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, // v0-v1-v2-v3 front
michael@0 431 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, // v0-v3-v4-v5 right
michael@0 432 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, // v0-v5-v6-v1 top
michael@0 433 -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, // v1-v6-v7-v2 left
michael@0 434 0,-1, 0, 0,-1, 0, 0,-1, 0, 0,-1, 0, // v7-v4-v3-v2 bottom
michael@0 435 0, 0,-1, 0, 0,-1, 0, 0,-1, 0, 0,-1 ] // v4-v7-v6-v5 back
michael@0 436 );
michael@0 437
michael@0 438
michael@0 439 // texCoord array
michael@0 440 var texCoords = new Float32Array(
michael@0 441 [ 1, 1, 0, 1, 0, 0, 1, 0, // v0-v1-v2-v3 front
michael@0 442 0, 1, 0, 0, 1, 0, 1, 1, // v0-v3-v4-v5 right
michael@0 443 1, 0, 1, 1, 0, 1, 0, 0, // v0-v5-v6-v1 top
michael@0 444 1, 1, 0, 1, 0, 0, 1, 0, // v1-v6-v7-v2 left
michael@0 445 0, 0, 1, 0, 1, 1, 0, 1, // v7-v4-v3-v2 bottom
michael@0 446 0, 0, 1, 0, 1, 1, 0, 1 ] // v4-v7-v6-v5 back
michael@0 447 );
michael@0 448
michael@0 449 // index array
michael@0 450 var indices = new Uint8Array(
michael@0 451 [ 0, 1, 2, 0, 2, 3, // front
michael@0 452 4, 5, 6, 4, 6, 7, // right
michael@0 453 8, 9,10, 8,10,11, // top
michael@0 454 12,13,14, 12,14,15, // left
michael@0 455 16,17,18, 16,18,19, // bottom
michael@0 456 20,21,22, 20,22,23 ] // back
michael@0 457 );
michael@0 458
michael@0 459 var retval = { };
michael@0 460
michael@0 461 retval.normalObject = ctx.createBuffer();
michael@0 462 ctx.bindBuffer(ctx.ARRAY_BUFFER, retval.normalObject);
michael@0 463 ctx.bufferData(ctx.ARRAY_BUFFER, normals, ctx.STATIC_DRAW);
michael@0 464
michael@0 465 retval.texCoordObject = ctx.createBuffer();
michael@0 466 ctx.bindBuffer(ctx.ARRAY_BUFFER, retval.texCoordObject);
michael@0 467 ctx.bufferData(ctx.ARRAY_BUFFER, texCoords, ctx.STATIC_DRAW);
michael@0 468
michael@0 469 retval.vertexObject = ctx.createBuffer();
michael@0 470 ctx.bindBuffer(ctx.ARRAY_BUFFER, retval.vertexObject);
michael@0 471 ctx.bufferData(ctx.ARRAY_BUFFER, vertices, ctx.STATIC_DRAW);
michael@0 472
michael@0 473 ctx.bindBuffer(ctx.ARRAY_BUFFER, 0);
michael@0 474
michael@0 475 retval.indexObject = ctx.createBuffer();
michael@0 476 ctx.bindBuffer(ctx.ELEMENT_ARRAY_BUFFER, retval.indexObject);
michael@0 477 ctx.bufferData(ctx.ELEMENT_ARRAY_BUFFER, indices, ctx.STATIC_DRAW);
michael@0 478 ctx.bindBuffer(ctx.ELEMENT_ARRAY_BUFFER, 0);
michael@0 479
michael@0 480 retval.numIndices = indices.length;
michael@0 481
michael@0 482 return retval;
michael@0 483 }
michael@0 484
michael@0 485 //
michael@0 486 // makeSphere
michael@0 487 //
michael@0 488 // Create a sphere with the passed number of latitude and longitude bands and the passed radius.
michael@0 489 // Sphere has vertices, normals and texCoords. Create VBOs for each as well as the index array.
michael@0 490 // Return an object with the following properties:
michael@0 491 //
michael@0 492 // normalObject WebGLBuffer object for normals
michael@0 493 // texCoordObject WebGLBuffer object for texCoords
michael@0 494 // vertexObject WebGLBuffer object for vertices
michael@0 495 // indexObject WebGLBuffer object for indices
michael@0 496 // numIndices The number of indices in the indexObject
michael@0 497 //
michael@0 498 function makeSphere(ctx, radius, lats, longs)
michael@0 499 {
michael@0 500 var geometryData = [ ];
michael@0 501 var normalData = [ ];
michael@0 502 var texCoordData = [ ];
michael@0 503 var indexData = [ ];
michael@0 504
michael@0 505 for (var latNumber = 0; latNumber <= lats; ++latNumber) {
michael@0 506 for (var longNumber = 0; longNumber <= longs; ++longNumber) {
michael@0 507 var theta = latNumber * Math.PI / lats;
michael@0 508 var phi = longNumber * 2 * Math.PI / longs;
michael@0 509 var sinTheta = Math.sin(theta);
michael@0 510 var sinPhi = Math.sin(phi);
michael@0 511 var cosTheta = Math.cos(theta);
michael@0 512 var cosPhi = Math.cos(phi);
michael@0 513
michael@0 514 var x = cosPhi * sinTheta;
michael@0 515 var y = cosTheta;
michael@0 516 var z = sinPhi * sinTheta;
michael@0 517 var u = 1-(longNumber/longs);
michael@0 518 var v = latNumber/lats;
michael@0 519
michael@0 520 normalData.push(x);
michael@0 521 normalData.push(y);
michael@0 522 normalData.push(z);
michael@0 523 texCoordData.push(u);
michael@0 524 texCoordData.push(v);
michael@0 525 geometryData.push(radius * x);
michael@0 526 geometryData.push(radius * y);
michael@0 527 geometryData.push(radius * z);
michael@0 528 }
michael@0 529 }
michael@0 530
michael@0 531 longs += 1;
michael@0 532 for (var latNumber = 0; latNumber < lats; ++latNumber) {
michael@0 533 for (var longNumber = 0; longNumber < longs; ++longNumber) {
michael@0 534 var first = (latNumber * longs) + (longNumber % longs);
michael@0 535 var second = first + longs;
michael@0 536 indexData.push(first);
michael@0 537 indexData.push(second);
michael@0 538 indexData.push(first+1);
michael@0 539
michael@0 540 indexData.push(second);
michael@0 541 indexData.push(second+1);
michael@0 542 indexData.push(first+1);
michael@0 543 }
michael@0 544 }
michael@0 545
michael@0 546 var retval = { };
michael@0 547
michael@0 548 retval.normalObject = ctx.createBuffer();
michael@0 549 ctx.bindBuffer(ctx.ARRAY_BUFFER, retval.normalObject);
michael@0 550 ctx.bufferData(ctx.ARRAY_BUFFER, new Float32Array(normalData), ctx.STATIC_DRAW);
michael@0 551
michael@0 552 retval.texCoordObject = ctx.createBuffer();
michael@0 553 ctx.bindBuffer(ctx.ARRAY_BUFFER, retval.texCoordObject);
michael@0 554 ctx.bufferData(ctx.ARRAY_BUFFER, new Float32Array(texCoordData), ctx.STATIC_DRAW);
michael@0 555
michael@0 556 retval.vertexObject = ctx.createBuffer();
michael@0 557 ctx.bindBuffer(ctx.ARRAY_BUFFER, retval.vertexObject);
michael@0 558 ctx.bufferData(ctx.ARRAY_BUFFER, new Float32Array(geometryData), ctx.STATIC_DRAW);
michael@0 559
michael@0 560 retval.numIndices = indexData.length;
michael@0 561 retval.indexObject = ctx.createBuffer();
michael@0 562 ctx.bindBuffer(ctx.ELEMENT_ARRAY_BUFFER, retval.indexObject);
michael@0 563 ctx.bufferData(ctx.ELEMENT_ARRAY_BUFFER, new Uint16Array(indexData), ctx.STREAM_DRAW);
michael@0 564
michael@0 565 return retval;
michael@0 566 }
michael@0 567
michael@0 568 //
michael@0 569 // loadObj
michael@0 570 //
michael@0 571 // Load a .obj file from the passed URL. Return an object with a 'loaded' property set to false.
michael@0 572 // When the object load is complete, the 'loaded' property becomes true and the following
michael@0 573 // properties are set:
michael@0 574 //
michael@0 575 // normalObject WebGLBuffer object for normals
michael@0 576 // texCoordObject WebGLBuffer object for texCoords
michael@0 577 // vertexObject WebGLBuffer object for vertices
michael@0 578 // indexObject WebGLBuffer object for indices
michael@0 579 // numIndices The number of indices in the indexObject
michael@0 580 //
michael@0 581 function loadObj(ctx, url)
michael@0 582 {
michael@0 583 var obj = { loaded : false };
michael@0 584 obj.ctx = ctx;
michael@0 585 var req = new XMLHttpRequest();
michael@0 586 req.obj = obj;
michael@0 587 req.onreadystatechange = function () { processLoadObj(req) };
michael@0 588 req.open("GET", url, true);
michael@0 589 req.send(null);
michael@0 590 return obj;
michael@0 591 }
michael@0 592
michael@0 593 function processLoadObj(req)
michael@0 594 {
michael@0 595 webglTestLog("req="+req)
michael@0 596 // only if req shows "complete"
michael@0 597 if (req.readyState == 4) {
michael@0 598 doLoadObj(req.obj, req.responseText);
michael@0 599 }
michael@0 600 }
michael@0 601
michael@0 602 function doLoadObj(obj, text)
michael@0 603 {
michael@0 604 vertexArray = [ ];
michael@0 605 normalArray = [ ];
michael@0 606 textureArray = [ ];
michael@0 607 indexArray = [ ];
michael@0 608
michael@0 609 var vertex = [ ];
michael@0 610 var normal = [ ];
michael@0 611 var texture = [ ];
michael@0 612 var facemap = { };
michael@0 613 var index = 0;
michael@0 614
michael@0 615 var lines = text.split("\n");
michael@0 616 for (var lineIndex in lines) {
michael@0 617 var line = lines[lineIndex].replace(/[ \t]+/g, " ").replace(/\s\s*$/, "");
michael@0 618
michael@0 619 // ignore comments
michael@0 620 if (line[0] == "#")
michael@0 621 continue;
michael@0 622
michael@0 623 var array = line.split(" ");
michael@0 624 if (array[0] == "v") {
michael@0 625 // vertex
michael@0 626 vertex.push(parseFloat(array[1]));
michael@0 627 vertex.push(parseFloat(array[2]));
michael@0 628 vertex.push(parseFloat(array[3]));
michael@0 629 }
michael@0 630 else if (array[0] == "vt") {
michael@0 631 // normal
michael@0 632 texture.push(parseFloat(array[1]));
michael@0 633 texture.push(parseFloat(array[2]));
michael@0 634 }
michael@0 635 else if (array[0] == "vn") {
michael@0 636 // normal
michael@0 637 normal.push(parseFloat(array[1]));
michael@0 638 normal.push(parseFloat(array[2]));
michael@0 639 normal.push(parseFloat(array[3]));
michael@0 640 }
michael@0 641 else if (array[0] == "f") {
michael@0 642 // face
michael@0 643 if (array.length != 4) {
michael@0 644 webglTestLog("*** Error: face '"+line+"' not handled");
michael@0 645 continue;
michael@0 646 }
michael@0 647
michael@0 648 for (var i = 1; i < 4; ++i) {
michael@0 649 if (!(array[i] in facemap)) {
michael@0 650 // add a new entry to the map and arrays
michael@0 651 var f = array[i].split("/");
michael@0 652 var vtx, nor, tex;
michael@0 653
michael@0 654 if (f.length == 1) {
michael@0 655 vtx = parseInt(f[0]) - 1;
michael@0 656 nor = vtx;
michael@0 657 tex = vtx;
michael@0 658 }
michael@0 659 else if (f.length = 3) {
michael@0 660 vtx = parseInt(f[0]) - 1;
michael@0 661 tex = parseInt(f[1]) - 1;
michael@0 662 nor = parseInt(f[2]) - 1;
michael@0 663 }
michael@0 664 else {
michael@0 665 webglTestLog("*** Error: did not understand face '"+array[i]+"'");
michael@0 666 return null;
michael@0 667 }
michael@0 668
michael@0 669 // do the vertices
michael@0 670 var x = 0;
michael@0 671 var y = 0;
michael@0 672 var z = 0;
michael@0 673 if (vtx * 3 + 2 < vertex.length) {
michael@0 674 x = vertex[vtx*3];
michael@0 675 y = vertex[vtx*3+1];
michael@0 676 z = vertex[vtx*3+2];
michael@0 677 }
michael@0 678 vertexArray.push(x);
michael@0 679 vertexArray.push(y);
michael@0 680 vertexArray.push(z);
michael@0 681
michael@0 682 // do the textures
michael@0 683 x = 0;
michael@0 684 y = 0;
michael@0 685 if (tex * 2 + 1 < texture.length) {
michael@0 686 x = texture[tex*2];
michael@0 687 y = texture[tex*2+1];
michael@0 688 }
michael@0 689 textureArray.push(x);
michael@0 690 textureArray.push(y);
michael@0 691
michael@0 692 // do the normals
michael@0 693 x = 0;
michael@0 694 y = 0;
michael@0 695 z = 1;
michael@0 696 if (nor * 3 + 2 < normal.length) {
michael@0 697 x = normal[nor*3];
michael@0 698 y = normal[nor*3+1];
michael@0 699 z = normal[nor*3+2];
michael@0 700 }
michael@0 701 normalArray.push(x);
michael@0 702 normalArray.push(y);
michael@0 703 normalArray.push(z);
michael@0 704
michael@0 705 facemap[array[i]] = index++;
michael@0 706 }
michael@0 707
michael@0 708 indexArray.push(facemap[array[i]]);
michael@0 709 }
michael@0 710 }
michael@0 711 }
michael@0 712
michael@0 713 // set the VBOs
michael@0 714 obj.normalObject = obj.ctx.createBuffer();
michael@0 715 obj.ctx.bindBuffer(obj.ctx.ARRAY_BUFFER, obj.normalObject);
michael@0 716 obj.ctx.bufferData(obj.ctx.ARRAY_BUFFER, new Float32Array(normalArray), obj.ctx.STATIC_DRAW);
michael@0 717
michael@0 718 obj.texCoordObject = obj.ctx.createBuffer();
michael@0 719 obj.ctx.bindBuffer(obj.ctx.ARRAY_BUFFER, obj.texCoordObject);
michael@0 720 obj.ctx.bufferData(obj.ctx.ARRAY_BUFFER, new Float32Array(textureArray), obj.ctx.STATIC_DRAW);
michael@0 721
michael@0 722 obj.vertexObject = obj.ctx.createBuffer();
michael@0 723 obj.ctx.bindBuffer(obj.ctx.ARRAY_BUFFER, obj.vertexObject);
michael@0 724 obj.ctx.bufferData(obj.ctx.ARRAY_BUFFER, new Float32Array(vertexArray), obj.ctx.STATIC_DRAW);
michael@0 725
michael@0 726 obj.numIndices = indexArray.length;
michael@0 727 obj.indexObject = obj.ctx.createBuffer();
michael@0 728 obj.ctx.bindBuffer(obj.ctx.ELEMENT_ARRAY_BUFFER, obj.indexObject);
michael@0 729 obj.ctx.bufferData(obj.ctx.ELEMENT_ARRAY_BUFFER, new Uint16Array(indexArray), obj.ctx.STREAM_DRAW);
michael@0 730
michael@0 731 obj.loaded = true;
michael@0 732 }
michael@0 733
michael@0 734 //
michael@0 735 // loadImageTexture
michael@0 736 //
michael@0 737 // Load the image at the passed url, place it in a new WebGLTexture object and return the WebGLTexture.
michael@0 738 //
michael@0 739 function loadImageTexture(ctx, url)
michael@0 740 {
michael@0 741 var texture = ctx.createTexture();
michael@0 742 texture.image = new Image();
michael@0 743 texture.image.onload = function() { doLoadImageTexture(ctx, texture.image, texture) }
michael@0 744 texture.image.src = url;
michael@0 745 return texture;
michael@0 746 }
michael@0 747
michael@0 748 function doLoadImageTexture(ctx, image, texture)
michael@0 749 {
michael@0 750 ctx.enable(ctx.TEXTURE_2D);
michael@0 751 ctx.bindTexture(ctx.TEXTURE_2D, texture);
michael@0 752 ctx.texImage2D(ctx.TEXTURE_2D, 0, ctx.RGBA, ctx.RGBA, ctx.UNSIGNED_BYTE, image);
michael@0 753 ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_MAG_FILTER, ctx.LINEAR);
michael@0 754 ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_MIN_FILTER, ctx.LINEAR_MIPMAP_LINEAR);
michael@0 755 ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_WRAP_S, ctx.CLAMP_TO_EDGE);
michael@0 756 ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_WRAP_T, ctx.CLAMP_TO_EDGE);
michael@0 757 ctx.generateMipmap(ctx.TEXTURE_2D)
michael@0 758 ctx.bindTexture(ctx.TEXTURE_2D, 0);
michael@0 759 }
michael@0 760
michael@0 761 //
michael@0 762 // Framerate object
michael@0 763 //
michael@0 764 // This object keeps track of framerate and displays it as the innerHTML text of the
michael@0 765 // HTML element with the passed id. Once created you call snapshot at the end
michael@0 766 // of every rendering cycle. Every 500ms the framerate is updated in the HTML element.
michael@0 767 //
michael@0 768 Framerate = function(id)
michael@0 769 {
michael@0 770 this.numFramerates = 10;
michael@0 771 this.framerateUpdateInterval = 500;
michael@0 772 this.id = id;
michael@0 773
michael@0 774 this.renderTime = -1;
michael@0 775 this.framerates = [ ];
michael@0 776 self = this;
michael@0 777 var fr = function() { self.updateFramerate() }
michael@0 778 setInterval(fr, this.framerateUpdateInterval);
michael@0 779 }
michael@0 780
michael@0 781 Framerate.prototype.updateFramerate = function()
michael@0 782 {
michael@0 783 var tot = 0;
michael@0 784 for (var i = 0; i < this.framerates.length; ++i)
michael@0 785 tot += this.framerates[i];
michael@0 786
michael@0 787 var framerate = tot / this.framerates.length;
michael@0 788 framerate = Math.round(framerate);
michael@0 789 document.getElementById(this.id).innerHTML = "Framerate:"+framerate+"fps";
michael@0 790 }
michael@0 791
michael@0 792 Framerate.prototype.snapshot = function()
michael@0 793 {
michael@0 794 if (this.renderTime < 0)
michael@0 795 this.renderTime = new Date().getTime();
michael@0 796 else {
michael@0 797 var newTime = new Date().getTime();
michael@0 798 var t = newTime - this.renderTime;
michael@0 799 var framerate = 1000/t;
michael@0 800 this.framerates.push(framerate);
michael@0 801 while (this.framerates.length > this.numFramerates)
michael@0 802 this.framerates.shift();
michael@0 803 this.renderTime = newTime;
michael@0 804 }
michael@0 805 }

mercurial