comparison webGL/dandy/resources/#utils3d.js# @ 7:4343c1feedb5

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