content/canvas/test/webgl-conformance/conformance/resources/webgl-test.js

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:356411fbfa90
1 /*
2 Copyright (C) 2011 Apple Computer, 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 COMPUTER, 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 COMPUTER, 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 function webglTestLog(msg) {
27 if (window.console && window.console.log) {
28 window.console.log(msg);
29 }
30 if (document.getElementById("console")) {
31 var log = document.getElementById("console");
32 log.innerHTML += msg + "<br>";
33 }
34 }
35
36 //
37 // create3DContext
38 //
39 // Returns the WebGLRenderingContext for any known implementation.
40 //
41 function create3DContext(canvas, attributes)
42 {
43 if (!canvas)
44 canvas = document.createElement("canvas");
45 var names = ["webgl", "experimental-webgl"];
46 var context = null;
47 for (var i = 0; i < names.length; ++i) {
48 try {
49 context = canvas.getContext(names[i], attributes);
50 } catch (e) {
51 }
52 if (context) {
53 break;
54 }
55 }
56 if (!context) {
57 throw "Unable to fetch WebGL rendering context for Canvas";
58 }
59 return context;
60 }
61
62 function createGLErrorWrapper(context, fname) {
63 return function() {
64 var rv = context[fname].apply(context, arguments);
65 var err = context.getError();
66 if (err != 0)
67 throw "GL error " + err + " in " + fname;
68 return rv;
69 };
70 }
71
72 function create3DContextWithWrapperThatThrowsOnGLError(canvas, attributes) {
73 var context = create3DContext(canvas, attributes);
74 // Thanks to Ilmari Heikkinen for the idea on how to implement this so elegantly.
75 var wrap = {};
76 for (var i in context) {
77 try {
78 if (typeof context[i] == 'function') {
79 wrap[i] = createGLErrorWrapper(context, i);
80 } else {
81 wrap[i] = context[i];
82 }
83 } catch (e) {
84 webglTestLog("createContextWrapperThatThrowsOnGLError: Error accessing " + i);
85 }
86 }
87 wrap.getError = function() {
88 return context.getError();
89 };
90 return wrap;
91 }
92
93 function getGLErrorAsString(ctx, err) {
94 if (err === ctx.NO_ERROR) {
95 return "NO_ERROR";
96 }
97 for (var name in ctx) {
98 if (ctx[name] === err) {
99 return name;
100 }
101 }
102 return "0x" + err.toString(16);
103 }
104
105 // Pass undefined for glError to test that it at least throws some error
106 function shouldGenerateGLError(ctx, glErrors, evalStr) {
107 if (!glErrors.length) {
108 glErrors = [glErrors];
109 }
110 var exception;
111 try {
112 eval(evalStr);
113 } catch (e) {
114 exception = e;
115 }
116 if (exception) {
117 testFailed(evalStr + " threw exception " + exception);
118 } else {
119 var err = ctx.getError();
120 if (glErrors.indexOf(err) < 0) {
121 var errStrs = [];
122 for (var ii = 0; ii < glErrors.length; ++ii) {
123 errStrs.push(getGLErrorAsString(ctx, glErrors[ii]));
124 }
125 testFailed(evalStr + " expected: " + errStrs.join(" or ") + ". Was " + getGLErrorAsString(ctx, err) + ".");
126 } else {
127 testPassed(evalStr + " generated expected GL error: " + getGLErrorAsString(ctx, err) + ".");
128 }
129 }
130 }
131
132 /**
133 * Tests that the first error GL returns is the specified error.
134 * @param {!WebGLContext} gl The WebGLContext to use.
135 * @param {number|!Array.<number>} glError The expected gl
136 * error. Multiple errors can be passed in using an
137 * array.
138 * @param {string} opt_msg Optional additional message.
139 */
140 function glErrorShouldBe(gl, glErrors, opt_msg) {
141 if (!glErrors.length) {
142 glErrors = [glErrors];
143 }
144 opt_msg = opt_msg || "";
145 var err = gl.getError();
146 var ndx = glErrors.indexOf(err);
147 if (ndx < 0) {
148 if (glErrors.length == 1) {
149 testFailed("getError expected: " + getGLErrorAsString(gl, glErrors[0]) +
150 ". Was " + getGLErrorAsString(gl, err) + " : " + opt_msg);
151 } else {
152 var errs = [];
153 for (var ii = 0; ii < glErrors.length; ++ii) {
154 errs.push(getGLErrorAsString(gl, glErrors[ii]));
155 }
156 testFailed("getError expected one of: [" + errs.join(", ") +
157 "]. Was " + getGLErrorAsString(gl, err) + " : " + opt_msg);
158 }
159 } else {
160 testPassed("getError was expected value: " +
161 getGLErrorAsString(gl, err) + " : " + opt_msg);
162 }
163 };
164
165 //
166 // createProgram
167 //
168 // Create and return a program object, attaching each of the given shaders.
169 //
170 // If attribs are given, bind an attrib with that name at that index.
171 //
172 function createProgram(gl, vshaders, fshaders, attribs)
173 {
174 if (typeof(vshaders) == "string")
175 vshaders = [vshaders];
176 if (typeof(fshaders) == "string")
177 fshaders = [fshaders];
178
179 var shaders = [];
180 var i;
181
182 for (i = 0; i < vshaders.length; ++i) {
183 var shader = loadShader(gl, vshaders[i], gl.VERTEX_SHADER);
184 if (!shader)
185 return null;
186 shaders.push(shader);
187 }
188
189 for (i = 0; i < fshaders.length; ++i) {
190 var shader = loadShader(gl, fshaders[i], gl.FRAGMENT_SHADER);
191 if (!shader)
192 return null;
193 shaders.push(shader);
194 }
195
196 var prog = gl.createProgram();
197 for (i = 0; i < shaders.length; ++i) {
198 gl.attachShader(prog, shaders[i]);
199 }
200
201 if (attribs) {
202 for (var i = 0; i < attribs.length; ++i) {
203 gl.bindAttribLocation(prog, i, attribs[i]);
204 }
205 }
206
207 gl.linkProgram(prog);
208
209 // Check the link status
210 var linked = gl.getProgramParameter(prog, gl.LINK_STATUS);
211 if (!linked) {
212 // something went wrong with the link
213 var error = gl.getProgramInfoLog(prog);
214 webglTestLog("Error in program linking:" + error);
215
216 gl.deleteProgram(prog);
217 for (i = 0; i < shaders.length; ++i)
218 gl.deleteShader(shaders[i]);
219 return null;
220 }
221
222 return prog;
223 }
224
225 //
226 // initWebGL
227 //
228 // Initialize the Canvas element with the passed name as a WebGL object and return the
229 // WebGLRenderingContext.
230 //
231 // Load shaders with the passed names and create a program with them. Return this program
232 // in the 'program' property of the returned context.
233 //
234 // For each string in the passed attribs array, bind an attrib with that name at that index.
235 // Once the attribs are bound, link the program and then use it.
236 //
237 // Set the clear color to the passed array (4 values) and set the clear depth to the passed value.
238 // Enable depth testing and blending with a blend func of (SRC_ALPHA, ONE_MINUS_SRC_ALPHA)
239 //
240 function initWebGL(canvasName, vshader, fshader, attribs, clearColor, clearDepth, contextAttribs)
241 {
242 var canvas = document.getElementById(canvasName);
243 var gl = create3DContext(canvas, contextAttribs);
244 if (!gl) {
245 alert("No WebGL context found");
246 return null;
247 }
248
249 // Create the program object
250 gl.program = createProgram(gl, vshader, fshader, attribs);
251 if (!gl.program)
252 return null;
253
254 gl.useProgram(gl.program);
255
256 gl.clearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]);
257 gl.clearDepth(clearDepth);
258
259 gl.enable(gl.DEPTH_TEST);
260 gl.enable(gl.BLEND);
261 gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
262
263 return gl;
264 }
265
266 //
267 // getShaderSource
268 //
269 // Load the source from the passed shader file.
270 //
271 function getShaderSource(file)
272 {
273 var xhr = new XMLHttpRequest();
274 xhr.open("GET", file, false);
275 xhr.send();
276 return xhr.responseText;
277 }
278
279
280 //
281 // loadShader
282 //
283 // 'shader' is either the id of a <script> element containing the shader source
284 // string, the shader string itself, or the URL of a file containing the shader
285 // source. Load this shader and return the WebGLShader object corresponding to
286 // it.
287 //
288 function loadShader(ctx, shaderId, shaderType, isFile)
289 {
290 var shaderSource = "";
291
292 if (isFile)
293 shaderSource = getShaderSource(shaderId);
294 else {
295 var shaderScript = document.getElementById(shaderId);
296 if (!shaderScript) {
297 shaderSource = shaderId;
298 } else {
299 if (shaderScript.type == "x-shader/x-vertex") {
300 shaderType = ctx.VERTEX_SHADER;
301 } else if (shaderScript.type == "x-shader/x-fragment") {
302 shaderType = ctx.FRAGMENT_SHADER;
303 } else if (shaderType != ctx.VERTEX_SHADER && shaderType != ctx.FRAGMENT_SHADER) {
304 webglTestLog("*** Error: unknown shader type");
305 return null;
306 }
307
308 shaderSource = shaderScript.text;
309 }
310 }
311
312 // Create the shader object
313 var shader = ctx.createShader(shaderType);
314 if (shader == null) {
315 webglTestLog("*** Error: unable to create shader '"+shaderId+"'");
316 return null;
317 }
318
319 // Load the shader source
320 ctx.shaderSource(shader, shaderSource);
321
322 // Compile the shader
323 ctx.compileShader(shader);
324
325 // Check the compile status
326 var compiled = ctx.getShaderParameter(shader, ctx.COMPILE_STATUS);
327 if (!compiled) {
328 // Something went wrong during compilation; get the error
329 var error = ctx.getShaderInfoLog(shader);
330 webglTestLog("*** Error compiling shader '"+shader+"':"+error);
331 ctx.deleteShader(shader);
332 return null;
333 }
334
335 return shader;
336 }
337
338 function loadShaderFromFile(ctx, file, type)
339 {
340 return loadShader(ctx, file, type, true);
341 }
342
343 function loadShaderFromScript(ctx, script)
344 {
345 return loadShader(ctx, script, 0, false);
346 }
347
348 function loadStandardProgram(context) {
349 var program = context.createProgram();
350 context.attachShader(program, loadStandardVertexShader(context));
351 context.attachShader(program, loadStandardFragmentShader(context));
352 context.linkProgram(program);
353 return program;
354 }
355
356 function loadProgram(context, vertexShaderPath, fragmentShaderPath, isFile) {
357 isFile = (isFile === undefined) ? true : isFile;
358 var program = context.createProgram();
359 context.attachShader(program, loadShader(context, vertexShaderPath, context.VERTEX_SHADER, isFile));
360 context.attachShader(program, loadShader(context, fragmentShaderPath, context.FRAGMENT_SHADER, isFile));
361 context.linkProgram(program);
362 return program;
363 }
364
365 var getBasePathForResources = function() {
366 var expectedBase = "webgl-test.js";
367 var scripts = document.getElementsByTagName('script');
368 for (var script, i = 0; script = scripts[i]; i++) {
369 var src = script.src;
370 var l = src.length;
371 if (src.substr(l - expectedBase.length) == expectedBase) {
372 return src.substr(0, l - expectedBase.length);
373 }
374 }
375 throw 'oops';
376 };
377
378
379 function loadStandardVertexShader(context) {
380 return loadShader(
381 context,
382 getBasePathForResources() + "vertexShader.vert",
383 context.VERTEX_SHADER,
384 true);
385 }
386
387 function loadStandardFragmentShader(context) {
388 return loadShader(
389 context,
390 getBasePathForResources() + "fragmentShader.frag",
391 context.FRAGMENT_SHADER,
392 true);
393 }
394
395 //
396 // makeBox
397 //
398 // Create a box with vertices, normals and texCoords. Create VBOs for each as well as the index array.
399 // Return an object with the following properties:
400 //
401 // normalObject WebGLBuffer object for normals
402 // texCoordObject WebGLBuffer object for texCoords
403 // vertexObject WebGLBuffer object for vertices
404 // indexObject WebGLBuffer object for indices
405 // numIndices The number of indices in the indexObject
406 //
407 function makeBox(ctx)
408 {
409 // box
410 // v6----- v5
411 // /| /|
412 // v1------v0|
413 // | | | |
414 // | |v7---|-|v4
415 // |/ |/
416 // v2------v3
417 //
418 // vertex coords array
419 var vertices = new Float32Array(
420 [ 1, 1, 1, -1, 1, 1, -1,-1, 1, 1,-1, 1, // v0-v1-v2-v3 front
421 1, 1, 1, 1,-1, 1, 1,-1,-1, 1, 1,-1, // v0-v3-v4-v5 right
422 1, 1, 1, 1, 1,-1, -1, 1,-1, -1, 1, 1, // v0-v5-v6-v1 top
423 -1, 1, 1, -1, 1,-1, -1,-1,-1, -1,-1, 1, // v1-v6-v7-v2 left
424 -1,-1,-1, 1,-1,-1, 1,-1, 1, -1,-1, 1, // v7-v4-v3-v2 bottom
425 1,-1,-1, -1,-1,-1, -1, 1,-1, 1, 1,-1 ] // v4-v7-v6-v5 back
426 );
427
428 // normal array
429 var normals = new Float32Array(
430 [ 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, // v0-v1-v2-v3 front
431 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, // v0-v3-v4-v5 right
432 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, // v0-v5-v6-v1 top
433 -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, // v1-v6-v7-v2 left
434 0,-1, 0, 0,-1, 0, 0,-1, 0, 0,-1, 0, // v7-v4-v3-v2 bottom
435 0, 0,-1, 0, 0,-1, 0, 0,-1, 0, 0,-1 ] // v4-v7-v6-v5 back
436 );
437
438
439 // texCoord array
440 var texCoords = new Float32Array(
441 [ 1, 1, 0, 1, 0, 0, 1, 0, // v0-v1-v2-v3 front
442 0, 1, 0, 0, 1, 0, 1, 1, // v0-v3-v4-v5 right
443 1, 0, 1, 1, 0, 1, 0, 0, // v0-v5-v6-v1 top
444 1, 1, 0, 1, 0, 0, 1, 0, // v1-v6-v7-v2 left
445 0, 0, 1, 0, 1, 1, 0, 1, // v7-v4-v3-v2 bottom
446 0, 0, 1, 0, 1, 1, 0, 1 ] // v4-v7-v6-v5 back
447 );
448
449 // index array
450 var indices = new Uint8Array(
451 [ 0, 1, 2, 0, 2, 3, // front
452 4, 5, 6, 4, 6, 7, // right
453 8, 9,10, 8,10,11, // top
454 12,13,14, 12,14,15, // left
455 16,17,18, 16,18,19, // bottom
456 20,21,22, 20,22,23 ] // back
457 );
458
459 var retval = { };
460
461 retval.normalObject = ctx.createBuffer();
462 ctx.bindBuffer(ctx.ARRAY_BUFFER, retval.normalObject);
463 ctx.bufferData(ctx.ARRAY_BUFFER, normals, ctx.STATIC_DRAW);
464
465 retval.texCoordObject = ctx.createBuffer();
466 ctx.bindBuffer(ctx.ARRAY_BUFFER, retval.texCoordObject);
467 ctx.bufferData(ctx.ARRAY_BUFFER, texCoords, ctx.STATIC_DRAW);
468
469 retval.vertexObject = ctx.createBuffer();
470 ctx.bindBuffer(ctx.ARRAY_BUFFER, retval.vertexObject);
471 ctx.bufferData(ctx.ARRAY_BUFFER, vertices, ctx.STATIC_DRAW);
472
473 ctx.bindBuffer(ctx.ARRAY_BUFFER, 0);
474
475 retval.indexObject = ctx.createBuffer();
476 ctx.bindBuffer(ctx.ELEMENT_ARRAY_BUFFER, retval.indexObject);
477 ctx.bufferData(ctx.ELEMENT_ARRAY_BUFFER, indices, ctx.STATIC_DRAW);
478 ctx.bindBuffer(ctx.ELEMENT_ARRAY_BUFFER, 0);
479
480 retval.numIndices = indices.length;
481
482 return retval;
483 }
484
485 //
486 // makeSphere
487 //
488 // Create a sphere with the passed number of latitude and longitude bands and the passed radius.
489 // Sphere has vertices, normals and texCoords. Create VBOs for each as well as the index array.
490 // Return an object with the following properties:
491 //
492 // normalObject WebGLBuffer object for normals
493 // texCoordObject WebGLBuffer object for texCoords
494 // vertexObject WebGLBuffer object for vertices
495 // indexObject WebGLBuffer object for indices
496 // numIndices The number of indices in the indexObject
497 //
498 function makeSphere(ctx, radius, lats, longs)
499 {
500 var geometryData = [ ];
501 var normalData = [ ];
502 var texCoordData = [ ];
503 var indexData = [ ];
504
505 for (var latNumber = 0; latNumber <= lats; ++latNumber) {
506 for (var longNumber = 0; longNumber <= longs; ++longNumber) {
507 var theta = latNumber * Math.PI / lats;
508 var phi = longNumber * 2 * Math.PI / longs;
509 var sinTheta = Math.sin(theta);
510 var sinPhi = Math.sin(phi);
511 var cosTheta = Math.cos(theta);
512 var cosPhi = Math.cos(phi);
513
514 var x = cosPhi * sinTheta;
515 var y = cosTheta;
516 var z = sinPhi * sinTheta;
517 var u = 1-(longNumber/longs);
518 var v = latNumber/lats;
519
520 normalData.push(x);
521 normalData.push(y);
522 normalData.push(z);
523 texCoordData.push(u);
524 texCoordData.push(v);
525 geometryData.push(radius * x);
526 geometryData.push(radius * y);
527 geometryData.push(radius * z);
528 }
529 }
530
531 longs += 1;
532 for (var latNumber = 0; latNumber < lats; ++latNumber) {
533 for (var longNumber = 0; longNumber < longs; ++longNumber) {
534 var first = (latNumber * longs) + (longNumber % longs);
535 var second = first + longs;
536 indexData.push(first);
537 indexData.push(second);
538 indexData.push(first+1);
539
540 indexData.push(second);
541 indexData.push(second+1);
542 indexData.push(first+1);
543 }
544 }
545
546 var retval = { };
547
548 retval.normalObject = ctx.createBuffer();
549 ctx.bindBuffer(ctx.ARRAY_BUFFER, retval.normalObject);
550 ctx.bufferData(ctx.ARRAY_BUFFER, new Float32Array(normalData), ctx.STATIC_DRAW);
551
552 retval.texCoordObject = ctx.createBuffer();
553 ctx.bindBuffer(ctx.ARRAY_BUFFER, retval.texCoordObject);
554 ctx.bufferData(ctx.ARRAY_BUFFER, new Float32Array(texCoordData), ctx.STATIC_DRAW);
555
556 retval.vertexObject = ctx.createBuffer();
557 ctx.bindBuffer(ctx.ARRAY_BUFFER, retval.vertexObject);
558 ctx.bufferData(ctx.ARRAY_BUFFER, new Float32Array(geometryData), ctx.STATIC_DRAW);
559
560 retval.numIndices = indexData.length;
561 retval.indexObject = ctx.createBuffer();
562 ctx.bindBuffer(ctx.ELEMENT_ARRAY_BUFFER, retval.indexObject);
563 ctx.bufferData(ctx.ELEMENT_ARRAY_BUFFER, new Uint16Array(indexData), ctx.STREAM_DRAW);
564
565 return retval;
566 }
567
568 //
569 // loadObj
570 //
571 // Load a .obj file from the passed URL. Return an object with a 'loaded' property set to false.
572 // When the object load is complete, the 'loaded' property becomes true and the following
573 // properties are set:
574 //
575 // normalObject WebGLBuffer object for normals
576 // texCoordObject WebGLBuffer object for texCoords
577 // vertexObject WebGLBuffer object for vertices
578 // indexObject WebGLBuffer object for indices
579 // numIndices The number of indices in the indexObject
580 //
581 function loadObj(ctx, url)
582 {
583 var obj = { loaded : false };
584 obj.ctx = ctx;
585 var req = new XMLHttpRequest();
586 req.obj = obj;
587 req.onreadystatechange = function () { processLoadObj(req) };
588 req.open("GET", url, true);
589 req.send(null);
590 return obj;
591 }
592
593 function processLoadObj(req)
594 {
595 webglTestLog("req="+req)
596 // only if req shows "complete"
597 if (req.readyState == 4) {
598 doLoadObj(req.obj, req.responseText);
599 }
600 }
601
602 function doLoadObj(obj, text)
603 {
604 vertexArray = [ ];
605 normalArray = [ ];
606 textureArray = [ ];
607 indexArray = [ ];
608
609 var vertex = [ ];
610 var normal = [ ];
611 var texture = [ ];
612 var facemap = { };
613 var index = 0;
614
615 var lines = text.split("\n");
616 for (var lineIndex in lines) {
617 var line = lines[lineIndex].replace(/[ \t]+/g, " ").replace(/\s\s*$/, "");
618
619 // ignore comments
620 if (line[0] == "#")
621 continue;
622
623 var array = line.split(" ");
624 if (array[0] == "v") {
625 // vertex
626 vertex.push(parseFloat(array[1]));
627 vertex.push(parseFloat(array[2]));
628 vertex.push(parseFloat(array[3]));
629 }
630 else if (array[0] == "vt") {
631 // normal
632 texture.push(parseFloat(array[1]));
633 texture.push(parseFloat(array[2]));
634 }
635 else if (array[0] == "vn") {
636 // normal
637 normal.push(parseFloat(array[1]));
638 normal.push(parseFloat(array[2]));
639 normal.push(parseFloat(array[3]));
640 }
641 else if (array[0] == "f") {
642 // face
643 if (array.length != 4) {
644 webglTestLog("*** Error: face '"+line+"' not handled");
645 continue;
646 }
647
648 for (var i = 1; i < 4; ++i) {
649 if (!(array[i] in facemap)) {
650 // add a new entry to the map and arrays
651 var f = array[i].split("/");
652 var vtx, nor, tex;
653
654 if (f.length == 1) {
655 vtx = parseInt(f[0]) - 1;
656 nor = vtx;
657 tex = vtx;
658 }
659 else if (f.length = 3) {
660 vtx = parseInt(f[0]) - 1;
661 tex = parseInt(f[1]) - 1;
662 nor = parseInt(f[2]) - 1;
663 }
664 else {
665 webglTestLog("*** Error: did not understand face '"+array[i]+"'");
666 return null;
667 }
668
669 // do the vertices
670 var x = 0;
671 var y = 0;
672 var z = 0;
673 if (vtx * 3 + 2 < vertex.length) {
674 x = vertex[vtx*3];
675 y = vertex[vtx*3+1];
676 z = vertex[vtx*3+2];
677 }
678 vertexArray.push(x);
679 vertexArray.push(y);
680 vertexArray.push(z);
681
682 // do the textures
683 x = 0;
684 y = 0;
685 if (tex * 2 + 1 < texture.length) {
686 x = texture[tex*2];
687 y = texture[tex*2+1];
688 }
689 textureArray.push(x);
690 textureArray.push(y);
691
692 // do the normals
693 x = 0;
694 y = 0;
695 z = 1;
696 if (nor * 3 + 2 < normal.length) {
697 x = normal[nor*3];
698 y = normal[nor*3+1];
699 z = normal[nor*3+2];
700 }
701 normalArray.push(x);
702 normalArray.push(y);
703 normalArray.push(z);
704
705 facemap[array[i]] = index++;
706 }
707
708 indexArray.push(facemap[array[i]]);
709 }
710 }
711 }
712
713 // set the VBOs
714 obj.normalObject = obj.ctx.createBuffer();
715 obj.ctx.bindBuffer(obj.ctx.ARRAY_BUFFER, obj.normalObject);
716 obj.ctx.bufferData(obj.ctx.ARRAY_BUFFER, new Float32Array(normalArray), obj.ctx.STATIC_DRAW);
717
718 obj.texCoordObject = obj.ctx.createBuffer();
719 obj.ctx.bindBuffer(obj.ctx.ARRAY_BUFFER, obj.texCoordObject);
720 obj.ctx.bufferData(obj.ctx.ARRAY_BUFFER, new Float32Array(textureArray), obj.ctx.STATIC_DRAW);
721
722 obj.vertexObject = obj.ctx.createBuffer();
723 obj.ctx.bindBuffer(obj.ctx.ARRAY_BUFFER, obj.vertexObject);
724 obj.ctx.bufferData(obj.ctx.ARRAY_BUFFER, new Float32Array(vertexArray), obj.ctx.STATIC_DRAW);
725
726 obj.numIndices = indexArray.length;
727 obj.indexObject = obj.ctx.createBuffer();
728 obj.ctx.bindBuffer(obj.ctx.ELEMENT_ARRAY_BUFFER, obj.indexObject);
729 obj.ctx.bufferData(obj.ctx.ELEMENT_ARRAY_BUFFER, new Uint16Array(indexArray), obj.ctx.STREAM_DRAW);
730
731 obj.loaded = true;
732 }
733
734 //
735 // loadImageTexture
736 //
737 // Load the image at the passed url, place it in a new WebGLTexture object and return the WebGLTexture.
738 //
739 function loadImageTexture(ctx, url)
740 {
741 var texture = ctx.createTexture();
742 texture.image = new Image();
743 texture.image.onload = function() { doLoadImageTexture(ctx, texture.image, texture) }
744 texture.image.src = url;
745 return texture;
746 }
747
748 function doLoadImageTexture(ctx, image, texture)
749 {
750 ctx.enable(ctx.TEXTURE_2D);
751 ctx.bindTexture(ctx.TEXTURE_2D, texture);
752 ctx.texImage2D(ctx.TEXTURE_2D, 0, ctx.RGBA, ctx.RGBA, ctx.UNSIGNED_BYTE, image);
753 ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_MAG_FILTER, ctx.LINEAR);
754 ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_MIN_FILTER, ctx.LINEAR_MIPMAP_LINEAR);
755 ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_WRAP_S, ctx.CLAMP_TO_EDGE);
756 ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_WRAP_T, ctx.CLAMP_TO_EDGE);
757 ctx.generateMipmap(ctx.TEXTURE_2D)
758 ctx.bindTexture(ctx.TEXTURE_2D, 0);
759 }
760
761 //
762 // Framerate object
763 //
764 // This object keeps track of framerate and displays it as the innerHTML text of the
765 // HTML element with the passed id. Once created you call snapshot at the end
766 // of every rendering cycle. Every 500ms the framerate is updated in the HTML element.
767 //
768 Framerate = function(id)
769 {
770 this.numFramerates = 10;
771 this.framerateUpdateInterval = 500;
772 this.id = id;
773
774 this.renderTime = -1;
775 this.framerates = [ ];
776 self = this;
777 var fr = function() { self.updateFramerate() }
778 setInterval(fr, this.framerateUpdateInterval);
779 }
780
781 Framerate.prototype.updateFramerate = function()
782 {
783 var tot = 0;
784 for (var i = 0; i < this.framerates.length; ++i)
785 tot += this.framerates[i];
786
787 var framerate = tot / this.framerates.length;
788 framerate = Math.round(framerate);
789 document.getElementById(this.id).innerHTML = "Framerate:"+framerate+"fps";
790 }
791
792 Framerate.prototype.snapshot = function()
793 {
794 if (this.renderTime < 0)
795 this.renderTime = new Date().getTime();
796 else {
797 var newTime = new Date().getTime();
798 var t = newTime - this.renderTime;
799 var framerate = 1000/t;
800 this.framerates.push(framerate);
801 while (this.framerates.length > this.numFramerates)
802 this.framerates.shift();
803 this.renderTime = newTime;
804 }
805 }

mercurial