content/canvas/test/webgl-conformance/conformance/more/util.js

Wed, 31 Dec 2014 13:27:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 13:27:57 +0100
branch
TOR_BUG_3246
changeset 6
8bccb770b82d
permissions
-rw-r--r--

Ignore runtime configuration files generated during quality assurance.

     1 /*
     2 Utilities for the OpenGL ES 2.0 HTML Canvas context
     4 Copyright (C) 2011  Ilmari Heikkinen <ilmari.heikkinen@gmail.com>
     6 Permission is hereby granted, free of charge, to any person
     7 obtaining a copy of this software and associated documentation
     8 files (the "Software"), to deal in the Software without
     9 restriction, including without limitation the rights to use,
    10 copy, modify, merge, publish, distribute, sublicense, and/or sell
    11 copies of the Software, and to permit persons to whom the
    12 Software is furnished to do so, subject to the following
    13 conditions:
    15 The above copyright notice and this permission notice shall be
    16 included in all copies or substantial portions of the Software.
    18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    19 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
    20 OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
    21 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    22 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
    23 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    24 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
    25 OTHER DEALINGS IN THE SOFTWARE.
    26 */
    28 function loadTexture(gl, elem, mipmaps) {
    29   var tex = gl.createTexture();
    30   gl.bindTexture(gl.TEXTURE_2D, tex);
    31   gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, elem);
    32   if (mipmaps != false)
    33     gl.generateMipmap(gl.TEXTURE_2D);
    34   gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
    35   gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
    36   gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
    37   if (mipmaps)
    38     gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
    39   else
    40     gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
    41   return tex;
    42 }
    44 function getShader(gl, id) {
    45   var shaderScript = document.getElementById(id);
    46   if (!shaderScript) {
    47     throw(new Error("No shader element with id: "+id));
    48   }
    50   var str = "";
    51   var k = shaderScript.firstChild;
    52   while (k) {
    53     if (k.nodeType == 3)
    54       str += k.textContent;
    55     k = k.nextSibling;
    56   }
    58   var shader;
    59   if (shaderScript.type == "x-shader/x-fragment") {
    60     shader = gl.createShader(gl.FRAGMENT_SHADER);
    61   } else if (shaderScript.type == "x-shader/x-vertex") {
    62     shader = gl.createShader(gl.VERTEX_SHADER);
    63   } else {
    64     throw(new Error("Unknown shader type "+shaderScript.type));
    65   }
    67   gl.shaderSource(shader, str);
    68   gl.compileShader(shader);
    70   if (gl.getShaderParameter(shader, gl.COMPILE_STATUS) != 1) {
    71     var ilog = gl.getShaderInfoLog(shader);
    72     gl.deleteShader(shader);
    73     throw(new Error("Failed to compile shader "+shaderScript.id + ", Shader info log: " + ilog));
    74   }
    75   return shader;
    76 }
    78 function loadShaderArray(gl, shaders) {
    79   var id = gl.createProgram();
    80   var shaderObjs = [];
    81   for (var i=0; i<shaders.length; ++i) {
    82     try {
    83       var sh = getShader(gl, shaders[i]);
    84       shaderObjs.push(sh);
    85       gl.attachShader(id, sh);
    86     } catch (e) {
    87       var pr = {program: id, shaders: shaderObjs};
    88       deleteShader(gl, pr);
    89       throw (e);
    90     }
    91   }
    92   var prog = {program: id, shaders: shaderObjs};
    93   gl.linkProgram(id);
    94   gl.validateProgram(id);
    95   if (gl.getProgramParameter(id, gl.LINK_STATUS) != 1) {
    96     deleteShader(gl,prog);
    97     throw(new Error("Failed to link shader"));
    98   }
    99   if (gl.getProgramParameter(id, gl.VALIDATE_STATUS) != 1) {
   100     deleteShader(gl,prog);
   101     throw(new Error("Failed to validate shader"));
   102   }
   103   return prog;
   104 }
   105 function loadShader(gl) {
   106   var sh = [];
   107   for (var i=1; i<arguments.length; ++i)
   108     sh.push(arguments[i]);
   109   return loadShaderArray(gl, sh);
   110 }
   112 function deleteShader(gl, sh) {
   113   gl.useProgram(null);
   114   sh.shaders.forEach(function(s){
   115     gl.detachShader(sh.program, s);
   116     gl.deleteShader(s);
   117   });
   118   gl.deleteProgram(sh.program);
   119 }
   121 function getGLErrorAsString(ctx, err) {
   122   if (err === ctx.NO_ERROR) {
   123     return "NO_ERROR";
   124   }
   125   for (var name in ctx) {
   126     if (ctx[name] === err) {
   127       return name;
   128     }
   129   }
   130   return err.toString();
   131 }
   133 function checkError(gl, msg) {
   134   var e = gl.getError();
   135   if (e != gl.NO_ERROR) {
   136     log("Error " + getGLErrorAsString(gl, e) + " at " + msg);
   137   }
   138   return e;
   139 }
   141 function throwError(gl, msg) {
   142   var e = gl.getError();
   143   if (e != 0) {
   144     throw(new Error("Error " + getGLErrorAsString(gl, e) + " at " + msg));
   145   }
   146 }
   148 Math.cot = function(z) { return 1.0 / Math.tan(z); }
   150 /*
   151   Matrix utilities, using the OpenGL element order where
   152   the last 4 elements are the translation column.
   154   Uses flat arrays as matrices for performance.
   156   Most operations have in-place variants to avoid allocating temporary matrices.
   158   Naming logic:
   159     Matrix.method operates on a 4x4 Matrix and returns a new Matrix.
   160     Matrix.method3x3 operates on a 3x3 Matrix and returns a new Matrix. Not all operations have a 3x3 version (as 3x3 is usually only used for the normal matrix: Matrix.transpose3x3(Matrix.inverseTo3x3(mat4x4)))
   161     Matrix.method[3x3]InPlace(args, target) stores its result in the target matrix.
   163     Matrix.scale([sx, sy, sz]) -- non-uniform scale by vector
   164     Matrix.scale1(s)           -- uniform scale by scalar
   165     Matrix.scale3(sx, sy, sz)  -- non-uniform scale by scalars
   167     Ditto for translate.
   168 */
   169 Matrix = {
   170   identity : [
   171     1.0, 0.0, 0.0, 0.0,
   172     0.0, 1.0, 0.0, 0.0,
   173     0.0, 0.0, 1.0, 0.0,
   174     0.0, 0.0, 0.0, 1.0
   175   ],
   177   newIdentity : function() {
   178     return [
   179       1.0, 0.0, 0.0, 0.0,
   180       0.0, 1.0, 0.0, 0.0,
   181       0.0, 0.0, 1.0, 0.0,
   182       0.0, 0.0, 0.0, 1.0
   183     ];
   184   },
   186   newIdentity3x3 : function() {
   187     return [
   188       1.0, 0.0, 0.0,
   189       0.0, 1.0, 0.0,
   190       0.0, 0.0, 1.0
   191     ];
   192   },
   194   copyMatrix : function(src, dst) {
   195     for (var i=0; i<16; i++) dst[i] = src[i];
   196     return dst;
   197   },
   199   to3x3 : function(m) {
   200     return [
   201       m[0], m[1], m[2],
   202       m[4], m[5], m[6],
   203       m[8], m[9], m[10]
   204     ];
   205   },
   207   // orthonormal matrix inverse
   208   inverseON : function(m) {
   209     var n = this.transpose4x4(m);
   210     var t = [m[12], m[13], m[14]];
   211     n[3] = n[7] = n[11] = 0;
   212     n[12] = -Vec3.dot([n[0], n[4], n[8]], t);
   213     n[13] = -Vec3.dot([n[1], n[5], n[9]], t);
   214     n[14] = -Vec3.dot([n[2], n[6], n[10]], t);
   215     return n;
   216   },
   218   inverseTo3x3 : function(m) {
   219     return this.inverse4x4to3x3InPlace(m, this.newIdentity3x3());
   220   },
   222   inverseTo3x3InPlace : function(m,n) {
   223     var a11 = m[10]*m[5]-m[6]*m[9],
   224         a21 = -m[10]*m[1]+m[2]*m[9],
   225         a31 = m[6]*m[1]-m[2]*m[5],
   226         a12 = -m[10]*m[4]+m[6]*m[8],
   227         a22 = m[10]*m[0]-m[2]*m[8],
   228         a32 = -m[6]*m[0]+m[2]*m[4],
   229         a13 = m[9]*m[4]-m[5]*m[8],
   230         a23 = -m[9]*m[0]+m[1]*m[8],
   231         a33 = m[5]*m[0]-m[1]*m[4];
   232     var det = m[0]*(a11) + m[1]*(a12) + m[2]*(a13);
   233     if (det == 0) // no inverse
   234       return [1,0,0,0,1,0,0,0,1];
   235     var idet = 1 / det;
   236     n[0] = idet*a11;
   237     n[1] = idet*a21;
   238     n[2] = idet*a31;
   239     n[3] = idet*a12;
   240     n[4] = idet*a22;
   241     n[5] = idet*a32;
   242     n[6] = idet*a13;
   243     n[7] = idet*a23;
   244     n[8] = idet*a33;
   245     return n;
   246   },
   248   inverse3x3 : function(m) {
   249     return this.inverse3x3InPlace(m, this.newIdentity3x3());
   250   },
   252   inverse3x3InPlace : function(m,n) {
   253     var a11 = m[8]*m[4]-m[5]*m[7],
   254         a21 = -m[8]*m[1]+m[2]*m[7],
   255         a31 = m[5]*m[1]-m[2]*m[4],
   256         a12 = -m[8]*m[3]+m[5]*m[6],
   257         a22 = m[8]*m[0]-m[2]*m[6],
   258         a32 = -m[5]*m[0]+m[2]*m[3],
   259         a13 = m[7]*m[4]-m[4]*m[8],
   260         a23 = -m[7]*m[0]+m[1]*m[6],
   261         a33 = m[4]*m[0]-m[1]*m[3];
   262     var det = m[0]*(a11) + m[1]*(a12) + m[2]*(a13);
   263     if (det == 0) // no inverse
   264       return [1,0,0,0,1,0,0,0,1];
   265     var idet = 1 / det;
   266     n[0] = idet*a11;
   267     n[1] = idet*a21;
   268     n[2] = idet*a31;
   269     n[3] = idet*a12;
   270     n[4] = idet*a22;
   271     n[5] = idet*a32;
   272     n[6] = idet*a13;
   273     n[7] = idet*a23;
   274     n[8] = idet*a33;
   275     return n;
   276   },
   278   frustum : function (left, right, bottom, top, znear, zfar) {
   279     var X = 2*znear/(right-left);
   280     var Y = 2*znear/(top-bottom);
   281     var A = (right+left)/(right-left);
   282     var B = (top+bottom)/(top-bottom);
   283     var C = -(zfar+znear)/(zfar-znear);
   284     var D = -2*zfar*znear/(zfar-znear);
   286     return [
   287       X, 0, 0, 0,
   288       0, Y, 0, 0,
   289       A, B, C, -1,
   290       0, 0, D, 0
   291     ];
   292  },
   294   perspective : function (fovy, aspect, znear, zfar) {
   295     var ymax = znear * Math.tan(fovy * Math.PI / 360.0);
   296     var ymin = -ymax;
   297     var xmin = ymin * aspect;
   298     var xmax = ymax * aspect;
   300     return this.frustum(xmin, xmax, ymin, ymax, znear, zfar);
   301   },
   303   mul4x4 : function (a,b) {
   304     return this.mul4x4InPlace(a,b,this.newIdentity());
   305   },
   307   mul4x4InPlace : function (a, b, c) {
   308         c[0] =   b[0] * a[0] +
   309                  b[0+1] * a[4] +
   310                  b[0+2] * a[8] +
   311                  b[0+3] * a[12];
   312         c[0+1] = b[0] * a[1] +
   313                  b[0+1] * a[5] +
   314                  b[0+2] * a[9] +
   315                  b[0+3] * a[13];
   316         c[0+2] = b[0] * a[2] +
   317                  b[0+1] * a[6] +
   318                  b[0+2] * a[10] +
   319                  b[0+3] * a[14];
   320         c[0+3] = b[0] * a[3] +
   321                  b[0+1] * a[7] +
   322                  b[0+2] * a[11] +
   323                  b[0+3] * a[15];
   324         c[4] =   b[4] * a[0] +
   325                  b[4+1] * a[4] +
   326                  b[4+2] * a[8] +
   327                  b[4+3] * a[12];
   328         c[4+1] = b[4] * a[1] +
   329                  b[4+1] * a[5] +
   330                  b[4+2] * a[9] +
   331                  b[4+3] * a[13];
   332         c[4+2] = b[4] * a[2] +
   333                  b[4+1] * a[6] +
   334                  b[4+2] * a[10] +
   335                  b[4+3] * a[14];
   336         c[4+3] = b[4] * a[3] +
   337                  b[4+1] * a[7] +
   338                  b[4+2] * a[11] +
   339                  b[4+3] * a[15];
   340         c[8] =   b[8] * a[0] +
   341                  b[8+1] * a[4] +
   342                  b[8+2] * a[8] +
   343                  b[8+3] * a[12];
   344         c[8+1] = b[8] * a[1] +
   345                  b[8+1] * a[5] +
   346                  b[8+2] * a[9] +
   347                  b[8+3] * a[13];
   348         c[8+2] = b[8] * a[2] +
   349                  b[8+1] * a[6] +
   350                  b[8+2] * a[10] +
   351                  b[8+3] * a[14];
   352         c[8+3] = b[8] * a[3] +
   353                  b[8+1] * a[7] +
   354                  b[8+2] * a[11] +
   355                  b[8+3] * a[15];
   356         c[12] =   b[12] * a[0] +
   357                  b[12+1] * a[4] +
   358                  b[12+2] * a[8] +
   359                  b[12+3] * a[12];
   360         c[12+1] = b[12] * a[1] +
   361                  b[12+1] * a[5] +
   362                  b[12+2] * a[9] +
   363                  b[12+3] * a[13];
   364         c[12+2] = b[12] * a[2] +
   365                  b[12+1] * a[6] +
   366                  b[12+2] * a[10] +
   367                  b[12+3] * a[14];
   368         c[12+3] = b[12] * a[3] +
   369                  b[12+1] * a[7] +
   370                  b[12+2] * a[11] +
   371                  b[12+3] * a[15];
   372     return c;
   373   },
   375   mulv4 : function (a, v) {
   376     c = new Array(4);
   377     for (var i=0; i<4; ++i) {
   378       var x = 0;
   379       for (var k=0; k<4; ++k)
   380         x += v[k] * a[k*4+i];
   381       c[i] = x;
   382     }
   383     return c;
   384   },
   386   rotate : function (angle, axis) {
   387     axis = Vec3.normalize(axis);
   388     var x=axis[0], y=axis[1], z=axis[2];
   389     var c = Math.cos(angle);
   390     var c1 = 1-c;
   391     var s = Math.sin(angle);
   392     return [
   393       x*x*c1+c, y*x*c1+z*s, z*x*c1-y*s, 0,
   394       x*y*c1-z*s, y*y*c1+c, y*z*c1+x*s, 0,
   395       x*z*c1+y*s, y*z*c1-x*s, z*z*c1+c, 0,
   396       0,0,0,1
   397     ];
   398   },
   399   rotateInPlace : function(angle, axis, m) {
   400     axis = Vec3.normalize(axis);
   401     var x=axis[0], y=axis[1], z=axis[2];
   402     var c = Math.cos(angle);
   403     var c1 = 1-c;
   404     var s = Math.sin(angle);
   405     var tmpMatrix = this.tmpMatrix;
   406     var tmpMatrix2 = this.tmpMatrix2;
   407     tmpMatrix[0] = x*x*c1+c; tmpMatrix[1] = y*x*c1+z*s; tmpMatrix[2] = z*x*c1-y*s; tmpMatrix[3] = 0;
   408     tmpMatrix[4] = x*y*c1-z*s; tmpMatrix[5] = y*y*c1+c; tmpMatrix[6] = y*z*c1+x*s; tmpMatrix[7] = 0;
   409     tmpMatrix[8] = x*z*c1+y*s; tmpMatrix[9] = y*z*c1-x*s; tmpMatrix[10] = z*z*c1+c; tmpMatrix[11] = 0;
   410     tmpMatrix[12] = 0; tmpMatrix[13] = 0; tmpMatrix[14] = 0; tmpMatrix[15] = 1;
   411     this.copyMatrix(m, tmpMatrix2);
   412     return this.mul4x4InPlace(tmpMatrix2, tmpMatrix, m);
   413   },
   415   scale : function(v) {
   416     return [
   417       v[0], 0, 0, 0,
   418       0, v[1], 0, 0,
   419       0, 0, v[2], 0,
   420       0, 0, 0, 1
   421     ];
   422   },
   423   scale3 : function(x,y,z) {
   424     return [
   425       x, 0, 0, 0,
   426       0, y, 0, 0,
   427       0, 0, z, 0,
   428       0, 0, 0, 1
   429     ];
   430   },
   431   scale1 : function(s) {
   432     return [
   433       s, 0, 0, 0,
   434       0, s, 0, 0,
   435       0, 0, s, 0,
   436       0, 0, 0, 1
   437     ];
   438   },
   439   scale3InPlace : function(x, y, z, m) {
   440     var tmpMatrix = this.tmpMatrix;
   441     var tmpMatrix2 = this.tmpMatrix2;
   442     tmpMatrix[0] = x; tmpMatrix[1] = 0; tmpMatrix[2] = 0; tmpMatrix[3] = 0;
   443     tmpMatrix[4] = 0; tmpMatrix[5] = y; tmpMatrix[6] = 0; tmpMatrix[7] = 0;
   444     tmpMatrix[8] = 0; tmpMatrix[9] = 0; tmpMatrix[10] = z; tmpMatrix[11] = 0;
   445     tmpMatrix[12] = 0; tmpMatrix[13] = 0; tmpMatrix[14] = 0; tmpMatrix[15] = 1;
   446     this.copyMatrix(m, tmpMatrix2);
   447     return this.mul4x4InPlace(tmpMatrix2, tmpMatrix, m);
   448   },
   449   scale1InPlace : function(s, m) { return this.scale3InPlace(s, s, s, m); },
   450   scaleInPlace : function(s, m) { return this.scale3InPlace(s[0],s[1],s[2],m); },
   452   translate3 : function(x,y,z) {
   453     return [
   454       1, 0, 0, 0,
   455       0, 1, 0, 0,
   456       0, 0, 1, 0,
   457       x, y, z, 1
   458     ];
   459   },
   461   translate : function(v) {
   462     return this.translate3(v[0], v[1], v[2]);
   463   },
   464   tmpMatrix : [0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0],
   465   tmpMatrix2 : [0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0],
   466   translate3InPlace : function(x,y,z,m) {
   467     var tmpMatrix = this.tmpMatrix;
   468     var tmpMatrix2 = this.tmpMatrix2;
   469     tmpMatrix[0] = 1; tmpMatrix[1] = 0; tmpMatrix[2] = 0; tmpMatrix[3] = 0;
   470     tmpMatrix[4] = 0; tmpMatrix[5] = 1; tmpMatrix[6] = 0; tmpMatrix[7] = 0;
   471     tmpMatrix[8] = 0; tmpMatrix[9] = 0; tmpMatrix[10] = 1; tmpMatrix[11] = 0;
   472     tmpMatrix[12] = x; tmpMatrix[13] = y; tmpMatrix[14] = z; tmpMatrix[15] = 1;
   473     this.copyMatrix(m, tmpMatrix2);
   474     return this.mul4x4InPlace(tmpMatrix2, tmpMatrix, m);
   475   },
   476   translateInPlace : function(v,m){ return this.translate3InPlace(v[0], v[1], v[2], m); },
   478   lookAt : function (eye, center, up) {
   479     var z = Vec3.direction(eye, center);
   480     var x = Vec3.normalizeInPlace(Vec3.cross(up, z));
   481     var y = Vec3.normalizeInPlace(Vec3.cross(z, x));
   483     var m = [
   484       x[0], y[0], z[0], 0,
   485       x[1], y[1], z[1], 0,
   486       x[2], y[2], z[2], 0,
   487       0, 0, 0, 1
   488     ];
   490     var t = [
   491       1, 0, 0, 0,
   492       0, 1, 0, 0,
   493       0, 0, 1, 0,
   494       -eye[0], -eye[1], -eye[2], 1
   495     ];
   497     return this.mul4x4(m,t);
   498   },
   500   transpose4x4 : function(m) {
   501     return [
   502       m[0], m[4], m[8], m[12],
   503       m[1], m[5], m[9], m[13],
   504       m[2], m[6], m[10], m[14],
   505       m[3], m[7], m[11], m[15]
   506     ];
   507   },
   509   transpose4x4InPlace : function(m) {
   510     var tmp = 0.0;
   511     tmp = m[1]; m[1] = m[4]; m[4] = tmp;
   512     tmp = m[2]; m[2] = m[8]; m[8] = tmp;
   513     tmp = m[3]; m[3] = m[12]; m[12] = tmp;
   514     tmp = m[6]; m[6] = m[9]; m[9] = tmp;
   515     tmp = m[7]; m[7] = m[13]; m[13] = tmp;
   516     tmp = m[11]; m[11] = m[14]; m[14] = tmp;
   517     return m;
   518   },
   520   transpose3x3 : function(m) {
   521     return [
   522       m[0], m[3], m[6],
   523       m[1], m[4], m[7],
   524       m[2], m[5], m[8]
   525     ];
   526   },
   528   transpose3x3InPlace : function(m) {
   529     var tmp = 0.0;
   530     tmp = m[1]; m[1] = m[3]; m[3] = tmp;
   531     tmp = m[2]; m[2] = m[6]; m[6] = tmp;
   532     tmp = m[5]; m[5] = m[7]; m[7] = tmp;
   533     return m;
   534   },
   535 }
   537 Vec3 = {
   538   make : function() { return [0,0,0]; },
   539   copy : function(v) { return [v[0],v[1],v[2]]; },
   541   add : function (u,v) {
   542     return [u[0]+v[0], u[1]+v[1], u[2]+v[2]];
   543   },
   545   sub : function (u,v) {
   546     return [u[0]-v[0], u[1]-v[1], u[2]-v[2]];
   547   },
   549   negate : function (u) {
   550     return [-u[0], -u[1], -u[2]];
   551   },
   553   direction : function (u,v) {
   554     return this.normalizeInPlace(this.sub(u,v));
   555   },
   557   normalizeInPlace : function(v) {
   558     var imag = 1.0 / Math.sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]);
   559     v[0] *= imag; v[1] *= imag; v[2] *= imag;
   560     return v;
   561   },
   563   normalize : function(v) {
   564     return this.normalizeInPlace(this.copy(v));
   565   },
   567   scale : function(f, v) {
   568     return [f*v[0], f*v[1], f*v[2]];
   569   },
   571   dot : function(u,v) {
   572     return u[0]*v[0] + u[1]*v[1] + u[2]*v[2];
   573   },
   575   inner : function(u,v) {
   576     return [u[0]*v[0], u[1]*v[1], u[2]*v[2]];
   577   },
   579   cross : function(u,v) {
   580     return [
   581       u[1]*v[2] - u[2]*v[1],
   582       u[2]*v[0] - u[0]*v[2],
   583       u[0]*v[1] - u[1]*v[0]
   584     ];
   585   }
   586 }
   588 Shader = function(gl){
   589   this.gl = gl;
   590   this.shaders = [];
   591   this.uniformLocations = {};
   592   this.attribLocations = {};
   593   for (var i=1; i<arguments.length; i++) {
   594     this.shaders.push(arguments[i]);
   595   }
   596 }
   597 Shader.prototype = {
   598   id : null,
   599   gl : null,
   600   compiled : false,
   601   shader : null,
   602   shaders : [],
   604   destroy : function() {
   605     if (this.shader != null) deleteShader(this.gl, this.shader);
   606   },
   608   compile : function() {
   609     this.shader = loadShaderArray(this.gl, this.shaders);
   610   },
   612   use : function() {
   613     if (this.shader == null)
   614       this.compile();
   615     this.gl.useProgram(this.shader.program);
   616   },
   618   uniform1fv : function(name, value) {
   619     var loc = this.uniform(name);
   620     this.gl.uniform1fv(loc, value);
   621   },
   623   uniform2fv : function(name, value) {
   624     var loc = this.uniform(name);
   625     this.gl.uniform2fv(loc, value);
   626   },
   628   uniform3fv : function(name, value) {
   629     var loc = this.uniform(name);
   630     this.gl.uniform3fv(loc, value);
   631   },
   633   uniform4fv : function(name, value) {
   634     var loc = this.uniform(name);
   635     this.gl.uniform4fv(loc, value);
   636   },
   638   uniform1f : function(name, value) {
   639     var loc = this.uniform(name);
   640     this.gl.uniform1f(loc, value);
   641   },
   643   uniform2f : function(name, v1,v2) {
   644     var loc = this.uniform(name);
   645     this.gl.uniform2f(loc, v1,v2);
   646   },
   648   uniform3f : function(name, v1,v2,v3) {
   649     var loc = this.uniform(name);
   650     this.gl.uniform3f(loc, v1,v2,v3);
   651   },
   653   uniform4f : function(name, v1,v2,v3,v4) {
   654     var loc = this.uniform(name);
   655     this.gl.uniform4f(loc, v1, v2, v3, v4);
   656   },
   658   uniform1iv : function(name, value) {
   659     var loc = this.uniform(name);
   660     this.gl.uniform1iv(loc, value);
   661   },
   663   uniform2iv : function(name, value) {
   664     var loc = this.uniform(name);
   665     this.gl.uniform2iv(loc, value);
   666   },
   668   uniform3iv : function(name, value) {
   669     var loc = this.uniform(name);
   670     this.gl.uniform3iv(loc, value);
   671   },
   673   uniform4iv : function(name, value) {
   674     var loc = this.uniform(name);
   675     this.gl.uniform4iv(loc, value);
   676   },
   678   uniform1i : function(name, value) {
   679     var loc = this.uniform(name);
   680     this.gl.uniform1i(loc, value);
   681   },
   683   uniform2i : function(name, v1,v2) {
   684     var loc = this.uniform(name);
   685     this.gl.uniform2i(loc, v1,v2);
   686   },
   688   uniform3i : function(name, v1,v2,v3) {
   689     var loc = this.uniform(name);
   690     this.gl.uniform3i(loc, v1,v2,v3);
   691   },
   693   uniform4i : function(name, v1,v2,v3,v4) {
   694     var loc = this.uniform(name);
   695     this.gl.uniform4i(loc, v1, v2, v3, v4);
   696   },
   698   uniformMatrix4fv : function(name, value) {
   699     var loc = this.uniform(name);
   700     this.gl.uniformMatrix4fv(loc, false, value);
   701   },
   703   uniformMatrix3fv : function(name, value) {
   704     var loc = this.uniform(name);
   705     this.gl.uniformMatrix3fv(loc, false, value);
   706   },
   708   uniformMatrix2fv : function(name, value) {
   709     var loc = this.uniform(name);
   710     this.gl.uniformMatrix2fv(loc, false, value);
   711   },
   713   attrib : function(name) {
   714     if (this.attribLocations[name] == null) {
   715       var loc = this.gl.getAttribLocation(this.shader.program, name);
   716       this.attribLocations[name] = loc;
   717     }
   718     return this.attribLocations[name];
   719   },
   721   uniform : function(name) {
   722     if (this.uniformLocations[name] == null) {
   723       var loc = this.gl.getUniformLocation(this.shader.program, name);
   724       this.uniformLocations[name] = loc;
   725     }
   726     return this.uniformLocations[name];
   727   }
   728 }
   729 Filter = function(gl, shader) {
   730   Shader.apply(this, arguments);
   731 }
   732 Filter.prototype = new Shader();
   733 Filter.prototype.apply = function(init) {
   734   this.use();
   735   var va = this.attrib("Vertex");
   736   var ta = this.attrib("Tex");
   737   var vbo = Quad.getCachedVBO(this.gl);
   738   if (init) init(this);
   739   vbo.draw(va, null, ta);
   740 }
   743 VBO = function(gl) {
   744   this.gl = gl;
   745   this.data = [];
   746   this.elementsVBO = null;
   747   for (var i=1; i<arguments.length; i++) {
   748     if (arguments[i].elements)
   749       this.elements = arguments[i];
   750     else
   751       this.data.push(arguments[i]);
   752   }
   753 }
   755 VBO.prototype = {
   756   initialized : false,
   757   length : 0,
   758   vbos : null,
   759   type : 'TRIANGLES',
   760   elementsVBO : null,
   761   elements : null,
   763   setData : function() {
   764     this.destroy();
   765     this.data = [];
   766     for (var i=0; i<arguments.length; i++) {
   767       if (arguments[i].elements)
   768         this.elements = arguments[i];
   769       else
   770         this.data.push(arguments[i]);
   771     }
   772   },
   774   destroy : function() {
   775     if (this.vbos != null)
   776       for (var i=0; i<this.vbos.length; i++)
   777         this.gl.deleteBuffer(this.vbos[i]);
   778     if (this.elementsVBO != null)
   779       this.gl.deleteBuffer(this.elementsVBO);
   780     this.length = this.elementsLength = 0;
   781     this.vbos = this.elementsVBO = null;
   782     this.initialized = false;
   783   },
   785   init : function() {
   786     this.destroy();
   787     var gl = this.gl;
   789     gl.getError();
   790     var vbos = [];
   791     var length = 0;
   792     for (var i=0; i<this.data.length; i++)
   793       vbos.push(gl.createBuffer());
   794     if (this.elements != null)
   795       this.elementsVBO = gl.createBuffer();
   796     try {
   797       throwError(gl, "genBuffers");
   798       for (var i = 0; i<this.data.length; i++) {
   799         var d = this.data[i];
   800         var dlen = Math.floor(d.data.length / d.size);
   801         if (i == 0 || dlen < length)
   802             length = dlen;
   803         if (!d.floatArray)
   804           d.floatArray = new Float32Array(d.data);
   805         gl.bindBuffer(gl.ARRAY_BUFFER, vbos[i]);
   806         throwError(gl, "bindBuffer");
   807         gl.bufferData(gl.ARRAY_BUFFER, d.floatArray, gl.STATIC_DRAW);
   808         throwError(gl, "bufferData");
   809       }
   810       if (this.elementsVBO != null) {
   811         var d = this.elements;
   812         this.elementsLength = d.data.length;
   813         this.elementsType = d.type == gl.UNSIGNED_BYTE ? d.type : gl.UNSIGNED_SHORT;
   814         gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.elementsVBO);
   815         throwError(gl, "bindBuffer ELEMENT_ARRAY_BUFFER");
   816         if (this.elementsType == gl.UNSIGNED_SHORT && !d.ushortArray) {
   817           d.ushortArray = new Uint16Array(d.data);
   818           gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, d.ushortArray, gl.STATIC_DRAW);
   819         } else if (this.elementsType == gl.UNSIGNED_BYTE && !d.ubyteArray) {
   820           d.ubyteArray = new Uint8Array(d.data);
   821           gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, d.ubyteArray, gl.STATIC_DRAW);
   822         }
   823         throwError(gl, "bufferData ELEMENT_ARRAY_BUFFER");
   824       }
   825     } catch(e) {
   826       for (var i=0; i<vbos.length; i++)
   827         gl.deleteBuffer(vbos[i]);
   828       throw(e);
   829     }
   831     gl.bindBuffer(gl.ARRAY_BUFFER, null);
   832     gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
   834     this.length = length;
   835     this.vbos = vbos;
   837     this.initialized = true;
   838   },
   840   use : function() {
   841     if (!this.initialized) this.init();
   842     var gl = this.gl;
   843     for (var i=0; i<arguments.length; i++) {
   844       if (arguments[i] == null) continue;
   845       gl.bindBuffer(gl.ARRAY_BUFFER, this.vbos[i]);
   846       gl.vertexAttribPointer(arguments[i], this.data[i].size, gl.FLOAT, false, 0, 0);
   847       gl.enableVertexAttribArray(arguments[i]);
   848     }
   849     if (this.elementsVBO != null) {
   850       gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.elementsVBO);
   851     }
   852   },
   854   draw : function() {
   855     var args = [];
   856     this.use.apply(this, arguments);
   857     var gl = this.gl;
   858     if (this.elementsVBO != null) {
   859       gl.drawElements(gl[this.type], this.elementsLength, this.elementsType, 0);
   860     } else {
   861       gl.drawArrays(gl[this.type], 0, this.length);
   862     }
   863   }
   864 }
   866 FBO = function(gl, width, height, use_depth) {
   867   this.gl = gl;
   868   this.width = width;
   869   this.height = height;
   870   if (use_depth != null)
   871     this.useDepth = use_depth;
   872 }
   873 FBO.prototype = {
   874   initialized : false,
   875   useDepth : true,
   876   fbo : null,
   877   rbo : null,
   878   texture : null,
   880   destroy : function() {
   881     if (this.fbo) this.gl.deleteFramebuffer(this.fbo);
   882     if (this.rbo) this.gl.deleteRenderbuffer(this.rbo);
   883     if (this.texture) this.gl.deleteTexture(this.texture);
   884   },
   886   init : function() {
   887     var gl = this.gl;
   888     var w = this.width, h = this.height;
   889     var fbo = this.fbo != null ? this.fbo : gl.createFramebuffer();
   890     var rb;
   892     gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
   893     checkError(gl, "FBO.init bindFramebuffer");
   894     if (this.useDepth) {
   895       rb = this.rbo != null ? this.rbo : gl.createRenderbuffer();
   896       gl.bindRenderbuffer(gl.RENDERBUFFER, rb);
   897       checkError(gl, "FBO.init bindRenderbuffer");
   898       gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, w, h);
   899       checkError(gl, "FBO.init renderbufferStorage");
   900     }
   902     var tex = this.texture != null ? this.texture : gl.createTexture();
   903     gl.bindTexture(gl.TEXTURE_2D, tex);
   904     try {
   905       gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, w, h, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
   906     } catch (e) { // argh, no null texture support
   907       var tmp = this.getTempCanvas(w,h);
   908       gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, tmp);
   909     }
   910     gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
   911     gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
   912     gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
   913     gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
   914     checkError(gl, "FBO.init tex");
   916     gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0);
   917     checkError(gl, "FBO.init bind tex");
   919     if (this.useDepth) {
   920       gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, rb);
   921       checkError(gl, "FBO.init bind depth buffer");
   922     }
   924     var fbstat = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
   925     if (fbstat != gl.FRAMEBUFFER_COMPLETE) {
   926       var glv;
   927       for (var v in gl) {
   928         try { glv = gl[v]; } catch (e) { glv = null; }
   929         if (glv == fbstat) { fbstat = v; break; }}
   930         log("Framebuffer status: " + fbstat);
   931     }
   932     checkError(gl, "FBO.init check fbo");
   934     this.fbo = fbo;
   935     this.rbo = rb;
   936     this.texture = tex;
   937     this.initialized = true;
   938   },
   940   getTempCanvas : function(w, h) {
   941     if (!FBO.tempCanvas) {
   942       FBO.tempCanvas = document.createElement('canvas');
   943     }
   944     FBO.tempCanvas.width = w;
   945     FBO.tempCanvas.height = h;
   946     return FBO.tempCanvas;
   947   },
   949   use : function() {
   950     if (!this.initialized) this.init();
   951     this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, this.fbo);
   952   }
   953 }
   955 function GLError(err, msg, fileName, lineNumber) {
   956   this.message = msg;
   957   this.glError = err;
   958 }
   960 GLError.prototype = new Error();
   962 function makeGLErrorWrapper(gl, fname) {
   963   return (function() {
   964     try {
   965       var rv = gl[fname].apply(gl, arguments);
   966       var err = gl.getError();
   967       if (err != gl.NO_ERROR) {
   968         throw(new GLError(
   969             err, "GL error "+getGLErrorAsString(gl, err)+" in "+fname));
   970       }
   971       return rv;
   972     } catch (e) {
   973       if (e.glError !== undefined) {
   974         throw e;
   975       }
   976       throw(new Error("Threw " + e.name +
   977                       " in " + fname + "\n" +
   978                       e.message + "\n" +
   979                       arguments.callee.caller));
   980     }
   981   });
   982 }
   984 function wrapGLContext(gl) {
   985   var wrap = {};
   986   for (var i in gl) {
   987     try {
   988       if (typeof gl[i] == 'function') {
   989           wrap[i] = makeGLErrorWrapper(gl, i);
   990       } else {
   991           wrap[i] = gl[i];
   992       }
   993     } catch (e) {
   994       // log("wrapGLContext: Error accessing " + i);
   995     }
   996   }
   997   wrap.getError = function(){ return gl.getError(); };
   998   return wrap;
   999 }
  1001 // Assert that f generates a specific GL error.
  1002 function assertGLError(gl, err, name, f) {
  1003   if (f == null) { f = name; name = null; }
  1004   var r = false;
  1005   var glErr = 0;
  1006   try { f(); } catch(e) { r=true; glErr = e.glError; }
  1007   if (glErr !== err) {
  1008     if (glErr === undefined) {
  1009       testFailed("assertGLError: UNEXPCETED EXCEPTION", name, f);
  1010     } else {
  1011       testFailed("assertGLError: expected: " + getGLErrorAsString(gl, err) +
  1012                  " actual: " + getGLErrorAsString(gl, glErr), name, f);
  1014     return false;
  1016   return true;
  1019 // Assert that f generates some GL error. Used in situations where it's
  1020 // ambigious which of multiple possible errors will be generated.
  1021 function assertSomeGLError(gl, name, f) {
  1022   if (f == null) { f = name; name = null; }
  1023   var r = false;
  1024   var glErr = 0;
  1025   var err = 0;
  1026   try { f(); } catch(e) { r=true; glErr = e.glError; }
  1027   if (glErr === 0) {
  1028     if (glErr === undefined) {
  1029       testFailed("assertGLError: UNEXPCETED EXCEPTION", name, f);
  1030     } else {
  1031       testFailed("assertGLError: expected: " + getGLErrorAsString(gl, err) +
  1032                  " actual: " + getGLErrorAsString(gl, glErr), name, f);
  1034     return false;
  1036   return true;
  1039 // Assert that f throws an exception but does not generate a GL error.
  1040 function assertThrowNoGLError(gl, name, f) {
  1041   if (f == null) { f = name; name = null; }
  1042   var r = false;
  1043   var glErr = undefined;
  1044   var exp;
  1045   try { f(); } catch(e) { r=true; glErr = e.glError; exp = e;}
  1046   if (!r) {
  1047     testFailed(
  1048       "assertThrowNoGLError: should have thrown exception", name, f);
  1049     return false;
  1050   } else {
  1051     if (glErr !== undefined) {
  1052       testFailed(
  1053         "assertThrowNoGLError: should be no GL error but generated: " +
  1054         getGLErrorAsString(gl, glErr), name, f);
  1055       return false;
  1058   testPassed("assertThrowNoGLError", name, f);
  1059   return true;
  1062 Quad = {
  1063   vertices : [
  1064     -1,-1,0,
  1065     1,-1,0,
  1066     -1,1,0,
  1067     1,-1,0,
  1068     1,1,0,
  1069     -1,1,0
  1070   ],
  1071   normals : [
  1072     0,0,-1,
  1073     0,0,-1,
  1074     0,0,-1,
  1075     0,0,-1,
  1076     0,0,-1,
  1077     0,0,-1
  1078   ],
  1079   texcoords : [
  1080     0,0,
  1081     1,0,
  1082     0,1,
  1083     1,0,
  1084     1,1,
  1085     0,1
  1086   ],
  1087   indices : [0,1,2,1,5,2],
  1088   makeVBO : function(gl) {
  1089     return new VBO(gl,
  1090         {size:3, data: Quad.vertices},
  1091         {size:3, data: Quad.normals},
  1092         {size:2, data: Quad.texcoords}
  1094   },
  1095   cache: {},
  1096   getCachedVBO : function(gl) {
  1097     if (!this.cache[gl])
  1098       this.cache[gl] = this.makeVBO(gl);
  1099     return this.cache[gl];
  1102 Cube = {
  1103   vertices : [  0.5, -0.5,  0.5, // +X
  1104                 0.5, -0.5, -0.5,
  1105                 0.5,  0.5, -0.5,
  1106                 0.5,  0.5,  0.5,
  1108                 0.5,  0.5,  0.5, // +Y
  1109                 0.5,  0.5, -0.5,
  1110                 -0.5,  0.5, -0.5,
  1111                 -0.5,  0.5,  0.5,
  1113                 0.5,  0.5,  0.5, // +Z
  1114                 -0.5,  0.5,  0.5,
  1115                 -0.5, -0.5,  0.5,
  1116                 0.5, -0.5,  0.5,
  1118                 -0.5, -0.5,  0.5, // -X
  1119                 -0.5,  0.5,  0.5,
  1120                 -0.5,  0.5, -0.5,
  1121                 -0.5, -0.5, -0.5,
  1123                 -0.5, -0.5,  0.5, // -Y
  1124                 -0.5, -0.5, -0.5,
  1125                 0.5, -0.5, -0.5,
  1126                 0.5, -0.5,  0.5,
  1128                 -0.5, -0.5, -0.5, // -Z
  1129                 -0.5,  0.5, -0.5,
  1130                 0.5,  0.5, -0.5,
  1131                 0.5, -0.5, -0.5,
  1132       ],
  1134   normals : [ 1, 0, 0,
  1135               1, 0, 0,
  1136               1, 0, 0,
  1137               1, 0, 0,
  1139               0, 1, 0,
  1140               0, 1, 0,
  1141               0, 1, 0,
  1142               0, 1, 0,
  1144               0, 0, 1,
  1145               0, 0, 1,
  1146               0, 0, 1,
  1147               0, 0, 1,
  1149               -1, 0, 0,
  1150               -1, 0, 0,
  1151               -1, 0, 0,
  1152               -1, 0, 0,
  1154               0,-1, 0,
  1155               0,-1, 0,
  1156               0,-1, 0,
  1157               0,-1, 0,
  1159               0, 0,-1,
  1160               0, 0,-1,
  1161               0, 0,-1,
  1162               0, 0,-1
  1163       ],
  1165   indices : [],
  1166   create : function(){
  1167     for (var i = 0; i < 6; i++) {
  1168       Cube.indices.push(i*4 + 0);
  1169       Cube.indices.push(i*4 + 1);
  1170       Cube.indices.push(i*4 + 3);
  1171       Cube.indices.push(i*4 + 1);
  1172       Cube.indices.push(i*4 + 2);
  1173       Cube.indices.push(i*4 + 3);
  1175   },
  1177   makeVBO : function(gl) {
  1178     return new VBO(gl,
  1179         {size:3, data: Cube.vertices},
  1180         {size:3, data: Cube.normals},
  1181         {elements: true, data: Cube.indices}
  1183   },
  1184   cache : {},
  1185   getCachedVBO : function(gl) {
  1186     if (!this.cache[gl])
  1187       this.cache[gl] = this.makeVBO(gl);
  1188     return this.cache[gl];
  1191 Cube.create();
  1193 Sphere = {
  1194   vertices : [],
  1195   normals : [],
  1196   indices : [],
  1197   create : function(){
  1198     var r = 0.75;
  1199     function vert(theta, phi)
  1201       var r = 0.75;
  1202       var x, y, z, nx, ny, nz;
  1204       nx = Math.sin(theta) * Math.cos(phi);
  1205       ny = Math.sin(phi);
  1206       nz = Math.cos(theta) * Math.cos(phi);
  1207       Sphere.normals.push(nx);
  1208       Sphere.normals.push(ny);
  1209       Sphere.normals.push(nz);
  1211       x = r * Math.sin(theta) * Math.cos(phi);
  1212       y = r * Math.sin(phi);
  1213       z = r * Math.cos(theta) * Math.cos(phi);
  1214       Sphere.vertices.push(x);
  1215       Sphere.vertices.push(y);
  1216       Sphere.vertices.push(z);
  1218     for (var phi = -Math.PI/2; phi < Math.PI/2; phi += Math.PI/20) {
  1219       var phi2 = phi + Math.PI/20;
  1220       for (var theta = -Math.PI/2; theta <= Math.PI/2; theta += Math.PI/20) {
  1221         vert(theta, phi);
  1222         vert(theta, phi2);
  1228 Sphere.create();
  1230 initGL_CONTEXT_ID = function(){
  1231   var c = document.createElement('canvas');
  1232   var contextNames = ['webgl', 'experimental-webgl'];
  1233   GL_CONTEXT_ID = null;
  1234   for (var i=0; i<contextNames.length; i++) {
  1235     try {
  1236       if (c.getContext(contextNames[i])) {
  1237         GL_CONTEXT_ID = contextNames[i];
  1238         break;
  1240     } catch (e) {
  1243   if (!GL_CONTEXT_ID) {
  1244     log("No WebGL context found. Unable to run tests.");
  1248 initGL_CONTEXT_ID();

mercurial