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