|
1 /* |
|
2 Utilities for the OpenGL ES 2.0 HTML Canvas context |
|
3 |
|
4 Copyright (C) 2011 Ilmari Heikkinen <ilmari.heikkinen@gmail.com> |
|
5 |
|
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: |
|
14 |
|
15 The above copyright notice and this permission notice shall be |
|
16 included in all copies or substantial portions of the Software. |
|
17 |
|
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 */ |
|
27 |
|
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 } |
|
43 |
|
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 } |
|
49 |
|
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 } |
|
57 |
|
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 } |
|
66 |
|
67 gl.shaderSource(shader, str); |
|
68 gl.compileShader(shader); |
|
69 |
|
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 } |
|
77 |
|
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 } |
|
111 |
|
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 } |
|
120 |
|
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 } |
|
132 |
|
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 } |
|
140 |
|
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 } |
|
147 |
|
148 Math.cot = function(z) { return 1.0 / Math.tan(z); } |
|
149 |
|
150 /* |
|
151 Matrix utilities, using the OpenGL element order where |
|
152 the last 4 elements are the translation column. |
|
153 |
|
154 Uses flat arrays as matrices for performance. |
|
155 |
|
156 Most operations have in-place variants to avoid allocating temporary matrices. |
|
157 |
|
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. |
|
162 |
|
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 |
|
166 |
|
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 ], |
|
176 |
|
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 }, |
|
185 |
|
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 }, |
|
193 |
|
194 copyMatrix : function(src, dst) { |
|
195 for (var i=0; i<16; i++) dst[i] = src[i]; |
|
196 return dst; |
|
197 }, |
|
198 |
|
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 }, |
|
206 |
|
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 }, |
|
217 |
|
218 inverseTo3x3 : function(m) { |
|
219 return this.inverse4x4to3x3InPlace(m, this.newIdentity3x3()); |
|
220 }, |
|
221 |
|
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 }, |
|
247 |
|
248 inverse3x3 : function(m) { |
|
249 return this.inverse3x3InPlace(m, this.newIdentity3x3()); |
|
250 }, |
|
251 |
|
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 }, |
|
277 |
|
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); |
|
285 |
|
286 return [ |
|
287 X, 0, 0, 0, |
|
288 0, Y, 0, 0, |
|
289 A, B, C, -1, |
|
290 0, 0, D, 0 |
|
291 ]; |
|
292 }, |
|
293 |
|
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; |
|
299 |
|
300 return this.frustum(xmin, xmax, ymin, ymax, znear, zfar); |
|
301 }, |
|
302 |
|
303 mul4x4 : function (a,b) { |
|
304 return this.mul4x4InPlace(a,b,this.newIdentity()); |
|
305 }, |
|
306 |
|
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 }, |
|
374 |
|
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 }, |
|
385 |
|
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 }, |
|
414 |
|
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); }, |
|
451 |
|
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 }, |
|
460 |
|
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); }, |
|
477 |
|
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)); |
|
482 |
|
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 ]; |
|
489 |
|
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 ]; |
|
496 |
|
497 return this.mul4x4(m,t); |
|
498 }, |
|
499 |
|
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 }, |
|
508 |
|
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 }, |
|
519 |
|
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 }, |
|
527 |
|
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 } |
|
536 |
|
537 Vec3 = { |
|
538 make : function() { return [0,0,0]; }, |
|
539 copy : function(v) { return [v[0],v[1],v[2]]; }, |
|
540 |
|
541 add : function (u,v) { |
|
542 return [u[0]+v[0], u[1]+v[1], u[2]+v[2]]; |
|
543 }, |
|
544 |
|
545 sub : function (u,v) { |
|
546 return [u[0]-v[0], u[1]-v[1], u[2]-v[2]]; |
|
547 }, |
|
548 |
|
549 negate : function (u) { |
|
550 return [-u[0], -u[1], -u[2]]; |
|
551 }, |
|
552 |
|
553 direction : function (u,v) { |
|
554 return this.normalizeInPlace(this.sub(u,v)); |
|
555 }, |
|
556 |
|
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 }, |
|
562 |
|
563 normalize : function(v) { |
|
564 return this.normalizeInPlace(this.copy(v)); |
|
565 }, |
|
566 |
|
567 scale : function(f, v) { |
|
568 return [f*v[0], f*v[1], f*v[2]]; |
|
569 }, |
|
570 |
|
571 dot : function(u,v) { |
|
572 return u[0]*v[0] + u[1]*v[1] + u[2]*v[2]; |
|
573 }, |
|
574 |
|
575 inner : function(u,v) { |
|
576 return [u[0]*v[0], u[1]*v[1], u[2]*v[2]]; |
|
577 }, |
|
578 |
|
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 } |
|
587 |
|
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 : [], |
|
603 |
|
604 destroy : function() { |
|
605 if (this.shader != null) deleteShader(this.gl, this.shader); |
|
606 }, |
|
607 |
|
608 compile : function() { |
|
609 this.shader = loadShaderArray(this.gl, this.shaders); |
|
610 }, |
|
611 |
|
612 use : function() { |
|
613 if (this.shader == null) |
|
614 this.compile(); |
|
615 this.gl.useProgram(this.shader.program); |
|
616 }, |
|
617 |
|
618 uniform1fv : function(name, value) { |
|
619 var loc = this.uniform(name); |
|
620 this.gl.uniform1fv(loc, value); |
|
621 }, |
|
622 |
|
623 uniform2fv : function(name, value) { |
|
624 var loc = this.uniform(name); |
|
625 this.gl.uniform2fv(loc, value); |
|
626 }, |
|
627 |
|
628 uniform3fv : function(name, value) { |
|
629 var loc = this.uniform(name); |
|
630 this.gl.uniform3fv(loc, value); |
|
631 }, |
|
632 |
|
633 uniform4fv : function(name, value) { |
|
634 var loc = this.uniform(name); |
|
635 this.gl.uniform4fv(loc, value); |
|
636 }, |
|
637 |
|
638 uniform1f : function(name, value) { |
|
639 var loc = this.uniform(name); |
|
640 this.gl.uniform1f(loc, value); |
|
641 }, |
|
642 |
|
643 uniform2f : function(name, v1,v2) { |
|
644 var loc = this.uniform(name); |
|
645 this.gl.uniform2f(loc, v1,v2); |
|
646 }, |
|
647 |
|
648 uniform3f : function(name, v1,v2,v3) { |
|
649 var loc = this.uniform(name); |
|
650 this.gl.uniform3f(loc, v1,v2,v3); |
|
651 }, |
|
652 |
|
653 uniform4f : function(name, v1,v2,v3,v4) { |
|
654 var loc = this.uniform(name); |
|
655 this.gl.uniform4f(loc, v1, v2, v3, v4); |
|
656 }, |
|
657 |
|
658 uniform1iv : function(name, value) { |
|
659 var loc = this.uniform(name); |
|
660 this.gl.uniform1iv(loc, value); |
|
661 }, |
|
662 |
|
663 uniform2iv : function(name, value) { |
|
664 var loc = this.uniform(name); |
|
665 this.gl.uniform2iv(loc, value); |
|
666 }, |
|
667 |
|
668 uniform3iv : function(name, value) { |
|
669 var loc = this.uniform(name); |
|
670 this.gl.uniform3iv(loc, value); |
|
671 }, |
|
672 |
|
673 uniform4iv : function(name, value) { |
|
674 var loc = this.uniform(name); |
|
675 this.gl.uniform4iv(loc, value); |
|
676 }, |
|
677 |
|
678 uniform1i : function(name, value) { |
|
679 var loc = this.uniform(name); |
|
680 this.gl.uniform1i(loc, value); |
|
681 }, |
|
682 |
|
683 uniform2i : function(name, v1,v2) { |
|
684 var loc = this.uniform(name); |
|
685 this.gl.uniform2i(loc, v1,v2); |
|
686 }, |
|
687 |
|
688 uniform3i : function(name, v1,v2,v3) { |
|
689 var loc = this.uniform(name); |
|
690 this.gl.uniform3i(loc, v1,v2,v3); |
|
691 }, |
|
692 |
|
693 uniform4i : function(name, v1,v2,v3,v4) { |
|
694 var loc = this.uniform(name); |
|
695 this.gl.uniform4i(loc, v1, v2, v3, v4); |
|
696 }, |
|
697 |
|
698 uniformMatrix4fv : function(name, value) { |
|
699 var loc = this.uniform(name); |
|
700 this.gl.uniformMatrix4fv(loc, false, value); |
|
701 }, |
|
702 |
|
703 uniformMatrix3fv : function(name, value) { |
|
704 var loc = this.uniform(name); |
|
705 this.gl.uniformMatrix3fv(loc, false, value); |
|
706 }, |
|
707 |
|
708 uniformMatrix2fv : function(name, value) { |
|
709 var loc = this.uniform(name); |
|
710 this.gl.uniformMatrix2fv(loc, false, value); |
|
711 }, |
|
712 |
|
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 }, |
|
720 |
|
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 } |
|
741 |
|
742 |
|
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 } |
|
754 |
|
755 VBO.prototype = { |
|
756 initialized : false, |
|
757 length : 0, |
|
758 vbos : null, |
|
759 type : 'TRIANGLES', |
|
760 elementsVBO : null, |
|
761 elements : null, |
|
762 |
|
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 }, |
|
773 |
|
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 }, |
|
784 |
|
785 init : function() { |
|
786 this.destroy(); |
|
787 var gl = this.gl; |
|
788 |
|
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 } |
|
830 |
|
831 gl.bindBuffer(gl.ARRAY_BUFFER, null); |
|
832 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null); |
|
833 |
|
834 this.length = length; |
|
835 this.vbos = vbos; |
|
836 |
|
837 this.initialized = true; |
|
838 }, |
|
839 |
|
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 }, |
|
853 |
|
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 } |
|
865 |
|
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, |
|
879 |
|
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 }, |
|
885 |
|
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; |
|
891 |
|
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 } |
|
901 |
|
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"); |
|
915 |
|
916 gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0); |
|
917 checkError(gl, "FBO.init bind tex"); |
|
918 |
|
919 if (this.useDepth) { |
|
920 gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, rb); |
|
921 checkError(gl, "FBO.init bind depth buffer"); |
|
922 } |
|
923 |
|
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"); |
|
933 |
|
934 this.fbo = fbo; |
|
935 this.rbo = rb; |
|
936 this.texture = tex; |
|
937 this.initialized = true; |
|
938 }, |
|
939 |
|
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 }, |
|
948 |
|
949 use : function() { |
|
950 if (!this.initialized) this.init(); |
|
951 this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, this.fbo); |
|
952 } |
|
953 } |
|
954 |
|
955 function GLError(err, msg, fileName, lineNumber) { |
|
956 this.message = msg; |
|
957 this.glError = err; |
|
958 } |
|
959 |
|
960 GLError.prototype = new Error(); |
|
961 |
|
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 } |
|
983 |
|
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 } |
|
1000 |
|
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); |
|
1013 } |
|
1014 return false; |
|
1015 } |
|
1016 return true; |
|
1017 } |
|
1018 |
|
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); |
|
1033 } |
|
1034 return false; |
|
1035 } |
|
1036 return true; |
|
1037 } |
|
1038 |
|
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; |
|
1056 } |
|
1057 } |
|
1058 testPassed("assertThrowNoGLError", name, f); |
|
1059 return true; |
|
1060 } |
|
1061 |
|
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} |
|
1093 ) |
|
1094 }, |
|
1095 cache: {}, |
|
1096 getCachedVBO : function(gl) { |
|
1097 if (!this.cache[gl]) |
|
1098 this.cache[gl] = this.makeVBO(gl); |
|
1099 return this.cache[gl]; |
|
1100 } |
|
1101 } |
|
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, |
|
1107 |
|
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, |
|
1112 |
|
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, |
|
1117 |
|
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, |
|
1122 |
|
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, |
|
1127 |
|
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 ], |
|
1133 |
|
1134 normals : [ 1, 0, 0, |
|
1135 1, 0, 0, |
|
1136 1, 0, 0, |
|
1137 1, 0, 0, |
|
1138 |
|
1139 0, 1, 0, |
|
1140 0, 1, 0, |
|
1141 0, 1, 0, |
|
1142 0, 1, 0, |
|
1143 |
|
1144 0, 0, 1, |
|
1145 0, 0, 1, |
|
1146 0, 0, 1, |
|
1147 0, 0, 1, |
|
1148 |
|
1149 -1, 0, 0, |
|
1150 -1, 0, 0, |
|
1151 -1, 0, 0, |
|
1152 -1, 0, 0, |
|
1153 |
|
1154 0,-1, 0, |
|
1155 0,-1, 0, |
|
1156 0,-1, 0, |
|
1157 0,-1, 0, |
|
1158 |
|
1159 0, 0,-1, |
|
1160 0, 0,-1, |
|
1161 0, 0,-1, |
|
1162 0, 0,-1 |
|
1163 ], |
|
1164 |
|
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); |
|
1174 } |
|
1175 }, |
|
1176 |
|
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} |
|
1182 ) |
|
1183 }, |
|
1184 cache : {}, |
|
1185 getCachedVBO : function(gl) { |
|
1186 if (!this.cache[gl]) |
|
1187 this.cache[gl] = this.makeVBO(gl); |
|
1188 return this.cache[gl]; |
|
1189 } |
|
1190 } |
|
1191 Cube.create(); |
|
1192 |
|
1193 Sphere = { |
|
1194 vertices : [], |
|
1195 normals : [], |
|
1196 indices : [], |
|
1197 create : function(){ |
|
1198 var r = 0.75; |
|
1199 function vert(theta, phi) |
|
1200 { |
|
1201 var r = 0.75; |
|
1202 var x, y, z, nx, ny, nz; |
|
1203 |
|
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); |
|
1210 |
|
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); |
|
1217 } |
|
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); |
|
1223 } |
|
1224 } |
|
1225 } |
|
1226 } |
|
1227 |
|
1228 Sphere.create(); |
|
1229 |
|
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; |
|
1239 } |
|
1240 } catch (e) { |
|
1241 } |
|
1242 } |
|
1243 if (!GL_CONTEXT_ID) { |
|
1244 log("No WebGL context found. Unable to run tests."); |
|
1245 } |
|
1246 } |
|
1247 |
|
1248 initGL_CONTEXT_ID(); |