diff webGL/dandy/resources/J3DI.js~ @ 26:933062d8e917

update library J3DI.js J3DMath.js
author NOBUYASU Oshiro
date Tue, 09 Nov 2010 00:03:42 +0900
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/webGL/dandy/resources/J3DI.js~	Tue Nov 09 00:03:42 2010 +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