scripts/utils3d.js
author Eugen Sawin <sawine@me73.com>
Wed, 27 Apr 2011 14:41:04 +0200
changeset 31 744b427d1379
parent 1 92e65293655d
permissions -rwxr-xr-x
Switched to per-fragment lighting.
sawine@2
     1
//
sawine@2
     2
// initWebGL
sawine@2
     3
//
sawine@2
     4
// Initialize the Canvas element with the passed name as a WebGL object and return the
sawine@2
     5
// WebGLRenderingContext. 
sawine@2
     6
//
sawine@2
     7
// Load shaders with the passed names and create a program with them. Return this program 
sawine@2
     8
// in the 'program' property of the returned context.
sawine@2
     9
//
sawine@2
    10
// For each string in the passed attribs array, bind an attrib with that name at that index.
sawine@2
    11
// Once the attribs are bound, link the program and then use it.
sawine@2
    12
//
sawine@2
    13
// Set the clear color to the passed array (4 values) and set the clear depth to the passed value.
sawine@2
    14
// Enable depth testing and blending with a blend func of (SRC_ALPHA, ONE_MINUS_SRC_ALPHA)
sawine@2
    15
//
sawine@2
    16
function initWebGL(canvasName, vshader, fshader, attribs, clearColor, clearDepth)
sawine@2
    17
{
sawine@2
    18
    var canvas = document.getElementById(canvasName);
sawine@2
    19
    var gl = canvas.getContext("webkit-3d");
sawine@2
    20
sawine@2
    21
    // create our shaders
sawine@2
    22
    var vertexShader = loadShader(gl, vshader);
sawine@2
    23
    var fragmentShader = loadShader(gl, fshader);
sawine@2
    24
sawine@2
    25
    if (!vertexShader || !fragmentShader)
sawine@2
    26
        return null;
sawine@2
    27
sawine@2
    28
    // Create the program object
sawine@2
    29
    gl.program = gl.createProgram();
sawine@2
    30
sawine@2
    31
    if (!gl.program)
sawine@2
    32
        return null;
sawine@2
    33
sawine@2
    34
    // Attach our two shaders to the program
sawine@2
    35
    gl.attachShader (gl.program, vertexShader);
sawine@2
    36
    gl.attachShader (gl.program, fragmentShader);
sawine@2
    37
sawine@2
    38
    // Bind attributes
sawine@2
    39
    for (var i in attribs)
sawine@2
    40
        gl.bindAttribLocation (gl.program, i, attribs[i]);
sawine@2
    41
sawine@2
    42
    // Link the program
sawine@2
    43
    gl.linkProgram(gl.program);
sawine@2
    44
sawine@2
    45
    // Check the link status
sawine@2
    46
    var linked = gl.getProgrami(gl.program, gl.LINK_STATUS);
sawine@2
    47
    if (!linked) {
sawine@2
    48
        // something went wrong with the link
sawine@2
    49
        var error = gl.getProgramInfoLog (gl.program);
sawine@2
    50
        console.log("Error in program linking:"+error);
sawine@2
    51
sawine@2
    52
        gl.deleteProgram(gl.program);
sawine@2
    53
        gl.deleteProgram(fragmentShader);
sawine@2
    54
        gl.deleteProgram(vertexShader);
sawine@2
    55
sawine@2
    56
        return null;
sawine@2
    57
    }
sawine@2
    58
sawine@2
    59
    gl.useProgram(gl.program);
sawine@2
    60
sawine@2
    61
    gl.clearColor (clearColor[0], clearColor[1], clearColor[2], clearColor[3]);
sawine@2
    62
    gl.clearDepth (clearDepth);
sawine@2
    63
sawine@2
    64
    gl.enable(gl.DEPTH_TEST);
sawine@2
    65
    gl.enable(gl.BLEND);
sawine@2
    66
    gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
sawine@2
    67
sawine@2
    68
    return gl;
sawine@2
    69
}
sawine@2
    70
sawine@2
    71
//
sawine@2
    72
// loadShader
sawine@2
    73
//
sawine@2
    74
// 'shaderId' is the id of a <script> element containing the shader source string.
sawine@2
    75
// Load this shader and return the WebGLShader object corresponding to it.
sawine@2
    76
//
sawine@2
    77
function loadShader(ctx, shaderId)
sawine@2
    78
{
sawine@2
    79
    var shaderScript = document.getElementById(shaderId);
sawine@2
    80
    if (!shaderScript) {
sawine@2
    81
        console.log("*** Error: shader script '"+shaderId+"' not found");
sawine@2
    82
        return null;
sawine@2
    83
    }
sawine@2
    84
        
sawine@2
    85
    if (shaderScript.type == "x-shader/x-vertex")
sawine@2
    86
        var shaderType = ctx.VERTEX_SHADER;
sawine@2
    87
    else if (shaderScript.type == "x-shader/x-fragment")
sawine@2
    88
        var shaderType = ctx.FRAGMENT_SHADER;
sawine@2
    89
    else {
sawine@2
    90
        console.log("*** Error: shader script '"+shaderId+"' of undefined type '"+shaderScript.type+"'");       
sawine@2
    91
        return null;
sawine@2
    92
    }
sawine@2
    93
sawine@2
    94
    // Create the shader object
sawine@2
    95
    var shader = ctx.createShader(shaderType);
sawine@2
    96
    if (shader == null) {
sawine@2
    97
        console.log("*** Error: unable to create shader '"+shaderId+"'");       
sawine@2
    98
        return null;
sawine@2
    99
    }
sawine@2
   100
sawine@2
   101
    // Load the shader source
sawine@2
   102
    ctx.shaderSource(shader, shaderScript.text);
sawine@2
   103
sawine@2
   104
    // Compile the shader
sawine@2
   105
    ctx.compileShader(shader);
sawine@2
   106
sawine@2
   107
    // Check the compile status
sawine@2
   108
    var compiled = ctx.getShaderi(shader, ctx.COMPILE_STATUS);
sawine@2
   109
    if (!compiled) {
sawine@2
   110
        // Something went wrong during compilation; get the error
sawine@2
   111
        var error = ctx.getShaderInfoLog(shader);
sawine@2
   112
        console.log("*** Error compiling shader '"+shaderId+"':"+error);
sawine@2
   113
        ctx.deleteShader(shader);
sawine@2
   114
        return null;
sawine@2
   115
    }
sawine@2
   116
sawine@2
   117
    return shader;
sawine@2
   118
}
sawine@2
   119
sawine@2
   120
// 
sawine@2
   121
// makeBox
sawine@2
   122
//
sawine@2
   123
// Create a box with vertices, normals and texCoords. Create VBOs for each as well as the index array.
sawine@2
   124
// Return an object with the following properties:
sawine@2
   125
//
sawine@2
   126
//  normalObject        WebGLBuffer object for normals
sawine@2
   127
//  texCoordObject      WebGLBuffer object for texCoords
sawine@2
   128
//  vertexObject        WebGLBuffer object for vertices
sawine@2
   129
//  indexObject         WebGLBuffer object for indices
sawine@2
   130
//  numIndices          The number of indices in the indexObject
sawine@2
   131
// 
sawine@2
   132
function makeBox(ctx)
sawine@2
   133
{
sawine@2
   134
    // box
sawine@2
   135
    //    v6----- v5
sawine@2
   136
    //   /|      /|
sawine@2
   137
    //  v1------v0|
sawine@2
   138
    //  | |     | |
sawine@2
   139
    //  | |v7---|-|v4
sawine@2
   140
    //  |/      |/
sawine@2
   141
    //  v2------v3
sawine@2
   142
    //
sawine@2
   143
    // vertex coords array
sawine@2
   144
    var vertices = new WebGLFloatArray(
sawine@2
   145
        [  1, 1, 1,  -1, 1, 1,  -1,-1, 1,   1,-1, 1,    // v0-v1-v2-v3 front
sawine@2
   146
           1, 1, 1,   1,-1, 1,   1,-1,-1,   1, 1,-1,    // v0-v3-v4-v5 right
sawine@2
   147
           1, 1, 1,   1, 1,-1,  -1, 1,-1,  -1, 1, 1,    // v0-v5-v6-v1 top
sawine@2
   148
          -1, 1, 1,  -1, 1,-1,  -1,-1,-1,  -1,-1, 1,    // v1-v6-v7-v2 left
sawine@2
   149
          -1,-1,-1,   1,-1,-1,   1,-1, 1,  -1,-1, 1,    // v7-v4-v3-v2 bottom
sawine@2
   150
           1,-1,-1,  -1,-1,-1,  -1, 1,-1,   1, 1,-1 ]   // v4-v7-v6-v5 back
sawine@2
   151
    );
sawine@2
   152
sawine@2
   153
    // normal array
sawine@2
   154
    var normals = new WebGLFloatArray(
sawine@2
   155
        [  0, 0, 1,   0, 0, 1,   0, 0, 1,   0, 0, 1,     // v0-v1-v2-v3 front
sawine@2
   156
           1, 0, 0,   1, 0, 0,   1, 0, 0,   1, 0, 0,     // v0-v3-v4-v5 right
sawine@2
   157
           0, 1, 0,   0, 1, 0,   0, 1, 0,   0, 1, 0,     // v0-v5-v6-v1 top
sawine@2
   158
          -1, 0, 0,  -1, 0, 0,  -1, 0, 0,  -1, 0, 0,     // v1-v6-v7-v2 left
sawine@2
   159
           0,-1, 0,   0,-1, 0,   0,-1, 0,   0,-1, 0,     // v7-v4-v3-v2 bottom
sawine@2
   160
           0, 0,-1,   0, 0,-1,   0, 0,-1,   0, 0,-1 ]    // v4-v7-v6-v5 back
sawine@2
   161
       );
sawine@2
   162
sawine@2
   163
sawine@2
   164
    // texCoord array
sawine@2
   165
    var texCoords = new WebGLFloatArray(
sawine@2
   166
        [  1, 1,   0, 1,   0, 0,   1, 0,    // v0-v1-v2-v3 front
sawine@2
   167
           0, 1,   0, 0,   1, 0,   1, 1,    // v0-v3-v4-v5 right
sawine@2
   168
           1, 0,   1, 1,   0, 1,   0, 0,    // v0-v5-v6-v1 top
sawine@2
   169
           1, 1,   0, 1,   0, 0,   1, 0,    // v1-v6-v7-v2 left
sawine@2
   170
           0, 0,   1, 0,   1, 1,   0, 1,    // v7-v4-v3-v2 bottom
sawine@2
   171
           0, 0,   1, 0,   1, 1,   0, 1 ]   // v4-v7-v6-v5 back
sawine@2
   172
       );
sawine@2
   173
sawine@2
   174
    // index array
sawine@2
   175
    var indices = new WebGLUnsignedByteArray(
sawine@2
   176
        [  0, 1, 2,   0, 2, 3,    // front
sawine@2
   177
           4, 5, 6,   4, 6, 7,    // right
sawine@2
   178
           8, 9,10,   8,10,11,    // top
sawine@2
   179
          12,13,14,  12,14,15,    // left
sawine@2
   180
          16,17,18,  16,18,19,    // bottom
sawine@2
   181
          20,21,22,  20,22,23 ]   // back
sawine@2
   182
      );
sawine@2
   183
sawine@2
   184
    var retval = { };
sawine@2
   185
    
sawine@2
   186
    retval.normalObject = ctx.createBuffer();
sawine@2
   187
    ctx.bindBuffer(ctx.ARRAY_BUFFER, retval.normalObject);
sawine@2
   188
    ctx.bufferData(ctx.ARRAY_BUFFER, normals, ctx.STATIC_DRAW);
sawine@2
   189
    
sawine@2
   190
    retval.texCoordObject = ctx.createBuffer();
sawine@2
   191
    ctx.bindBuffer(ctx.ARRAY_BUFFER, retval.texCoordObject);
sawine@2
   192
    ctx.bufferData(ctx.ARRAY_BUFFER, texCoords, ctx.STATIC_DRAW);
sawine@2
   193
sawine@2
   194
    retval.vertexObject = ctx.createBuffer();
sawine@2
   195
    ctx.bindBuffer(ctx.ARRAY_BUFFER, retval.vertexObject);
sawine@2
   196
    ctx.bufferData(ctx.ARRAY_BUFFER, vertices, ctx.STATIC_DRAW);
sawine@2
   197
    
sawine@2
   198
    ctx.bindBuffer(ctx.ARRAY_BUFFER, 0);
sawine@2
   199
sawine@2
   200
    retval.indexObject = ctx.createBuffer();
sawine@2
   201
    ctx.bindBuffer(ctx.ELEMENT_ARRAY_BUFFER, retval.indexObject);
sawine@2
   202
    ctx.bufferData(ctx.ELEMENT_ARRAY_BUFFER, indices, ctx.STATIC_DRAW);
sawine@2
   203
    ctx.bindBuffer(ctx.ELEMENT_ARRAY_BUFFER, 0);
sawine@2
   204
    
sawine@2
   205
    retval.numIndices = indices.length;
sawine@2
   206
sawine@2
   207
    return retval;
sawine@2
   208
}
sawine@2
   209
sawine@2
   210
// 
sawine@2
   211
// makeSphere
sawine@2
   212
//
sawine@2
   213
// Create a sphere with the passed number of latitude and longitude bands and the passed radius. 
sawine@2
   214
// Sphere has vertices, normals and texCoords. Create VBOs for each as well as the index array.
sawine@2
   215
// Return an object with the following properties:
sawine@2
   216
//
sawine@2
   217
//  normalObject        WebGLBuffer object for normals
sawine@2
   218
//  texCoordObject      WebGLBuffer object for texCoords
sawine@2
   219
//  vertexObject        WebGLBuffer object for vertices
sawine@2
   220
//  indexObject         WebGLBuffer object for indices
sawine@2
   221
//  numIndices          The number of indices in the indexObject
sawine@2
   222
// 
sawine@2
   223
function makeSphere(ctx, radius, lats, longs)
sawine@2
   224
{
sawine@2
   225
    var geometryData = [ ];
sawine@2
   226
    var normalData = [ ];
sawine@2
   227
    var texCoordData = [ ];
sawine@2
   228
    var indexData = [ ];
sawine@2
   229
    
sawine@2
   230
    for (var latNumber = 0; latNumber <= lats; ++latNumber) {
sawine@2
   231
        for (var longNumber = 0; longNumber <= longs; ++longNumber) {
sawine@2
   232
            var theta = latNumber * Math.PI / lats;
sawine@2
   233
            var phi = longNumber * 2 * Math.PI / longs;
sawine@2
   234
            var sinTheta = Math.sin(theta);
sawine@2
   235
            var sinPhi = Math.sin(phi);
sawine@2
   236
            var cosTheta = Math.cos(theta);
sawine@2
   237
            var cosPhi = Math.cos(phi);
sawine@2
   238
            
sawine@2
   239
            var x = cosPhi * sinTheta;
sawine@2
   240
            var y = cosTheta;
sawine@2
   241
            var z = sinPhi * sinTheta;
sawine@2
   242
            var u = 1-(longNumber/longs);
sawine@2
   243
            var v = latNumber/lats;
sawine@2
   244
            
sawine@2
   245
            normalData.push(x);
sawine@2
   246
            normalData.push(y);
sawine@2
   247
            normalData.push(z);
sawine@2
   248
            texCoordData.push(u);
sawine@2
   249
            texCoordData.push(v);
sawine@2
   250
            geometryData.push(radius * x);
sawine@2
   251
            geometryData.push(radius * y);
sawine@2
   252
            geometryData.push(radius * z);
sawine@2
   253
        }
sawine@2
   254
    }
sawine@2
   255
    
sawine@2
   256
    longs += 1;
sawine@2
   257
    for (var latNumber = 0; latNumber < lats; ++latNumber) {
sawine@2
   258
        for (var longNumber = 0; longNumber < longs; ++longNumber) {
sawine@2
   259
            var first = (latNumber * longs) + (longNumber % longs);
sawine@2
   260
            var second = first + longs;
sawine@2
   261
            indexData.push(first);
sawine@2
   262
            indexData.push(second);
sawine@2
   263
            indexData.push(first+1);
sawine@2
   264
sawine@2
   265
            indexData.push(second);
sawine@2
   266
            indexData.push(second+1);
sawine@2
   267
            indexData.push(first+1);
sawine@2
   268
        }
sawine@2
   269
    }
sawine@2
   270
    
sawine@2
   271
    var retval = { };
sawine@2
   272
    
sawine@2
   273
    retval.normalObject = ctx.createBuffer();
sawine@2
   274
    ctx.bindBuffer(ctx.ARRAY_BUFFER, retval.normalObject);
sawine@2
   275
    ctx.bufferData(ctx.ARRAY_BUFFER, new WebGLFloatArray(normalData), ctx.STATIC_DRAW);
sawine@2
   276
sawine@2
   277
    retval.texCoordObject = ctx.createBuffer();
sawine@2
   278
    ctx.bindBuffer(ctx.ARRAY_BUFFER, retval.texCoordObject);
sawine@2
   279
    ctx.bufferData(ctx.ARRAY_BUFFER, new WebGLFloatArray(texCoordData), ctx.STATIC_DRAW);
sawine@2
   280
sawine@2
   281
    retval.vertexObject = ctx.createBuffer();
sawine@2
   282
    ctx.bindBuffer(ctx.ARRAY_BUFFER, retval.vertexObject);
sawine@2
   283
    ctx.bufferData(ctx.ARRAY_BUFFER, new WebGLFloatArray(geometryData), ctx.STATIC_DRAW);
sawine@2
   284
    
sawine@2
   285
    retval.numIndices = indexData.length;
sawine@2
   286
    retval.indexObject = ctx.createBuffer();
sawine@2
   287
    ctx.bindBuffer(ctx.ELEMENT_ARRAY_BUFFER, retval.indexObject);
sawine@2
   288
    ctx.bufferData(ctx.ELEMENT_ARRAY_BUFFER, new WebGLUnsignedShortArray(indexData), ctx.STREAM_DRAW);
sawine@2
   289
    
sawine@2
   290
    return retval;
sawine@2
   291
}
sawine@2
   292
sawine@2
   293
//
sawine@2
   294
// loadObj
sawine@2
   295
//
sawine@2
   296
// Load a .obj file from the passed URL. Return an object with a 'loaded' property set to false.
sawine@2
   297
// When the object load is complete, the 'loaded' property becomes true and the following 
sawine@2
   298
// properties are set:
sawine@2
   299
//
sawine@2
   300
//  normalObject        WebGLBuffer object for normals
sawine@2
   301
//  texCoordObject      WebGLBuffer object for texCoords
sawine@2
   302
//  vertexObject        WebGLBuffer object for vertices
sawine@2
   303
//  indexObject         WebGLBuffer object for indices
sawine@2
   304
//  numIndices          The number of indices in the indexObject
sawine@2
   305
//  
sawine@2
   306
function loadObj(ctx, url)
sawine@2
   307
{
sawine@2
   308
    var obj = { loaded : false };
sawine@2
   309
    obj.ctx = ctx;
sawine@2
   310
    var req = new XMLHttpRequest();
sawine@2
   311
    req.obj = obj;
sawine@2
   312
    req.onreadystatechange = function () { processLoadObj(req) };
sawine@2
   313
    req.open("GET", url, true);
sawine@2
   314
    req.send(null);
sawine@2
   315
    return obj;
sawine@2
   316
}
sawine@2
   317
sawine@2
   318
function processLoadObj(req) 
sawine@2
   319
{
sawine@2
   320
    console.log("req="+req)
sawine@2
   321
    // only if req shows "complete"
sawine@2
   322
    if (req.readyState == 4) {
sawine@2
   323
        doLoadObj(req.obj, req.responseText);
sawine@2
   324
    }
sawine@2
   325
}
sawine@2
   326
sawine@2
   327
function doLoadObj(obj, text)
sawine@2
   328
{
sawine@2
   329
    vertexArray = [ ];
sawine@2
   330
    normalArray = [ ];
sawine@2
   331
    textureArray = [ ];
sawine@2
   332
    indexArray = [ ];
sawine@2
   333
    
sawine@2
   334
    var vertex = [ ];
sawine@2
   335
    var normal = [ ];
sawine@2
   336
    var texture = [ ];
sawine@2
   337
    var facemap = { };
sawine@2
   338
    var index = 0;
sawine@2
   339
        
sawine@2
   340
    var lines = text.split("\n");
sawine@2
   341
    for (var lineIndex in lines) {
sawine@2
   342
        var line = lines[lineIndex].replace(/[ \t]+/g, " ").replace(/\s\s*$/, "");
sawine@2
   343
        
sawine@2
   344
        // ignore comments
sawine@2
   345
        if (line[0] == "#")
sawine@2
   346
            continue;
sawine@2
   347
            
sawine@2
   348
        var array = line.split(" ");
sawine@2
   349
        if (array[0] == "v") {
sawine@2
   350
            // vertex
sawine@2
   351
            vertex.push(parseFloat(array[1]));
sawine@2
   352
            vertex.push(parseFloat(array[2]));
sawine@2
   353
            vertex.push(parseFloat(array[3]));
sawine@2
   354
        }
sawine@2
   355
        else if (array[0] == "vt") {
sawine@2
   356
            // normal
sawine@2
   357
            texture.push(parseFloat(array[1]));
sawine@2
   358
            texture.push(parseFloat(array[2]));
sawine@2
   359
        }
sawine@2
   360
        else if (array[0] == "vn") {
sawine@2
   361
            // normal
sawine@2
   362
            normal.push(parseFloat(array[1]));
sawine@2
   363
            normal.push(parseFloat(array[2]));
sawine@2
   364
            normal.push(parseFloat(array[3]));
sawine@2
   365
        }
sawine@2
   366
        else if (array[0] == "f") {
sawine@2
   367
            // face
sawine@2
   368
            if (array.length != 4) {
sawine@2
   369
                console.log("*** Error: face '"+line+"' not handled");
sawine@2
   370
                continue;
sawine@2
   371
            }
sawine@2
   372
            
sawine@2
   373
            for (var i = 1; i < 4; ++i) {
sawine@2
   374
                if (!(array[i] in facemap)) {
sawine@2
   375
                    // add a new entry to the map and arrays
sawine@2
   376
                    var f = array[i].split("/");
sawine@2
   377
                    var vtx, nor, tex;
sawine@2
   378
                    
sawine@2
   379
                    if (f.length == 1) {
sawine@2
   380
                        vtx = parseInt(f[0]) - 1;
sawine@2
   381
                        nor = vtx;
sawine@2
   382
                        tex = vtx;
sawine@2
   383
                    }
sawine@2
   384
                    else if (f.length = 3) {
sawine@2
   385
                        vtx = parseInt(f[0]) - 1;
sawine@2
   386
                        tex = parseInt(f[1]) - 1;
sawine@2
   387
                        nor = parseInt(f[2]) - 1;
sawine@2
   388
                    }
sawine@2
   389
                    else {
sawine@2
   390
                        console.log("*** Error: did not understand face '"+array[i]+"'");
sawine@2
   391
                        return null;
sawine@2
   392
                    }
sawine@2
   393
                    
sawine@2
   394
                    // do the vertices
sawine@2
   395
                    var x = 0;
sawine@2
   396
                    var y = 0;
sawine@2
   397
                    var z = 0;
sawine@2
   398
                    if (vtx * 3 + 2 < vertex.length) {
sawine@2
   399
                        x = vertex[vtx*3];
sawine@2
   400
                        y = vertex[vtx*3+1];
sawine@2
   401
                        z = vertex[vtx*3+2];
sawine@2
   402
                    }
sawine@2
   403
                    vertexArray.push(x);
sawine@2
   404
                    vertexArray.push(y);
sawine@2
   405
                    vertexArray.push(z);
sawine@2
   406
                    
sawine@2
   407
                    // do the textures
sawine@2
   408
                    x = 0;
sawine@2
   409
                    y = 0;
sawine@2
   410
                    if (tex * 2 + 1 < texture.length) {
sawine@2
   411
                        x = texture[tex*2];
sawine@2
   412
                        y = texture[tex*2+1];
sawine@2
   413
                    }
sawine@2
   414
                    textureArray.push(x);
sawine@2
   415
                    textureArray.push(y);
sawine@2
   416
                    
sawine@2
   417
                    // do the normals
sawine@2
   418
                    x = 0;
sawine@2
   419
                    y = 0;
sawine@2
   420
                    z = 1;
sawine@2
   421
                    if (nor * 3 + 2 < normal.length) {
sawine@2
   422
                        x = normal[nor*3];
sawine@2
   423
                        y = normal[nor*3+1];
sawine@2
   424
                        z = normal[nor*3+2];
sawine@2
   425
                    }
sawine@2
   426
                    normalArray.push(x);
sawine@2
   427
                    normalArray.push(y);
sawine@2
   428
                    normalArray.push(z);
sawine@2
   429
                    
sawine@2
   430
                    facemap[array[i]] = index++;
sawine@2
   431
                }
sawine@2
   432
                
sawine@2
   433
                indexArray.push(facemap[array[i]]);
sawine@2
   434
            }
sawine@2
   435
        }
sawine@2
   436
    }
sawine@2
   437
sawine@2
   438
    // set the VBOs
sawine@2
   439
    obj.normalObject = obj.ctx.createBuffer();
sawine@2
   440
    obj.ctx.bindBuffer(obj.ctx.ARRAY_BUFFER, obj.normalObject);
sawine@2
   441
    obj.ctx.bufferData(obj.ctx.ARRAY_BUFFER, new WebGLFloatArray(normalArray), obj.ctx.STATIC_DRAW);
sawine@2
   442
sawine@2
   443
    obj.texCoordObject = obj.ctx.createBuffer();
sawine@2
   444
    obj.ctx.bindBuffer(obj.ctx.ARRAY_BUFFER, obj.texCoordObject);
sawine@2
   445
    obj.ctx.bufferData(obj.ctx.ARRAY_BUFFER, new WebGLFloatArray(textureArray), obj.ctx.STATIC_DRAW);
sawine@2
   446
sawine@2
   447
    obj.vertexObject = obj.ctx.createBuffer();
sawine@2
   448
    obj.ctx.bindBuffer(obj.ctx.ARRAY_BUFFER, obj.vertexObject);
sawine@2
   449
    obj.ctx.bufferData(obj.ctx.ARRAY_BUFFER, new WebGLFloatArray(vertexArray), obj.ctx.STATIC_DRAW);
sawine@2
   450
    
sawine@2
   451
    obj.numIndices = indexArray.length;
sawine@2
   452
    obj.indexObject = obj.ctx.createBuffer();
sawine@2
   453
    obj.ctx.bindBuffer(obj.ctx.ELEMENT_ARRAY_BUFFER, obj.indexObject);
sawine@2
   454
    obj.ctx.bufferData(obj.ctx.ELEMENT_ARRAY_BUFFER, new WebGLUnsignedShortArray(indexArray), obj.ctx.STREAM_DRAW);
sawine@2
   455
    
sawine@2
   456
    obj.loaded = true;
sawine@2
   457
}
sawine@2
   458
sawine@2
   459
//
sawine@2
   460
// loadImageTexture
sawine@2
   461
//
sawine@2
   462
// Load the image at the passed url, place it in a new WebGLTexture object and return the WebGLTexture.
sawine@2
   463
//
sawine@2
   464
function loadImageTexture(ctx, url)
sawine@2
   465
{
sawine@2
   466
    var texture = ctx.createTexture();
sawine@2
   467
    texture.image = new Image();
sawine@2
   468
    texture.image.onload = function() { doLoadImageTexture(ctx, texture.image, texture) }
sawine@2
   469
    texture.image.src = url;
sawine@2
   470
    return texture;
sawine@2
   471
}
sawine@2
   472
sawine@2
   473
function doLoadImageTexture(ctx, image, texture)
sawine@2
   474
{
sawine@2
   475
    ctx.enable(ctx.TEXTURE_2D);
sawine@2
   476
    ctx.bindTexture(ctx.TEXTURE_2D, texture);
sawine@2
   477
    ctx.texImage2D(ctx.TEXTURE_2D, 0, image);
sawine@2
   478
    ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_MAG_FILTER, ctx.LINEAR);
sawine@2
   479
    ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_MIN_FILTER, ctx.LINEAR_MIPMAP_LINEAR);
sawine@2
   480
    ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_WRAP_S, ctx.CLAMP_TO_EDGE);
sawine@2
   481
    ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_WRAP_T, ctx.CLAMP_TO_EDGE);
sawine@2
   482
    ctx.generateMipmap(ctx.TEXTURE_2D)
sawine@2
   483
    ctx.bindTexture(ctx.TEXTURE_2D, 0);
sawine@2
   484
}
sawine@2
   485
sawine@2
   486
//
sawine@2
   487
// Framerate object
sawine@2
   488
//
sawine@2
   489
// This object keeps track of framerate and displays it as the innerHTML text of the 
sawine@2
   490
// HTML element with the passed id. Once created you call snapshot at the end
sawine@2
   491
// of every rendering cycle. Every 500ms the framerate is updated in the HTML element.
sawine@2
   492
//
sawine@2
   493
Framerate = function(id)
sawine@2
   494
{
sawine@2
   495
    this.numFramerates = 10;
sawine@2
   496
    this.framerateUpdateInterval = 500;
sawine@2
   497
    this.id = id;
sawine@2
   498
sawine@2
   499
    this.renderTime = -1;
sawine@2
   500
    this.framerates = [ ];
sawine@2
   501
    self = this;
sawine@2
   502
    var fr = function() { self.updateFramerate() }
sawine@2
   503
    setInterval(fr, this.framerateUpdateInterval);
sawine@2
   504
}
sawine@2
   505
sawine@2
   506
Framerate.prototype.updateFramerate = function()
sawine@2
   507
{
sawine@2
   508
    var tot = 0;
sawine@2
   509
    for (var i = 0; i < this.framerates.length; ++i)
sawine@2
   510
        tot += this.framerates[i];
sawine@2
   511
        
sawine@2
   512
    var framerate = tot / this.framerates.length;
sawine@2
   513
    framerate = Math.round(framerate);
sawine@2
   514
    document.getElementById(this.id).innerHTML = "Framerate:"+framerate+"fps";
sawine@2
   515
}
sawine@2
   516
sawine@2
   517
Framerate.prototype.snapshot = function()
sawine@2
   518
{
sawine@2
   519
    if (this.renderTime < 0)
sawine@2
   520
        this.renderTime = new Date().getTime();
sawine@2
   521
    else {
sawine@2
   522
        var newTime = new Date().getTime();
sawine@2
   523
        var t = newTime - this.renderTime;
sawine@2
   524
        var framerate = 1000/t;
sawine@2
   525
        this.framerates.push(framerate);
sawine@2
   526
        while (this.framerates.length > this.numFramerates)
sawine@2
   527
            this.framerates.shift();
sawine@2
   528
        this.renderTime = newTime;
sawine@2
   529
    }
sawine@2
   530
}