scripts/utils3d.js
changeset 0 6dc831fb5a60
child 1 92e65293655d
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/scripts/utils3d.js	Thu Mar 31 23:53:48 2011 +0200
     1.3 @@ -0,0 +1,530 @@
     1.4 +//
     1.5 +// initWebGL
     1.6 +//
     1.7 +// Initialize the Canvas element with the passed name as a WebGL object and return the
     1.8 +// WebGLRenderingContext. 
     1.9 +//
    1.10 +// Load shaders with the passed names and create a program with them. Return this program 
    1.11 +// in the 'program' property of the returned context.
    1.12 +//
    1.13 +// For each string in the passed attribs array, bind an attrib with that name at that index.
    1.14 +// Once the attribs are bound, link the program and then use it.
    1.15 +//
    1.16 +// Set the clear color to the passed array (4 values) and set the clear depth to the passed value.
    1.17 +// Enable depth testing and blending with a blend func of (SRC_ALPHA, ONE_MINUS_SRC_ALPHA)
    1.18 +//
    1.19 +function initWebGL(canvasName, vshader, fshader, attribs, clearColor, clearDepth)
    1.20 +{
    1.21 +    var canvas = document.getElementById(canvasName);
    1.22 +    var gl = canvas.getContext("webkit-3d");
    1.23 +
    1.24 +    // create our shaders
    1.25 +    var vertexShader = loadShader(gl, vshader);
    1.26 +    var fragmentShader = loadShader(gl, fshader);
    1.27 +
    1.28 +    if (!vertexShader || !fragmentShader)
    1.29 +        return null;
    1.30 +
    1.31 +    // Create the program object
    1.32 +    gl.program = gl.createProgram();
    1.33 +
    1.34 +    if (!gl.program)
    1.35 +        return null;
    1.36 +
    1.37 +    // Attach our two shaders to the program
    1.38 +    gl.attachShader (gl.program, vertexShader);
    1.39 +    gl.attachShader (gl.program, fragmentShader);
    1.40 +
    1.41 +    // Bind attributes
    1.42 +    for (var i in attribs)
    1.43 +        gl.bindAttribLocation (gl.program, i, attribs[i]);
    1.44 +
    1.45 +    // Link the program
    1.46 +    gl.linkProgram(gl.program);
    1.47 +
    1.48 +    // Check the link status
    1.49 +    var linked = gl.getProgrami(gl.program, gl.LINK_STATUS);
    1.50 +    if (!linked) {
    1.51 +        // something went wrong with the link
    1.52 +        var error = gl.getProgramInfoLog (gl.program);
    1.53 +        console.log("Error in program linking:"+error);
    1.54 +
    1.55 +        gl.deleteProgram(gl.program);
    1.56 +        gl.deleteProgram(fragmentShader);
    1.57 +        gl.deleteProgram(vertexShader);
    1.58 +
    1.59 +        return null;
    1.60 +    }
    1.61 +
    1.62 +    gl.useProgram(gl.program);
    1.63 +
    1.64 +    gl.clearColor (clearColor[0], clearColor[1], clearColor[2], clearColor[3]);
    1.65 +    gl.clearDepth (clearDepth);
    1.66 +
    1.67 +    gl.enable(gl.DEPTH_TEST);
    1.68 +    gl.enable(gl.BLEND);
    1.69 +    gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
    1.70 +
    1.71 +    return gl;
    1.72 +}
    1.73 +
    1.74 +//
    1.75 +// loadShader
    1.76 +//
    1.77 +// 'shaderId' is the id of a <script> element containing the shader source string.
    1.78 +// Load this shader and return the WebGLShader object corresponding to it.
    1.79 +//
    1.80 +function loadShader(ctx, shaderId)
    1.81 +{
    1.82 +    var shaderScript = document.getElementById(shaderId);
    1.83 +    if (!shaderScript) {
    1.84 +        console.log("*** Error: shader script '"+shaderId+"' not found");
    1.85 +        return null;
    1.86 +    }
    1.87 +        
    1.88 +    if (shaderScript.type == "x-shader/x-vertex")
    1.89 +        var shaderType = ctx.VERTEX_SHADER;
    1.90 +    else if (shaderScript.type == "x-shader/x-fragment")
    1.91 +        var shaderType = ctx.FRAGMENT_SHADER;
    1.92 +    else {
    1.93 +        console.log("*** Error: shader script '"+shaderId+"' of undefined type '"+shaderScript.type+"'");       
    1.94 +        return null;
    1.95 +    }
    1.96 +
    1.97 +    // Create the shader object
    1.98 +    var shader = ctx.createShader(shaderType);
    1.99 +    if (shader == null) {
   1.100 +        console.log("*** Error: unable to create shader '"+shaderId+"'");       
   1.101 +        return null;
   1.102 +    }
   1.103 +
   1.104 +    // Load the shader source
   1.105 +    ctx.shaderSource(shader, shaderScript.text);
   1.106 +
   1.107 +    // Compile the shader
   1.108 +    ctx.compileShader(shader);
   1.109 +
   1.110 +    // Check the compile status
   1.111 +    var compiled = ctx.getShaderi(shader, ctx.COMPILE_STATUS);
   1.112 +    if (!compiled) {
   1.113 +        // Something went wrong during compilation; get the error
   1.114 +        var error = ctx.getShaderInfoLog(shader);
   1.115 +        console.log("*** Error compiling shader '"+shaderId+"':"+error);
   1.116 +        ctx.deleteShader(shader);
   1.117 +        return null;
   1.118 +    }
   1.119 +
   1.120 +    return shader;
   1.121 +}
   1.122 +
   1.123 +// 
   1.124 +// makeBox
   1.125 +//
   1.126 +// Create a box with vertices, normals and texCoords. Create VBOs for each as well as the index array.
   1.127 +// Return an object with the following properties:
   1.128 +//
   1.129 +//  normalObject        WebGLBuffer object for normals
   1.130 +//  texCoordObject      WebGLBuffer object for texCoords
   1.131 +//  vertexObject        WebGLBuffer object for vertices
   1.132 +//  indexObject         WebGLBuffer object for indices
   1.133 +//  numIndices          The number of indices in the indexObject
   1.134 +// 
   1.135 +function makeBox(ctx)
   1.136 +{
   1.137 +    // box
   1.138 +    //    v6----- v5
   1.139 +    //   /|      /|
   1.140 +    //  v1------v0|
   1.141 +    //  | |     | |
   1.142 +    //  | |v7---|-|v4
   1.143 +    //  |/      |/
   1.144 +    //  v2------v3
   1.145 +    //
   1.146 +    // vertex coords array
   1.147 +    var vertices = new WebGLFloatArray(
   1.148 +        [  1, 1, 1,  -1, 1, 1,  -1,-1, 1,   1,-1, 1,    // v0-v1-v2-v3 front
   1.149 +           1, 1, 1,   1,-1, 1,   1,-1,-1,   1, 1,-1,    // v0-v3-v4-v5 right
   1.150 +           1, 1, 1,   1, 1,-1,  -1, 1,-1,  -1, 1, 1,    // v0-v5-v6-v1 top
   1.151 +          -1, 1, 1,  -1, 1,-1,  -1,-1,-1,  -1,-1, 1,    // v1-v6-v7-v2 left
   1.152 +          -1,-1,-1,   1,-1,-1,   1,-1, 1,  -1,-1, 1,    // v7-v4-v3-v2 bottom
   1.153 +           1,-1,-1,  -1,-1,-1,  -1, 1,-1,   1, 1,-1 ]   // v4-v7-v6-v5 back
   1.154 +    );
   1.155 +
   1.156 +    // normal array
   1.157 +    var normals = new WebGLFloatArray(
   1.158 +        [  0, 0, 1,   0, 0, 1,   0, 0, 1,   0, 0, 1,     // v0-v1-v2-v3 front
   1.159 +           1, 0, 0,   1, 0, 0,   1, 0, 0,   1, 0, 0,     // v0-v3-v4-v5 right
   1.160 +           0, 1, 0,   0, 1, 0,   0, 1, 0,   0, 1, 0,     // v0-v5-v6-v1 top
   1.161 +          -1, 0, 0,  -1, 0, 0,  -1, 0, 0,  -1, 0, 0,     // v1-v6-v7-v2 left
   1.162 +           0,-1, 0,   0,-1, 0,   0,-1, 0,   0,-1, 0,     // v7-v4-v3-v2 bottom
   1.163 +           0, 0,-1,   0, 0,-1,   0, 0,-1,   0, 0,-1 ]    // v4-v7-v6-v5 back
   1.164 +       );
   1.165 +
   1.166 +
   1.167 +    // texCoord array
   1.168 +    var texCoords = new WebGLFloatArray(
   1.169 +        [  1, 1,   0, 1,   0, 0,   1, 0,    // v0-v1-v2-v3 front
   1.170 +           0, 1,   0, 0,   1, 0,   1, 1,    // v0-v3-v4-v5 right
   1.171 +           1, 0,   1, 1,   0, 1,   0, 0,    // v0-v5-v6-v1 top
   1.172 +           1, 1,   0, 1,   0, 0,   1, 0,    // v1-v6-v7-v2 left
   1.173 +           0, 0,   1, 0,   1, 1,   0, 1,    // v7-v4-v3-v2 bottom
   1.174 +           0, 0,   1, 0,   1, 1,   0, 1 ]   // v4-v7-v6-v5 back
   1.175 +       );
   1.176 +
   1.177 +    // index array
   1.178 +    var indices = new WebGLUnsignedByteArray(
   1.179 +        [  0, 1, 2,   0, 2, 3,    // front
   1.180 +           4, 5, 6,   4, 6, 7,    // right
   1.181 +           8, 9,10,   8,10,11,    // top
   1.182 +          12,13,14,  12,14,15,    // left
   1.183 +          16,17,18,  16,18,19,    // bottom
   1.184 +          20,21,22,  20,22,23 ]   // back
   1.185 +      );
   1.186 +
   1.187 +    var retval = { };
   1.188 +    
   1.189 +    retval.normalObject = ctx.createBuffer();
   1.190 +    ctx.bindBuffer(ctx.ARRAY_BUFFER, retval.normalObject);
   1.191 +    ctx.bufferData(ctx.ARRAY_BUFFER, normals, ctx.STATIC_DRAW);
   1.192 +    
   1.193 +    retval.texCoordObject = ctx.createBuffer();
   1.194 +    ctx.bindBuffer(ctx.ARRAY_BUFFER, retval.texCoordObject);
   1.195 +    ctx.bufferData(ctx.ARRAY_BUFFER, texCoords, ctx.STATIC_DRAW);
   1.196 +
   1.197 +    retval.vertexObject = ctx.createBuffer();
   1.198 +    ctx.bindBuffer(ctx.ARRAY_BUFFER, retval.vertexObject);
   1.199 +    ctx.bufferData(ctx.ARRAY_BUFFER, vertices, ctx.STATIC_DRAW);
   1.200 +    
   1.201 +    ctx.bindBuffer(ctx.ARRAY_BUFFER, 0);
   1.202 +
   1.203 +    retval.indexObject = ctx.createBuffer();
   1.204 +    ctx.bindBuffer(ctx.ELEMENT_ARRAY_BUFFER, retval.indexObject);
   1.205 +    ctx.bufferData(ctx.ELEMENT_ARRAY_BUFFER, indices, ctx.STATIC_DRAW);
   1.206 +    ctx.bindBuffer(ctx.ELEMENT_ARRAY_BUFFER, 0);
   1.207 +    
   1.208 +    retval.numIndices = indices.length;
   1.209 +
   1.210 +    return retval;
   1.211 +}
   1.212 +
   1.213 +// 
   1.214 +// makeSphere
   1.215 +//
   1.216 +// Create a sphere with the passed number of latitude and longitude bands and the passed radius. 
   1.217 +// Sphere has vertices, normals and texCoords. Create VBOs for each as well as the index array.
   1.218 +// Return an object with the following properties:
   1.219 +//
   1.220 +//  normalObject        WebGLBuffer object for normals
   1.221 +//  texCoordObject      WebGLBuffer object for texCoords
   1.222 +//  vertexObject        WebGLBuffer object for vertices
   1.223 +//  indexObject         WebGLBuffer object for indices
   1.224 +//  numIndices          The number of indices in the indexObject
   1.225 +// 
   1.226 +function makeSphere(ctx, radius, lats, longs)
   1.227 +{
   1.228 +    var geometryData = [ ];
   1.229 +    var normalData = [ ];
   1.230 +    var texCoordData = [ ];
   1.231 +    var indexData = [ ];
   1.232 +    
   1.233 +    for (var latNumber = 0; latNumber <= lats; ++latNumber) {
   1.234 +        for (var longNumber = 0; longNumber <= longs; ++longNumber) {
   1.235 +            var theta = latNumber * Math.PI / lats;
   1.236 +            var phi = longNumber * 2 * Math.PI / longs;
   1.237 +            var sinTheta = Math.sin(theta);
   1.238 +            var sinPhi = Math.sin(phi);
   1.239 +            var cosTheta = Math.cos(theta);
   1.240 +            var cosPhi = Math.cos(phi);
   1.241 +            
   1.242 +            var x = cosPhi * sinTheta;
   1.243 +            var y = cosTheta;
   1.244 +            var z = sinPhi * sinTheta;
   1.245 +            var u = 1-(longNumber/longs);
   1.246 +            var v = latNumber/lats;
   1.247 +            
   1.248 +            normalData.push(x);
   1.249 +            normalData.push(y);
   1.250 +            normalData.push(z);
   1.251 +            texCoordData.push(u);
   1.252 +            texCoordData.push(v);
   1.253 +            geometryData.push(radius * x);
   1.254 +            geometryData.push(radius * y);
   1.255 +            geometryData.push(radius * z);
   1.256 +        }
   1.257 +    }
   1.258 +    
   1.259 +    longs += 1;
   1.260 +    for (var latNumber = 0; latNumber < lats; ++latNumber) {
   1.261 +        for (var longNumber = 0; longNumber < longs; ++longNumber) {
   1.262 +            var first = (latNumber * longs) + (longNumber % longs);
   1.263 +            var second = first + longs;
   1.264 +            indexData.push(first);
   1.265 +            indexData.push(second);
   1.266 +            indexData.push(first+1);
   1.267 +
   1.268 +            indexData.push(second);
   1.269 +            indexData.push(second+1);
   1.270 +            indexData.push(first+1);
   1.271 +        }
   1.272 +    }
   1.273 +    
   1.274 +    var retval = { };
   1.275 +    
   1.276 +    retval.normalObject = ctx.createBuffer();
   1.277 +    ctx.bindBuffer(ctx.ARRAY_BUFFER, retval.normalObject);
   1.278 +    ctx.bufferData(ctx.ARRAY_BUFFER, new WebGLFloatArray(normalData), ctx.STATIC_DRAW);
   1.279 +
   1.280 +    retval.texCoordObject = ctx.createBuffer();
   1.281 +    ctx.bindBuffer(ctx.ARRAY_BUFFER, retval.texCoordObject);
   1.282 +    ctx.bufferData(ctx.ARRAY_BUFFER, new WebGLFloatArray(texCoordData), ctx.STATIC_DRAW);
   1.283 +
   1.284 +    retval.vertexObject = ctx.createBuffer();
   1.285 +    ctx.bindBuffer(ctx.ARRAY_BUFFER, retval.vertexObject);
   1.286 +    ctx.bufferData(ctx.ARRAY_BUFFER, new WebGLFloatArray(geometryData), ctx.STATIC_DRAW);
   1.287 +    
   1.288 +    retval.numIndices = indexData.length;
   1.289 +    retval.indexObject = ctx.createBuffer();
   1.290 +    ctx.bindBuffer(ctx.ELEMENT_ARRAY_BUFFER, retval.indexObject);
   1.291 +    ctx.bufferData(ctx.ELEMENT_ARRAY_BUFFER, new WebGLUnsignedShortArray(indexData), ctx.STREAM_DRAW);
   1.292 +    
   1.293 +    return retval;
   1.294 +}
   1.295 +
   1.296 +//
   1.297 +// loadObj
   1.298 +//
   1.299 +// Load a .obj file from the passed URL. Return an object with a 'loaded' property set to false.
   1.300 +// When the object load is complete, the 'loaded' property becomes true and the following 
   1.301 +// properties are set:
   1.302 +//
   1.303 +//  normalObject        WebGLBuffer object for normals
   1.304 +//  texCoordObject      WebGLBuffer object for texCoords
   1.305 +//  vertexObject        WebGLBuffer object for vertices
   1.306 +//  indexObject         WebGLBuffer object for indices
   1.307 +//  numIndices          The number of indices in the indexObject
   1.308 +//  
   1.309 +function loadObj(ctx, url)
   1.310 +{
   1.311 +    var obj = { loaded : false };
   1.312 +    obj.ctx = ctx;
   1.313 +    var req = new XMLHttpRequest();
   1.314 +    req.obj = obj;
   1.315 +    req.onreadystatechange = function () { processLoadObj(req) };
   1.316 +    req.open("GET", url, true);
   1.317 +    req.send(null);
   1.318 +    return obj;
   1.319 +}
   1.320 +
   1.321 +function processLoadObj(req) 
   1.322 +{
   1.323 +    console.log("req="+req)
   1.324 +    // only if req shows "complete"
   1.325 +    if (req.readyState == 4) {
   1.326 +        doLoadObj(req.obj, req.responseText);
   1.327 +    }
   1.328 +}
   1.329 +
   1.330 +function doLoadObj(obj, text)
   1.331 +{
   1.332 +    vertexArray = [ ];
   1.333 +    normalArray = [ ];
   1.334 +    textureArray = [ ];
   1.335 +    indexArray = [ ];
   1.336 +    
   1.337 +    var vertex = [ ];
   1.338 +    var normal = [ ];
   1.339 +    var texture = [ ];
   1.340 +    var facemap = { };
   1.341 +    var index = 0;
   1.342 +        
   1.343 +    var lines = text.split("\n");
   1.344 +    for (var lineIndex in lines) {
   1.345 +        var line = lines[lineIndex].replace(/[ \t]+/g, " ").replace(/\s\s*$/, "");
   1.346 +        
   1.347 +        // ignore comments
   1.348 +        if (line[0] == "#")
   1.349 +            continue;
   1.350 +            
   1.351 +        var array = line.split(" ");
   1.352 +        if (array[0] == "v") {
   1.353 +            // vertex
   1.354 +            vertex.push(parseFloat(array[1]));
   1.355 +            vertex.push(parseFloat(array[2]));
   1.356 +            vertex.push(parseFloat(array[3]));
   1.357 +        }
   1.358 +        else if (array[0] == "vt") {
   1.359 +            // normal
   1.360 +            texture.push(parseFloat(array[1]));
   1.361 +            texture.push(parseFloat(array[2]));
   1.362 +        }
   1.363 +        else if (array[0] == "vn") {
   1.364 +            // normal
   1.365 +            normal.push(parseFloat(array[1]));
   1.366 +            normal.push(parseFloat(array[2]));
   1.367 +            normal.push(parseFloat(array[3]));
   1.368 +        }
   1.369 +        else if (array[0] == "f") {
   1.370 +            // face
   1.371 +            if (array.length != 4) {
   1.372 +                console.log("*** Error: face '"+line+"' not handled");
   1.373 +                continue;
   1.374 +            }
   1.375 +            
   1.376 +            for (var i = 1; i < 4; ++i) {
   1.377 +                if (!(array[i] in facemap)) {
   1.378 +                    // add a new entry to the map and arrays
   1.379 +                    var f = array[i].split("/");
   1.380 +                    var vtx, nor, tex;
   1.381 +                    
   1.382 +                    if (f.length == 1) {
   1.383 +                        vtx = parseInt(f[0]) - 1;
   1.384 +                        nor = vtx;
   1.385 +                        tex = vtx;
   1.386 +                    }
   1.387 +                    else if (f.length = 3) {
   1.388 +                        vtx = parseInt(f[0]) - 1;
   1.389 +                        tex = parseInt(f[1]) - 1;
   1.390 +                        nor = parseInt(f[2]) - 1;
   1.391 +                    }
   1.392 +                    else {
   1.393 +                        console.log("*** Error: did not understand face '"+array[i]+"'");
   1.394 +                        return null;
   1.395 +                    }
   1.396 +                    
   1.397 +                    // do the vertices
   1.398 +                    var x = 0;
   1.399 +                    var y = 0;
   1.400 +                    var z = 0;
   1.401 +                    if (vtx * 3 + 2 < vertex.length) {
   1.402 +                        x = vertex[vtx*3];
   1.403 +                        y = vertex[vtx*3+1];
   1.404 +                        z = vertex[vtx*3+2];
   1.405 +                    }
   1.406 +                    vertexArray.push(x);
   1.407 +                    vertexArray.push(y);
   1.408 +                    vertexArray.push(z);
   1.409 +                    
   1.410 +                    // do the textures
   1.411 +                    x = 0;
   1.412 +                    y = 0;
   1.413 +                    if (tex * 2 + 1 < texture.length) {
   1.414 +                        x = texture[tex*2];
   1.415 +                        y = texture[tex*2+1];
   1.416 +                    }
   1.417 +                    textureArray.push(x);
   1.418 +                    textureArray.push(y);
   1.419 +                    
   1.420 +                    // do the normals
   1.421 +                    x = 0;
   1.422 +                    y = 0;
   1.423 +                    z = 1;
   1.424 +                    if (nor * 3 + 2 < normal.length) {
   1.425 +                        x = normal[nor*3];
   1.426 +                        y = normal[nor*3+1];
   1.427 +                        z = normal[nor*3+2];
   1.428 +                    }
   1.429 +                    normalArray.push(x);
   1.430 +                    normalArray.push(y);
   1.431 +                    normalArray.push(z);
   1.432 +                    
   1.433 +                    facemap[array[i]] = index++;
   1.434 +                }
   1.435 +                
   1.436 +                indexArray.push(facemap[array[i]]);
   1.437 +            }
   1.438 +        }
   1.439 +    }
   1.440 +
   1.441 +    // set the VBOs
   1.442 +    obj.normalObject = obj.ctx.createBuffer();
   1.443 +    obj.ctx.bindBuffer(obj.ctx.ARRAY_BUFFER, obj.normalObject);
   1.444 +    obj.ctx.bufferData(obj.ctx.ARRAY_BUFFER, new WebGLFloatArray(normalArray), obj.ctx.STATIC_DRAW);
   1.445 +
   1.446 +    obj.texCoordObject = obj.ctx.createBuffer();
   1.447 +    obj.ctx.bindBuffer(obj.ctx.ARRAY_BUFFER, obj.texCoordObject);
   1.448 +    obj.ctx.bufferData(obj.ctx.ARRAY_BUFFER, new WebGLFloatArray(textureArray), obj.ctx.STATIC_DRAW);
   1.449 +
   1.450 +    obj.vertexObject = obj.ctx.createBuffer();
   1.451 +    obj.ctx.bindBuffer(obj.ctx.ARRAY_BUFFER, obj.vertexObject);
   1.452 +    obj.ctx.bufferData(obj.ctx.ARRAY_BUFFER, new WebGLFloatArray(vertexArray), obj.ctx.STATIC_DRAW);
   1.453 +    
   1.454 +    obj.numIndices = indexArray.length;
   1.455 +    obj.indexObject = obj.ctx.createBuffer();
   1.456 +    obj.ctx.bindBuffer(obj.ctx.ELEMENT_ARRAY_BUFFER, obj.indexObject);
   1.457 +    obj.ctx.bufferData(obj.ctx.ELEMENT_ARRAY_BUFFER, new WebGLUnsignedShortArray(indexArray), obj.ctx.STREAM_DRAW);
   1.458 +    
   1.459 +    obj.loaded = true;
   1.460 +}
   1.461 +
   1.462 +//
   1.463 +// loadImageTexture
   1.464 +//
   1.465 +// Load the image at the passed url, place it in a new WebGLTexture object and return the WebGLTexture.
   1.466 +//
   1.467 +function loadImageTexture(ctx, url)
   1.468 +{
   1.469 +    var texture = ctx.createTexture();
   1.470 +    texture.image = new Image();
   1.471 +    texture.image.onload = function() { doLoadImageTexture(ctx, texture.image, texture) }
   1.472 +    texture.image.src = url;
   1.473 +    return texture;
   1.474 +}
   1.475 +
   1.476 +function doLoadImageTexture(ctx, image, texture)
   1.477 +{
   1.478 +    ctx.enable(ctx.TEXTURE_2D);
   1.479 +    ctx.bindTexture(ctx.TEXTURE_2D, texture);
   1.480 +    ctx.texImage2D(ctx.TEXTURE_2D, 0, image);
   1.481 +    ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_MAG_FILTER, ctx.LINEAR);
   1.482 +    ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_MIN_FILTER, ctx.LINEAR_MIPMAP_LINEAR);
   1.483 +    ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_WRAP_S, ctx.CLAMP_TO_EDGE);
   1.484 +    ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_WRAP_T, ctx.CLAMP_TO_EDGE);
   1.485 +    ctx.generateMipmap(ctx.TEXTURE_2D)
   1.486 +    ctx.bindTexture(ctx.TEXTURE_2D, 0);
   1.487 +}
   1.488 +
   1.489 +//
   1.490 +// Framerate object
   1.491 +//
   1.492 +// This object keeps track of framerate and displays it as the innerHTML text of the 
   1.493 +// HTML element with the passed id. Once created you call snapshot at the end
   1.494 +// of every rendering cycle. Every 500ms the framerate is updated in the HTML element.
   1.495 +//
   1.496 +Framerate = function(id)
   1.497 +{
   1.498 +    this.numFramerates = 10;
   1.499 +    this.framerateUpdateInterval = 500;
   1.500 +    this.id = id;
   1.501 +
   1.502 +    this.renderTime = -1;
   1.503 +    this.framerates = [ ];
   1.504 +    self = this;
   1.505 +    var fr = function() { self.updateFramerate() }
   1.506 +    setInterval(fr, this.framerateUpdateInterval);
   1.507 +}
   1.508 +
   1.509 +Framerate.prototype.updateFramerate = function()
   1.510 +{
   1.511 +    var tot = 0;
   1.512 +    for (var i = 0; i < this.framerates.length; ++i)
   1.513 +        tot += this.framerates[i];
   1.514 +        
   1.515 +    var framerate = tot / this.framerates.length;
   1.516 +    framerate = Math.round(framerate);
   1.517 +    document.getElementById(this.id).innerHTML = "Framerate:"+framerate+"fps";
   1.518 +}
   1.519 +
   1.520 +Framerate.prototype.snapshot = function()
   1.521 +{
   1.522 +    if (this.renderTime < 0)
   1.523 +        this.renderTime = new Date().getTime();
   1.524 +    else {
   1.525 +        var newTime = new Date().getTime();
   1.526 +        var t = newTime - this.renderTime;
   1.527 +        var framerate = 1000/t;
   1.528 +        this.framerates.push(framerate);
   1.529 +        while (this.framerates.length > this.numFramerates)
   1.530 +            this.framerates.shift();
   1.531 +        this.renderTime = newTime;
   1.532 +    }
   1.533 +}
   1.534 \ No newline at end of file