Mercurial > hg > Members > e105711
diff webGL/src/J3DI.js @ 0:9285dae61395 draft
1st commit,just play sounds.
author | e105711 <yomitan.ie.u-ryukyu.ac.jp> |
---|---|
date | Wed, 25 Apr 2012 23:45:59 +0900 |
parents | |
children | 55702e139f69 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/webGL/src/J3DI.js Wed Apr 25 23:45:59 2012 +0900 @@ -0,0 +1,583 @@ +/* + * Copyright (C) 2009 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// +// initWebGL +// +// Initialize the Canvas element with the passed name as a WebGL object and return the +// WebGLRenderingContext. +// +// Load shaders with the passed names and create a program with them. Return this program +// in the 'program' property of the returned context. +// +// For each string in the passed attribs array, bind an attrib with that name at that index. +// Once the attribs are bound, link the program and then use it. +// +// Set the clear color to the passed array (4 values) and set the clear depth to the passed value. +// Enable depth testing and blending with a blend func of (SRC_ALPHA, ONE_MINUS_SRC_ALPHA) +// +// A console function is added to the context: console(string). This can be replaced +// by the caller. By default, it maps to the window.console() function on WebKit and to +// an empty function on other browsers. +// +function initWebGL(canvasName, vshader, fshader, attribs, clearColor, clearDepth) +{ + var canvas = document.getElementById(canvasName); + var gl = canvas.getContext("experimental-webgl"); + if (!gl) { + alert("No WebGL context found"); + return null; + } + + // Add a console + gl.console = ("console" in window) ? window.console : { log: function() { } }; + + // create our shaders + var vertexShader = loadShader(gl, vshader); + var fragmentShader = loadShader(gl, fshader); + + if (!vertexShader || !fragmentShader) + return null; + + // Create the program object + gl.program = gl.createProgram(); + + if (!gl.program) + return null; + + // Attach our two shaders to the program + gl.attachShader (gl.program, vertexShader); + gl.attachShader (gl.program, fragmentShader); + + // Bind attributes + for (var i = 0; i < attribs.length; ++i) + gl.bindAttribLocation (gl.program, i, attribs[i]); + + // Link the program + gl.linkProgram(gl.program); + + // Check the link status + var linked = gl.getProgramParameter(gl.program, gl.LINK_STATUS); + if (!linked) { + // something went wrong with the link + var error = gl.getProgramInfoLog (gl.program); + gl.console.log("Error in program linking:"+error); + + gl.deleteProgram(gl.program); + gl.deleteProgram(fragmentShader); + gl.deleteProgram(vertexShader); + + return null; + } + + gl.useProgram(gl.program); + + gl.clearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]); + gl.clearDepth(clearDepth); + + gl.enable(gl.DEPTH_TEST); + gl.enable(gl.BLEND); + gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); + + return gl; +} + +// +// loadShader +// +// 'shaderId' is the id of a <script> element containing the shader source string. +// Load this shader and return the WebGLShader object corresponding to it. +// +function loadShader(ctx, shaderId) +{ + var shaderScript = document.getElementById(shaderId); + if (!shaderScript) { + ctx.console.log("*** Error: shader script '"+shaderId+"' not found"); + return null; + } + + if (shaderScript.type == "x-shader/x-vertex") + var shaderType = ctx.VERTEX_SHADER; + else if (shaderScript.type == "x-shader/x-fragment") + var shaderType = ctx.FRAGMENT_SHADER; + else { + ctx.console.log("*** Error: shader script '"+shaderId+"' of undefined type '"+shaderScript.type+"'"); + return null; + } + + // Create the shader object + var shader = ctx.createShader(shaderType); + if (shader == null) { + ctx.console.log("*** Error: unable to create shader '"+shaderId+"'"); + return null; + } + + // Load the shader source + ctx.shaderSource(shader, shaderScript.text); + + // Compile the shader + ctx.compileShader(shader); + + // Check the compile status + var compiled = ctx.getShaderParameter(shader, ctx.COMPILE_STATUS); + if (!compiled) { + // Something went wrong during compilation; get the error + var error = ctx.getShaderInfoLog(shader); + ctx.console.log("*** Error compiling shader '"+shaderId+"':"+error); + ctx.deleteShader(shader); + return null; + } + + return shader; +} + +// +// makeBox +// +// Create a box with vertices, normals and texCoords. Create VBOs for each as well as the index array. +// Return an object with the following properties: +// +// normalObject WebGLBuffer object for normals +// texCoordObject WebGLBuffer object for texCoords +// vertexObject WebGLBuffer object for vertices +// indexObject WebGLBuffer object for indices +// numIndices The number of indices in the indexObject +// +function makeBox(ctx) +{ + // box + // v6----- v5 + // /| /| + // v1------v0| + // | | | | + // | |v7---|-|v4 + // |/ |/ + // v2------v3 + // + // vertex coords array + var vertices = new Float32Array( + [ 1, 1, 1, -1, 1, 1, -1,-1, 1, 1,-1, 1, // v0-v1-v2-v3 front + 1, 1, 1, 1,-1, 1, 1,-1,-1, 1, 1,-1, // v0-v3-v4-v5 right + 1, 1, 1, 1, 1,-1, -1, 1,-1, -1, 1, 1, // v0-v5-v6-v1 top + -1, 1, 1, -1, 1,-1, -1,-1,-1, -1,-1, 1, // v1-v6-v7-v2 left + -1,-1,-1, 1,-1,-1, 1,-1, 1, -1,-1, 1, // v7-v4-v3-v2 bottom + 1,-1,-1, -1,-1,-1, -1, 1,-1, 1, 1,-1 ] // v4-v7-v6-v5 back + ); + + // normal array + var normals = new Float32Array( + [ 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, // v0-v1-v2-v3 front + 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, // v0-v3-v4-v5 right + 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, // v0-v5-v6-v1 top + -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, // v1-v6-v7-v2 left + 0,-1, 0, 0,-1, 0, 0,-1, 0, 0,-1, 0, // v7-v4-v3-v2 bottom + 0, 0,-1, 0, 0,-1, 0, 0,-1, 0, 0,-1 ] // v4-v7-v6-v5 back + ); + + + // texCoord array + var texCoords = new Float32Array( + [ 1, 1, 0, 1, 0, 0, 1, 0, // v0-v1-v2-v3 front + 0, 1, 0, 0, 1, 0, 1, 1, // v0-v3-v4-v5 right + 1, 0, 1, 1, 0, 1, 0, 0, // v0-v5-v6-v1 top + 1, 1, 0, 1, 0, 0, 1, 0, // v1-v6-v7-v2 left + 0, 0, 1, 0, 1, 1, 0, 1, // v7-v4-v3-v2 bottom + 0, 0, 1, 0, 1, 1, 0, 1 ] // v4-v7-v6-v5 back + ); + + // index array + var indices = new Uint8Array( + [ 0, 1, 2, 0, 2, 3, // front + 4, 5, 6, 4, 6, 7, // right + 8, 9,10, 8,10,11, // top + 12,13,14, 12,14,15, // left + 16,17,18, 16,18,19, // bottom + 20,21,22, 20,22,23 ] // back + ); + + var retval = { }; + + retval.normalObject = ctx.createBuffer(); + ctx.bindBuffer(ctx.ARRAY_BUFFER, retval.normalObject); + ctx.bufferData(ctx.ARRAY_BUFFER, normals, ctx.STATIC_DRAW); + + retval.texCoordObject = ctx.createBuffer(); + ctx.bindBuffer(ctx.ARRAY_BUFFER, retval.texCoordObject); + ctx.bufferData(ctx.ARRAY_BUFFER, texCoords, ctx.STATIC_DRAW); + + retval.vertexObject = ctx.createBuffer(); + ctx.bindBuffer(ctx.ARRAY_BUFFER, retval.vertexObject); + ctx.bufferData(ctx.ARRAY_BUFFER, vertices, ctx.STATIC_DRAW); + + ctx.bindBuffer(ctx.ARRAY_BUFFER, null); + + retval.indexObject = ctx.createBuffer(); + ctx.bindBuffer(ctx.ELEMENT_ARRAY_BUFFER, retval.indexObject); + ctx.bufferData(ctx.ELEMENT_ARRAY_BUFFER, indices, ctx.STATIC_DRAW); + ctx.bindBuffer(ctx.ELEMENT_ARRAY_BUFFER, null); + + retval.numIndices = indices.length; + + return retval; +} + +// +// makeSphere +// +// Create a sphere with the passed number of latitude and longitude bands and the passed radius. +// Sphere has vertices, normals and texCoords. Create VBOs for each as well as the index array. +// Return an object with the following properties: +// +// normalObject WebGLBuffer object for normals +// texCoordObject WebGLBuffer object for texCoords +// vertexObject WebGLBuffer object for vertices +// indexObject WebGLBuffer object for indices +// numIndices The number of indices in the indexObject +// +function makeSphere(ctx, radius, lats, longs) +{ + var geometryData = [ ]; + var normalData = [ ]; + var texCoordData = [ ]; + var indexData = [ ]; + + for (var latNumber = 0; latNumber <= lats; ++latNumber) { + for (var longNumber = 0; longNumber <= longs; ++longNumber) { + var theta = latNumber * Math.PI / lats; + var phi = longNumber * 2 * Math.PI / longs; + var sinTheta = Math.sin(theta); + var sinPhi = Math.sin(phi); + var cosTheta = Math.cos(theta); + var cosPhi = Math.cos(phi); + + var x = cosPhi * sinTheta; + var y = cosTheta; + var z = sinPhi * sinTheta; + var u = 1-(longNumber/longs); + var v = latNumber/lats; + + normalData.push(x); + normalData.push(y); + normalData.push(z); + texCoordData.push(u); + texCoordData.push(v); + geometryData.push(radius * x); + geometryData.push(radius * y); + geometryData.push(radius * z); + } + } + + for (var latNumber = 0; latNumber < lats; ++latNumber) { + for (var longNumber = 0; longNumber < longs; ++longNumber) { + var first = (latNumber * (longs+1)) + longNumber; + var second = first + longs + 1; + indexData.push(first); + indexData.push(second); + indexData.push(first+1); + + indexData.push(second); + indexData.push(second+1); + indexData.push(first+1); + } + } + + var retval = { }; + + retval.normalObject = ctx.createBuffer(); + ctx.bindBuffer(ctx.ARRAY_BUFFER, retval.normalObject); + ctx.bufferData(ctx.ARRAY_BUFFER, new Float32Array(normalData), ctx.STATIC_DRAW); + + retval.texCoordObject = ctx.createBuffer(); + ctx.bindBuffer(ctx.ARRAY_BUFFER, retval.texCoordObject); + ctx.bufferData(ctx.ARRAY_BUFFER, new Float32Array(texCoordData), ctx.STATIC_DRAW); + + retval.vertexObject = ctx.createBuffer(); + ctx.bindBuffer(ctx.ARRAY_BUFFER, retval.vertexObject); + ctx.bufferData(ctx.ARRAY_BUFFER, new Float32Array(geometryData), ctx.STATIC_DRAW); + + retval.numIndices = indexData.length; + retval.indexObject = ctx.createBuffer(); + ctx.bindBuffer(ctx.ELEMENT_ARRAY_BUFFER, retval.indexObject); + ctx.bufferData(ctx.ELEMENT_ARRAY_BUFFER, new Uint16Array(indexData), ctx.STREAM_DRAW); + + return retval; +} + +// +// loadObj +// +// Load a .obj file from the passed URL. Return an object with a 'loaded' property set to false. +// When the object load is complete, the 'loaded' property becomes true and the following +// properties are set: +// +// normalObject WebGLBuffer object for normals +// texCoordObject WebGLBuffer object for texCoords +// vertexObject WebGLBuffer object for vertices +// indexObject WebGLBuffer object for indices +// numIndices The number of indices in the indexObject +// +function loadObj(ctx, url) +{ + var obj = { loaded : false }; + obj.ctx = ctx; + var req = new XMLHttpRequest(); + req.obj = obj; + req.onreadystatechange = function () { processLoadObj(req) }; + req.open("GET", url, true); + req.send(null); + return obj; +} + +function processLoadObj(req) +{ + req.obj.ctx.console.log("req="+req) + // only if req shows "complete" + if (req.readyState == 4) { + doLoadObj(req.obj, req.responseText); + } +} + +function doLoadObj(obj, text) +{ + vertexArray = [ ]; + normalArray = [ ]; + textureArray = [ ]; + indexArray = [ ]; + + var vertex = [ ]; + var normal = [ ]; + var texture = [ ]; + var facemap = { }; + var index = 0; + + // This is a map which associates a range of indices with a name + // The name comes from the 'g' tag (of the form "g NAME"). Indices + // are part of one group until another 'g' tag is seen. If any indices + // come before a 'g' tag, it is given the group name "_unnamed" + // 'group' is an object whose property names are the group name and + // whose value is a 2 element array with [<first index>, <num indices>] + var groups = { }; + var currentGroup = [-1, 0]; + groups["_unnamed"] = currentGroup; + + var lines = text.split("\n"); + for (var lineIndex in lines) { + var line = lines[lineIndex].replace(/[ \t]+/g, " ").replace(/\s\s*$/, ""); + + // ignore comments + if (line[0] == "#") + continue; + + var array = line.split(" "); + if (array[0] == "g") { + // new group + currentGroup = [indexArray.length, 0]; + groups[array[1]] = currentGroup; + } + else if (array[0] == "v") { + // vertex + vertex.push(parseFloat(array[1])); + vertex.push(parseFloat(array[2])); + vertex.push(parseFloat(array[3])); + } + else if (array[0] == "vt") { + // normal + texture.push(parseFloat(array[1])); + texture.push(parseFloat(array[2])); + } + else if (array[0] == "vn") { + // normal + normal.push(parseFloat(array[1])); + normal.push(parseFloat(array[2])); + normal.push(parseFloat(array[3])); + } + else if (array[0] == "f") { + // face + if (array.length != 4) { + obj.ctx.console.log("*** Error: face '"+line+"' not handled"); + continue; + } + + for (var i = 1; i < 4; ++i) { + if (!(array[i] in facemap)) { + // add a new entry to the map and arrays + var f = array[i].split("/"); + var vtx, nor, tex; + + if (f.length == 1) { + vtx = parseInt(f[0]) - 1; + nor = vtx; + tex = vtx; + } + else if (f.length = 3) { + vtx = parseInt(f[0]) - 1; + tex = parseInt(f[1]) - 1; + nor = parseInt(f[2]) - 1; + } + else { + obj.ctx.console.log("*** Error: did not understand face '"+array[i]+"'"); + return null; + } + + // do the vertices + var x = 0; + var y = 0; + var z = 0; + if (vtx * 3 + 2 < vertex.length) { + x = vertex[vtx*3]; + y = vertex[vtx*3+1]; + z = vertex[vtx*3+2]; + } + vertexArray.push(x); + vertexArray.push(y); + vertexArray.push(z); + + // do the textures + x = 0; + y = 0; + if (tex * 2 + 1 < texture.length) { + x = texture[tex*2]; + y = texture[tex*2+1]; + } + textureArray.push(x); + textureArray.push(y); + + // do the normals + x = 0; + y = 0; + z = 1; + if (nor * 3 + 2 < normal.length) { + x = normal[nor*3]; + y = normal[nor*3+1]; + z = normal[nor*3+2]; + } + normalArray.push(x); + normalArray.push(y); + normalArray.push(z); + + facemap[array[i]] = index++; + } + + indexArray.push(facemap[array[i]]); + currentGroup[1]++; + } + } + } + + // set the VBOs + obj.normalObject = obj.ctx.createBuffer(); + obj.ctx.bindBuffer(obj.ctx.ARRAY_BUFFER, obj.normalObject); + obj.ctx.bufferData(obj.ctx.ARRAY_BUFFER, new Float32Array(normalArray), obj.ctx.STATIC_DRAW); + + obj.texCoordObject = obj.ctx.createBuffer(); + obj.ctx.bindBuffer(obj.ctx.ARRAY_BUFFER, obj.texCoordObject); + obj.ctx.bufferData(obj.ctx.ARRAY_BUFFER, new Float32Array(textureArray), obj.ctx.STATIC_DRAW); + + obj.vertexObject = obj.ctx.createBuffer(); + obj.ctx.bindBuffer(obj.ctx.ARRAY_BUFFER, obj.vertexObject); + obj.ctx.bufferData(obj.ctx.ARRAY_BUFFER, new Float32Array(vertexArray), obj.ctx.STATIC_DRAW); + + obj.numIndices = indexArray.length; + obj.indexObject = obj.ctx.createBuffer(); + obj.ctx.bindBuffer(obj.ctx.ELEMENT_ARRAY_BUFFER, obj.indexObject); + obj.ctx.bufferData(obj.ctx.ELEMENT_ARRAY_BUFFER, new Uint16Array(indexArray), obj.ctx.STREAM_DRAW); + + obj.groups = groups; + + obj.loaded = true; +} + +// +// loadImageTexture +// +// Load the image at the passed url, place it in a new WebGLTexture object and return the WebGLTexture. +// +function loadImageTexture(ctx, url) +{ + var texture = ctx.createTexture(); + texture.image = new Image(); + texture.image.onload = function() { doLoadImageTexture(ctx, texture.image, texture) } + texture.image.src = url; + return texture; +} + +function doLoadImageTexture(ctx, image, texture) +{ + ctx.bindTexture(ctx.TEXTURE_2D, texture); + ctx.texImage2D( + ctx.TEXTURE_2D, 0, ctx.RGBA, ctx.RGBA, ctx.UNSIGNED_BYTE, image); + ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_MAG_FILTER, ctx.LINEAR); + ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_MIN_FILTER, ctx.LINEAR); + ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_WRAP_S, ctx.CLAMP_TO_EDGE); + ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_WRAP_T, ctx.CLAMP_TO_EDGE); + //ctx.generateMipmap(ctx.TEXTURE_2D) + ctx.bindTexture(ctx.TEXTURE_2D, null); +} + +// +// Framerate object +// +// This object keeps track of framerate and displays it as the innerHTML text of the +// HTML element with the passed id. Once created you call snapshot at the end +// of every rendering cycle. Every 500ms the framerate is updated in the HTML element. +// +Framerate = function(id) +{ + this.numFramerates = 10; + this.framerateUpdateInterval = 500; + this.id = id; + + this.renderTime = -1; + this.framerates = [ ]; + self = this; + var fr = function() { self.updateFramerate() } + setInterval(fr, this.framerateUpdateInterval); +} + +Framerate.prototype.updateFramerate = function() +{ + var tot = 0; + for (var i = 0; i < this.framerates.length; ++i) + tot += this.framerates[i]; + + var framerate = tot / this.framerates.length; + framerate = Math.round(framerate); + document.getElementById(this.id).innerHTML = "Framerate:"+framerate+"fps"; +} + +Framerate.prototype.snapshot = function() +{ + if (this.renderTime < 0) + this.renderTime = new Date().getTime(); + else { + var newTime = new Date().getTime(); + var t = newTime - this.renderTime; + var framerate = 1000/t; + this.framerates.push(framerate); + while (this.framerates.length > this.numFramerates) + this.framerates.shift(); + this.renderTime = newTime; + } +} \ No newline at end of file