OLD | NEW |
| (Empty) |
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_LINE
AR); | |
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 matr
ix. | |
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_SH
ORT; | |
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_BYT
E, 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.RENDERB
UFFER, 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 if (!GL_CONTEXT_ID) { | |
1243 log("No WebGL context found. Unable to run tests."); | |
1244 } | |
1245 } | |
1246 | |
1247 initGL_CONTEXT_ID(); | |
OLD | NEW |