| //---------------------------------------------------------------------- |
| // Differences with respect to Khronos version of webgl-test.js |
| if (window.testRunner) |
| testRunner.overridePreference("WebKitWebGLEnabled", "1"); |
| |
| function assertMsg(assertion, msg) { |
| if (assertion) { |
| testPassed(msg); |
| } else { |
| testFailed(msg); |
| } |
| } |
| |
| function initNonKhronosFramework(waitUntilDone) { |
| if (window.testRunner) { |
| testRunner.overridePreference("WebKitWebGLEnabled", "1"); |
| testRunner.dumpAsText(); |
| if (waitUntilDone) { |
| window.jsTestIsAsync = true; |
| testRunner.waitUntilDone(); |
| } |
| } |
| } |
| |
| function nonKhronosFrameworkNotifyDone() { |
| if (window.testRunner) { |
| testRunner.notifyDone(); |
| } |
| } |
| |
| function finishTest() { |
| finishJSTest(); |
| } |
| |
| // |
| //---------------------------------------------------------------------- |
| |
| function webglTestLog(msg) { |
| if (window.console && window.console.log) { |
| window.console.log(msg); |
| } |
| if (document.getElementById("console")) { |
| var log = document.getElementById("console"); |
| log.innerHTML += msg + "<br>"; |
| } |
| } |
| |
| // |
| // create3DContext |
| // |
| // Returns the WebGLRenderingContext for any known implementation. |
| // |
| function create3DContext(canvas, attributes) |
| { |
| if (!canvas) |
| canvas = document.createElement("canvas"); |
| var context = null; |
| try { |
| context = canvas.getContext("webgl", attributes); |
| } catch(e) {} |
| if (!context) { |
| try { |
| context = canvas.getContext("webkit-3d", attributes); |
| } catch(e) {} |
| } |
| if (!context) { |
| try { |
| context = canvas.getContext("moz-webgl", attributes); |
| } catch(e) {} |
| } |
| if (!context) { |
| throw "Unable to fetch WebGL rendering context for Canvas"; |
| } |
| return context; |
| } |
| |
| function createGLErrorWrapper(context, fname) { |
| return function() { |
| var rv = context[fname].apply(context, arguments); |
| var err = context.getError(); |
| if (err != 0) |
| throw "GL error " + err + " in " + fname; |
| return rv; |
| }; |
| } |
| |
| function create3DContextWithWrapperThatThrowsOnGLError(canvas, attributes) { |
| var context = create3DContext(canvas, attributes); |
| // Thanks to Ilmari Heikkinen for the idea on how to implement this so elegantly. |
| var wrap = {}; |
| for (var i in context) { |
| try { |
| if (typeof context[i] == 'function') { |
| wrap[i] = createGLErrorWrapper(context, i); |
| } else { |
| wrap[i] = context[i]; |
| } |
| } catch (e) { |
| webglTestLog("createContextWrapperThatThrowsOnGLError: Error accessing " + i); |
| } |
| } |
| wrap.getError = function() { |
| return context.getError(); |
| }; |
| return wrap; |
| } |
| |
| function getGLErrorAsString(ctx, err) { |
| if (err === ctx.NO_ERROR) { |
| return "NO_ERROR"; |
| } |
| for (var name in ctx) { |
| if (ctx[name] === err) { |
| return name; |
| } |
| } |
| return "0x" + err.toString(16); |
| } |
| |
| // Pass undefined for glError to test that it at least throws some error |
| function shouldGenerateGLError(ctx, glErrors, evalStr) { |
| if (!glErrors.length) { |
| glErrors = [glErrors]; |
| } |
| var exception; |
| try { |
| eval(evalStr); |
| } catch (e) { |
| exception = e; |
| } |
| if (exception) { |
| testFailed(evalStr + " threw exception " + exception); |
| } else { |
| var err = ctx.getError(); |
| var errStrs = []; |
| for (var ii = 0; ii < glErrors.length; ++ii) { |
| errStrs.push(getGLErrorAsString(ctx, glErrors[ii])); |
| } |
| var expected = errStrs.join(" or "); |
| if (glErrors.indexOf(err) < 0) { |
| testFailed(evalStr + " expected: " + expected + ". Was " + getGLErrorAsString(ctx, err) + "."); |
| } else { |
| var msg = (glErrors.length == 1) ? " generated expected GL error: " : |
| " generated one of expected GL errors: "; |
| testPassed(evalStr + msg + expected + "."); |
| } |
| } |
| } |
| |
| /** |
| * Tests that the first error GL returns is the specified error. |
| * @param {!WebGLContext} gl The WebGLContext to use. |
| * @param {number|!Array.<number>} glError The expected gl |
| * error. Multiple errors can be passed in using an |
| * array. |
| * @param {string} opt_msg Optional additional message. |
| */ |
| function glErrorShouldBe(gl, glErrors, opt_msg) { |
| if (!glErrors.length) { |
| glErrors = [glErrors]; |
| } |
| opt_msg = opt_msg || ""; |
| var err = gl.getError(); |
| var ndx = glErrors.indexOf(err); |
| var errStrs = []; |
| for (var ii = 0; ii < glErrors.length; ++ii) { |
| errStrs.push(getGLErrorAsString(gl, glErrors[ii])); |
| } |
| var expected = errStrs.join(" or "); |
| if (ndx < 0) { |
| var msg = "getError expected" + ((glErrors.length > 1) ? " one of: " : ": "); |
| testFailed(msg + expected + ". Was " + getGLErrorAsString(gl, err) + " : " + opt_msg); |
| } else { |
| var msg = "getError was " + ((glErrors.length > 1) ? "one of: " : "expected value: "); |
| testPassed(msg + expected + " : " + opt_msg); |
| } |
| }; |
| |
| // |
| // createProgram |
| // |
| // Create and return a program object, attaching each of the given shaders. |
| // |
| // If attribs are given, bind an attrib with that name at that index. |
| // |
| function createProgram(gl, vshaders, fshaders, attribs) |
| { |
| if (typeof(vshaders) == "string") |
| vshaders = [vshaders]; |
| if (typeof(fshaders) == "string") |
| fshaders = [fshaders]; |
| |
| var shaders = []; |
| var i; |
| |
| for (i = 0; i < vshaders.length; ++i) { |
| var shader = loadShader(gl, vshaders[i], gl.VERTEX_SHADER); |
| if (!shader) |
| return null; |
| shaders.push(shader); |
| } |
| |
| for (i = 0; i < fshaders.length; ++i) { |
| var shader = loadShader(gl, fshaders[i], gl.FRAGMENT_SHADER); |
| if (!shader) |
| return null; |
| shaders.push(shader); |
| } |
| |
| var prog = gl.createProgram(); |
| for (i = 0; i < shaders.length; ++i) { |
| gl.attachShader(prog, shaders[i]); |
| } |
| |
| if (attribs) { |
| for (var i in attribs) { |
| gl.bindAttribLocation (prog, parseInt(i), attribs[i]); |
| } |
| } |
| |
| gl.linkProgram(prog); |
| |
| // Check the link status |
| var linked = gl.getProgramParameter(prog, gl.LINK_STATUS); |
| if (!linked) { |
| // something went wrong with the link |
| var error = gl.getProgramInfoLog(prog); |
| webglTestLog("Error in program linking:" + error); |
| |
| gl.deleteProgram(prog); |
| for (i = 0; i < shaders.length; ++i) |
| gl.deleteShader(shaders[i]); |
| return null; |
| } |
| |
| return prog; |
| } |
| |
| // |
| // initWebGL |
| // |
| // Initialize the Canvas element with the passed name as a WebGL object and return the |
| // WebGLRenderingContext. |
| // |
| // Load shaders with the passed names and create a program with them. Return this program |
| // in the 'program' property of the returned context. |
| // |
| // For each string in the passed attribs array, bind an attrib with that name at that index. |
| // Once the attribs are bound, link the program and then use it. |
| // |
| // Set the clear color to the passed array (4 values) and set the clear depth to the passed value. |
| // Enable depth testing and blending with a blend func of (SRC_ALPHA, ONE_MINUS_SRC_ALPHA) |
| // |
| function initWebGL(canvasName, vshader, fshader, attribs, clearColor, clearDepth, contextAttribs) |
| { |
| var canvas = document.getElementById(canvasName); |
| var gl = create3DContext(canvas, contextAttribs); |
| if (!gl) { |
| alert("No WebGL context found"); |
| return null; |
| } |
| |
| // Create the program object |
| gl.program = createProgram(gl, vshader, fshader, attribs); |
| if (!gl.program) |
| return null; |
| |
| gl.useProgram(gl.program); |
| |
| gl.clearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]); |
| gl.clearDepth(clearDepth); |
| |
| gl.enable(gl.DEPTH_TEST); |
| gl.enable(gl.BLEND); |
| gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); |
| |
| return gl; |
| } |
| |
| // |
| // getShaderSource |
| // |
| // Load the source from the passed shader file. |
| // |
| function getShaderSource(file) |
| { |
| var xhr = new XMLHttpRequest(); |
| xhr.open("GET", file, false); |
| xhr.send(); |
| return xhr.responseText; |
| } |
| |
| |
| // |
| // loadShader |
| // |
| // 'shader' is either the id of a <script> element containing the shader source |
| // string, the shader string itself, or the URL of a file containing the shader |
| // source. Load this shader and return the WebGLShader object corresponding to |
| // it. |
| // |
| function loadShader(ctx, shaderId, shaderType, isFile) |
| { |
| var shaderSource = ""; |
| |
| if (isFile) |
| shaderSource = getShaderSource(shaderId); |
| else { |
| var shaderScript = document.getElementById(shaderId); |
| if (!shaderScript) { |
| shaderSource = shaderId; |
| } else { |
| if (shaderScript.type == "x-shader/x-vertex") { |
| shaderType = ctx.VERTEX_SHADER; |
| } else if (shaderScript.type == "x-shader/x-fragment") { |
| shaderType = ctx.FRAGMENT_SHADER; |
| } else if (shaderType != ctx.VERTEX_SHADER && shaderType != ctx.FRAGMENT_SHADER) { |
| webglTestLog("*** Error: unknown shader type"); |
| return null; |
| } |
| |
| shaderSource = shaderScript.text; |
| } |
| } |
| |
| // Create the shader object |
| var shader = ctx.createShader(shaderType); |
| if (shader == null) { |
| webglTestLog("*** Error: unable to create shader '"+shaderId+"'"); |
| return null; |
| } |
| |
| // Load the shader source |
| ctx.shaderSource(shader, shaderSource); |
| |
| // Compile the shader |
| ctx.compileShader(shader); |
| |
| // Check the compile status |
| var compiled = ctx.getShaderParameter(shader, ctx.COMPILE_STATUS); |
| if (!compiled) { |
| // Something went wrong during compilation; get the error |
| var error = ctx.getShaderInfoLog(shader); |
| webglTestLog("*** Error compiling shader '"+shader+"':"+error); |
| ctx.deleteShader(shader); |
| return null; |
| } |
| |
| return shader; |
| } |
| |
| function loadShaderFromFile(ctx, file, type) |
| { |
| return loadShader(ctx, file, type, true); |
| } |
| |
| function loadShaderFromScript(ctx, script) |
| { |
| return loadShader(ctx, script, 0, false); |
| } |
| |
| function loadStandardProgram(context) { |
| var program = context.createProgram(); |
| context.attachShader(program, loadStandardVertexShader(context)); |
| context.attachShader(program, loadStandardFragmentShader(context)); |
| context.linkProgram(program); |
| return program; |
| } |
| |
| function loadProgram(context, vertexShaderPath, fragmentShaderPath, isFile) { |
| isFile = (isFile === undefined) ? true : isFile; |
| var program = context.createProgram(); |
| context.attachShader(program, loadShader(context, vertexShaderPath, context.VERTEX_SHADER, isFile)); |
| context.attachShader(program, loadShader(context, fragmentShaderPath, context.FRAGMENT_SHADER, isFile)); |
| context.linkProgram(program); |
| return program; |
| } |
| |
| function loadStandardVertexShader(context) { |
| return loadShader(context, "resources/vertexShader.vert", context.VERTEX_SHADER, true); |
| } |
| |
| function loadStandardFragmentShader(context) { |
| return loadShader(context, "resources/fragmentShader.frag", context.FRAGMENT_SHADER, true); |
| } |
| |
| // |
| // makeBox |
| // |
| // Create a box with vertices, normals and texCoords. Create VBOs for each as well as the index array. |
| // Return an object with the following properties: |
| // |
| // normalObject WebGLBuffer object for normals |
| // texCoordObject WebGLBuffer object for texCoords |
| // vertexObject WebGLBuffer object for vertices |
| // indexObject WebGLBuffer object for indices |
| // numIndices The number of indices in the indexObject |
| // |
| function makeBox(ctx) |
| { |
| // box |
| // v6----- v5 |
| // /| /| |
| // v1------v0| |
| // | | | | |
| // | |v7---|-|v4 |
| // |/ |/ |
| // v2------v3 |
| // |
| // vertex coords array |
| var vertices = new Float32Array( |
| [ 1, 1, 1, -1, 1, 1, -1,-1, 1, 1,-1, 1, // v0-v1-v2-v3 front |
| 1, 1, 1, 1,-1, 1, 1,-1,-1, 1, 1,-1, // v0-v3-v4-v5 right |
| 1, 1, 1, 1, 1,-1, -1, 1,-1, -1, 1, 1, // v0-v5-v6-v1 top |
| -1, 1, 1, -1, 1,-1, -1,-1,-1, -1,-1, 1, // v1-v6-v7-v2 left |
| -1,-1,-1, 1,-1,-1, 1,-1, 1, -1,-1, 1, // v7-v4-v3-v2 bottom |
| 1,-1,-1, -1,-1,-1, -1, 1,-1, 1, 1,-1 ] // v4-v7-v6-v5 back |
| ); |
| |
| // normal array |
| var normals = new Float32Array( |
| [ 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, // v0-v1-v2-v3 front |
| 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, // v0-v3-v4-v5 right |
| 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, // v0-v5-v6-v1 top |
| -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, // v1-v6-v7-v2 left |
| 0,-1, 0, 0,-1, 0, 0,-1, 0, 0,-1, 0, // v7-v4-v3-v2 bottom |
| 0, 0,-1, 0, 0,-1, 0, 0,-1, 0, 0,-1 ] // v4-v7-v6-v5 back |
| ); |
| |
| |
| // texCoord array |
| var texCoords = new Float32Array( |
| [ 1, 1, 0, 1, 0, 0, 1, 0, // v0-v1-v2-v3 front |
| 0, 1, 0, 0, 1, 0, 1, 1, // v0-v3-v4-v5 right |
| 1, 0, 1, 1, 0, 1, 0, 0, // v0-v5-v6-v1 top |
| 1, 1, 0, 1, 0, 0, 1, 0, // v1-v6-v7-v2 left |
| 0, 0, 1, 0, 1, 1, 0, 1, // v7-v4-v3-v2 bottom |
| 0, 0, 1, 0, 1, 1, 0, 1 ] // v4-v7-v6-v5 back |
| ); |
| |
| // index array |
| var indices = new Uint8Array( |
| [ 0, 1, 2, 0, 2, 3, // front |
| 4, 5, 6, 4, 6, 7, // right |
| 8, 9,10, 8,10,11, // top |
| 12,13,14, 12,14,15, // left |
| 16,17,18, 16,18,19, // bottom |
| 20,21,22, 20,22,23 ] // back |
| ); |
| |
| var retval = { }; |
| |
| retval.normalObject = ctx.createBuffer(); |
| ctx.bindBuffer(ctx.ARRAY_BUFFER, retval.normalObject); |
| ctx.bufferData(ctx.ARRAY_BUFFER, normals, ctx.STATIC_DRAW); |
| |
| retval.texCoordObject = ctx.createBuffer(); |
| ctx.bindBuffer(ctx.ARRAY_BUFFER, retval.texCoordObject); |
| ctx.bufferData(ctx.ARRAY_BUFFER, texCoords, ctx.STATIC_DRAW); |
| |
| retval.vertexObject = ctx.createBuffer(); |
| ctx.bindBuffer(ctx.ARRAY_BUFFER, retval.vertexObject); |
| ctx.bufferData(ctx.ARRAY_BUFFER, vertices, ctx.STATIC_DRAW); |
| |
| ctx.bindBuffer(ctx.ARRAY_BUFFER, 0); |
| |
| retval.indexObject = ctx.createBuffer(); |
| ctx.bindBuffer(ctx.ELEMENT_ARRAY_BUFFER, retval.indexObject); |
| ctx.bufferData(ctx.ELEMENT_ARRAY_BUFFER, indices, ctx.STATIC_DRAW); |
| ctx.bindBuffer(ctx.ELEMENT_ARRAY_BUFFER, 0); |
| |
| retval.numIndices = indices.length; |
| |
| return retval; |
| } |
| |
| // |
| // makeSphere |
| // |
| // Create a sphere with the passed number of latitude and longitude bands and the passed radius. |
| // Sphere has vertices, normals and texCoords. Create VBOs for each as well as the index array. |
| // Return an object with the following properties: |
| // |
| // normalObject WebGLBuffer object for normals |
| // texCoordObject WebGLBuffer object for texCoords |
| // vertexObject WebGLBuffer object for vertices |
| // indexObject WebGLBuffer object for indices |
| // numIndices The number of indices in the indexObject |
| // |
| function makeSphere(ctx, radius, lats, longs) |
| { |
| var geometryData = [ ]; |
| var normalData = [ ]; |
| var texCoordData = [ ]; |
| var indexData = [ ]; |
| |
| for (var latNumber = 0; latNumber <= lats; ++latNumber) { |
| for (var longNumber = 0; longNumber <= longs; ++longNumber) { |
| var theta = latNumber * Math.PI / lats; |
| var phi = longNumber * 2 * Math.PI / longs; |
| var sinTheta = Math.sin(theta); |
| var sinPhi = Math.sin(phi); |
| var cosTheta = Math.cos(theta); |
| var cosPhi = Math.cos(phi); |
| |
| var x = cosPhi * sinTheta; |
| var y = cosTheta; |
| var z = sinPhi * sinTheta; |
| var u = 1-(longNumber/longs); |
| var v = latNumber/lats; |
| |
| normalData.push(x); |
| normalData.push(y); |
| normalData.push(z); |
| texCoordData.push(u); |
| texCoordData.push(v); |
| geometryData.push(radius * x); |
| geometryData.push(radius * y); |
| geometryData.push(radius * z); |
| } |
| } |
| |
| longs += 1; |
| for (var latNumber = 0; latNumber < lats; ++latNumber) { |
| for (var longNumber = 0; longNumber < longs; ++longNumber) { |
| var first = (latNumber * longs) + (longNumber % longs); |
| var second = first + longs; |
| indexData.push(first); |
| indexData.push(second); |
| indexData.push(first+1); |
| |
| indexData.push(second); |
| indexData.push(second+1); |
| indexData.push(first+1); |
| } |
| } |
| |
| var retval = { }; |
| |
| retval.normalObject = ctx.createBuffer(); |
| ctx.bindBuffer(ctx.ARRAY_BUFFER, retval.normalObject); |
| ctx.bufferData(ctx.ARRAY_BUFFER, new Float32Array(normalData), ctx.STATIC_DRAW); |
| |
| retval.texCoordObject = ctx.createBuffer(); |
| ctx.bindBuffer(ctx.ARRAY_BUFFER, retval.texCoordObject); |
| ctx.bufferData(ctx.ARRAY_BUFFER, new Float32Array(texCoordData), ctx.STATIC_DRAW); |
| |
| retval.vertexObject = ctx.createBuffer(); |
| ctx.bindBuffer(ctx.ARRAY_BUFFER, retval.vertexObject); |
| ctx.bufferData(ctx.ARRAY_BUFFER, new Float32Array(geometryData), ctx.STATIC_DRAW); |
| |
| retval.numIndices = indexData.length; |
| retval.indexObject = ctx.createBuffer(); |
| ctx.bindBuffer(ctx.ELEMENT_ARRAY_BUFFER, retval.indexObject); |
| ctx.bufferData(ctx.ELEMENT_ARRAY_BUFFER, new Uint16Array(indexData), ctx.STREAM_DRAW); |
| |
| return retval; |
| } |
| |
| // |
| // loadObj |
| // |
| // Load a .obj file from the passed URL. Return an object with a 'loaded' property set to false. |
| // When the object load is complete, the 'loaded' property becomes true and the following |
| // properties are set: |
| // |
| // normalObject WebGLBuffer object for normals |
| // texCoordObject WebGLBuffer object for texCoords |
| // vertexObject WebGLBuffer object for vertices |
| // indexObject WebGLBuffer object for indices |
| // numIndices The number of indices in the indexObject |
| // |
| function loadObj(ctx, url) |
| { |
| var obj = { loaded : false }; |
| obj.ctx = ctx; |
| var req = new XMLHttpRequest(); |
| req.obj = obj; |
| req.onreadystatechange = function () { processLoadObj(req) }; |
| req.open("GET", url, true); |
| req.send(null); |
| return obj; |
| } |
| |
| function processLoadObj(req) |
| { |
| webglTestLog("req="+req) |
| // only if req shows "complete" |
| if (req.readyState == 4) { |
| doLoadObj(req.obj, req.responseText); |
| } |
| } |
| |
| function doLoadObj(obj, text) |
| { |
| vertexArray = [ ]; |
| normalArray = [ ]; |
| textureArray = [ ]; |
| indexArray = [ ]; |
| |
| var vertex = [ ]; |
| var normal = [ ]; |
| var texture = [ ]; |
| var facemap = { }; |
| var index = 0; |
| |
| var lines = text.split("\n"); |
| for (var lineIndex in lines) { |
| var line = lines[lineIndex].replace(/[ \t]+/g, " ").replace(/\s\s*$/, ""); |
| |
| // ignore comments |
| if (line[0] == "#") |
| continue; |
| |
| var array = line.split(" "); |
| if (array[0] == "v") { |
| // vertex |
| vertex.push(parseFloat(array[1])); |
| vertex.push(parseFloat(array[2])); |
| vertex.push(parseFloat(array[3])); |
| } |
| else if (array[0] == "vt") { |
| // normal |
| texture.push(parseFloat(array[1])); |
| texture.push(parseFloat(array[2])); |
| } |
| else if (array[0] == "vn") { |
| // normal |
| normal.push(parseFloat(array[1])); |
| normal.push(parseFloat(array[2])); |
| normal.push(parseFloat(array[3])); |
| } |
| else if (array[0] == "f") { |
| // face |
| if (array.length != 4) { |
| webglTestLog("*** Error: face '"+line+"' not handled"); |
| continue; |
| } |
| |
| for (var i = 1; i < 4; ++i) { |
| if (!(array[i] in facemap)) { |
| // add a new entry to the map and arrays |
| var f = array[i].split("/"); |
| var vtx, nor, tex; |
| |
| if (f.length == 1) { |
| vtx = parseInt(f[0]) - 1; |
| nor = vtx; |
| tex = vtx; |
| } |
| else if (f.length = 3) { |
| vtx = parseInt(f[0]) - 1; |
| tex = parseInt(f[1]) - 1; |
| nor = parseInt(f[2]) - 1; |
| } |
| else { |
| webglTestLog("*** Error: did not understand face '"+array[i]+"'"); |
| return null; |
| } |
| |
| // do the vertices |
| var x = 0; |
| var y = 0; |
| var z = 0; |
| if (vtx * 3 + 2 < vertex.length) { |
| x = vertex[vtx*3]; |
| y = vertex[vtx*3+1]; |
| z = vertex[vtx*3+2]; |
| } |
| vertexArray.push(x); |
| vertexArray.push(y); |
| vertexArray.push(z); |
| |
| // do the textures |
| x = 0; |
| y = 0; |
| if (tex * 2 + 1 < texture.length) { |
| x = texture[tex*2]; |
| y = texture[tex*2+1]; |
| } |
| textureArray.push(x); |
| textureArray.push(y); |
| |
| // do the normals |
| x = 0; |
| y = 0; |
| z = 1; |
| if (nor * 3 + 2 < normal.length) { |
| x = normal[nor*3]; |
| y = normal[nor*3+1]; |
| z = normal[nor*3+2]; |
| } |
| normalArray.push(x); |
| normalArray.push(y); |
| normalArray.push(z); |
| |
| facemap[array[i]] = index++; |
| } |
| |
| indexArray.push(facemap[array[i]]); |
| } |
| } |
| } |
| |
| // set the VBOs |
| obj.normalObject = obj.ctx.createBuffer(); |
| obj.ctx.bindBuffer(obj.ctx.ARRAY_BUFFER, obj.normalObject); |
| obj.ctx.bufferData(obj.ctx.ARRAY_BUFFER, new Float32Array(normalArray), obj.ctx.STATIC_DRAW); |
| |
| obj.texCoordObject = obj.ctx.createBuffer(); |
| obj.ctx.bindBuffer(obj.ctx.ARRAY_BUFFER, obj.texCoordObject); |
| obj.ctx.bufferData(obj.ctx.ARRAY_BUFFER, new Float32Array(textureArray), obj.ctx.STATIC_DRAW); |
| |
| obj.vertexObject = obj.ctx.createBuffer(); |
| obj.ctx.bindBuffer(obj.ctx.ARRAY_BUFFER, obj.vertexObject); |
| obj.ctx.bufferData(obj.ctx.ARRAY_BUFFER, new Float32Array(vertexArray), obj.ctx.STATIC_DRAW); |
| |
| obj.numIndices = indexArray.length; |
| obj.indexObject = obj.ctx.createBuffer(); |
| obj.ctx.bindBuffer(obj.ctx.ELEMENT_ARRAY_BUFFER, obj.indexObject); |
| obj.ctx.bufferData(obj.ctx.ELEMENT_ARRAY_BUFFER, new Uint16Array(indexArray), obj.ctx.STREAM_DRAW); |
| |
| obj.loaded = true; |
| } |
| |
| // |
| // loadImageTexture |
| // |
| // Load the image at the passed url, place it in a new WebGLTexture object and return the WebGLTexture. |
| // |
| function loadImageTexture(ctx, url) |
| { |
| var texture = ctx.createTexture(); |
| texture.image = new Image(); |
| texture.image.onload = function() { doLoadImageTexture(ctx, texture.image, texture) } |
| texture.image.src = url; |
| return texture; |
| } |
| |
| function doLoadImageTexture(ctx, image, texture) |
| { |
| ctx.enable(ctx.TEXTURE_2D); |
| ctx.bindTexture(ctx.TEXTURE_2D, texture); |
| ctx.texImage2D(ctx.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image); |
| ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_MAG_FILTER, ctx.LINEAR); |
| ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_MIN_FILTER, ctx.LINEAR_MIPMAP_LINEAR); |
| ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_WRAP_S, ctx.CLAMP_TO_EDGE); |
| ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_WRAP_T, ctx.CLAMP_TO_EDGE); |
| ctx.generateMipmap(ctx.TEXTURE_2D) |
| ctx.bindTexture(ctx.TEXTURE_2D, 0); |
| } |
| |
| // |
| // Framerate object |
| // |
| // This object keeps track of framerate and displays it as the innerHTML text of the |
| // HTML element with the passed id. Once created you call snapshot at the end |
| // of every rendering cycle. Every 500ms the framerate is updated in the HTML element. |
| // |
| Framerate = function(id) |
| { |
| this.numFramerates = 10; |
| this.framerateUpdateInterval = 500; |
| this.id = id; |
| |
| this.renderTime = -1; |
| this.framerates = [ ]; |
| self = this; |
| var fr = function() { self.updateFramerate() } |
| setInterval(fr, this.framerateUpdateInterval); |
| } |
| |
| Framerate.prototype.updateFramerate = function() |
| { |
| var tot = 0; |
| for (var i = 0; i < this.framerates.length; ++i) |
| tot += this.framerates[i]; |
| |
| var framerate = tot / this.framerates.length; |
| framerate = Math.round(framerate); |
| document.getElementById(this.id).innerHTML = "Framerate:"+framerate+"fps"; |
| } |
| |
| Framerate.prototype.snapshot = function() |
| { |
| if (this.renderTime < 0) |
| this.renderTime = new Date().getTime(); |
| else { |
| var newTime = new Date().getTime(); |
| var t = newTime - this.renderTime; |
| var framerate = 1000/t; |
| this.framerates.push(framerate); |
| while (this.framerates.length > this.numFramerates) |
| this.framerates.shift(); |
| this.renderTime = newTime; |
| } |
| } |