UNPKG

markgojs

Version:

Interactive diagrams, charts, and graphs, such as trees, flowcharts, orgcharts, UML, BPMN, or business diagrams

824 lines (689 loc) 27.8 kB
// San Angeles Observation OpenGL ES version example // Copyright 2004-2005 Jetro Lauha // Web: http://iki.fi/jetro/ // This source is free software; you can redistribute it and/or // modify it under the terms of EITHER: // (1) The GNU Lesser General Public License as published by the Free // Software Foundation; either version 2.1 of the License, or (at // your option) any later version. The text of the GNU Lesser // General Public License is included with this source in the // file LICENSE-LGPL.txt. // (2) The BSD-style license that is included with this source in // the file LICENSE-BSD.txt. // // This source is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files // LICENSE-LGPL.txt and LICENSE-BSD.txt for more details. // WebGL port by Kenneth Waters var shapeParams = [ // m a b n1 n2 n3 m a b n1 n2 n3 res1 res2 scale (org.res1,res2) [10, 1, 2, 90, 1, -45, 8, 1, 1, -1, 1, -0.4 , 20, 30, 2], // 40, 60 [10, 1, 2, 90, 1, -45, 4, 1, 1, 10, 1, -0.4 , 20, 20, 4], // 40, 40 [10, 1, 2, 60, 1, -10, 4, 1, 1, -1, -2, -0.4 , 41, 41, 1], // 82, 82 [ 6, 1, 1, 60, 1, -70, 8, 1, 1, 0.4 , 3, 0.25 , 20, 20, 1], // 40, 40 [ 4, 1, 1, 30, 1, 20, 12, 1, 1, 0.4 , 3, 0.25 , 10, 30, 1], // 20, 60 [ 8, 1, 1, 30, 1, -4, 8, 2, 1, -1, 5, 0.5 , 25, 26, 1], // 60, 60 [13, 1, 1, 30, 1, -4, 13, 1, 1, 1, 5, 1, 30, 30, 6], // 60, 60 [10, 1, 1.1 , -0.5 , 0.1 , 70, 60, 1, 1, -90, 0, -0.25 , 20, 60, 8], // 60, 180 [ 7, 1, 1, 20, -0.3 , -3.5 , 6, 1, 1, -1, 4.5 , 0.5 , 10, 20, 4], // 60, 80 [ 4, 1, 1, 10, 10, 10, 4, 1, 1, 10, 10, 10, 10, 20, 1], // 20, 40 [ 4, 1, 1, 1, 1, 1, 4, 1, 1, 1, 1, 1, 10, 10, 2], // 10, 10 [ 1, 1, 1, 38, -0.25 , 19, 4, 1, 1, 10, 10, 10, 10, 15, 2], // 20, 40 [ 2, 1, 1, 0.7 , 0.3 , 0.2 , 3, 1, 1, 100, 100, 100, 10, 25, 2], // 20, 50 [ 6, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 30, 30, 2], // 60, 60 [ 3, 1, 1, 1, 1, 1, 6, 1, 1, 2, 1, 1, 10, 20, 2], // 20, 40 [ 6, 1, 1, 6, 5.5 , 100, 6, 1, 1, 25, 10, 10, 30, 20, 2], // 60, 40 [ 3, 1, 1, 0.5 , 1.7 , 1.7 , 2, 1, 1, 10, 10, 10, 20, 20, 2], // 40, 40 [ 5, 1, 1, 0.1 , 1.7 , 1.7 , 1, 1, 1, 0.3 , 0.5 , 0.5 , 20, 20, 4], // 40, 40 [ 2, 1, 1, 6, 5.5 , 100, 6, 1, 1, 4, 10, 10, 10, 22, 1], // 40, 40 [ 6, 1, 1, -1, 70, 0.1 , 9, 1, 0.5 , -98, 0.05 , -45, 20, 30, 4], // 60, 91 [ 6, 1, 1, -1, 90, -0.1 , 7, 1, 1, 90, 1.3 , 34, 13, 16, 1] // 32, 60 ]; var camTracks = [ {src:[4500, 2700, 100, 70, -30], dest:[50, 50, -90, -100, 0], dist:20, len:1}, {src:[ -1448, 4294, 25, 363, 0 ], dest:[ -136, 202, 125, -98, 100], dist:0, len:1}, {src:[ 1437, 4930, 200, -275, -20 ], dest:[ 1684, 0, 0, 9, 0], dist:0, len:1}, {src:[ 1800, 3609, 200, 0, 675 ], dest:[ 0, 0, 0, 300, 0], dist:0, len:1}, {src:[ 923, 996, 50, 2336, -80 ], dest:[ 0, -20, -50, 0, 170], dist:0, len:1}, {src:[ -1663, -43, 600, 2170, 0 ], dest:[ 20, 0, -600, 0, 100], dist:0, len:1}, {src:[ 1049, -1420, 175, 2111, -17 ], dest:[ 0, 0, 0, -334, 0], dist:0, len:2}, {src:[ 0, 0, 50, 300, 25 ], dest:[ 0, 0, 0, 300, 0], dist:70, len:2}, {src:[ -473, -953, 3500, -353, -350 ], dest:[ 0, 0, -2800, 0, 0], dist:0, len:2}, {src:[ 191, 1938, 35, 1139, -17 ], dest:[ 1205, -2909, 0, 0, 0], dist:0, len:2}, {src:[ -1449, -2700, 150, 0, 0 ], dest:[ 0, 2000, 0, 0, 0], dist:0, len:2}, {src:[ 5273, 4992, 650, 373, -50 ], dest:[ -4598, -3072, 0, 0, 0], dist:0, len:2}, {src:[ 3223, -3282, 1075, -393, -25 ], dest:[ 1649, -1649, 0, 0, 0], dist:0, len:2} ]; var CAMTRACK_LEN = 5442; var flatVertexSource = [ "attribute vec3 pos;", "attribute vec4 colorIn;", "uniform mat4 mvp;", "varying vec4 color;", "void main() {", " color = colorIn;", " gl_Position = mvp * vec4(pos.xyz, 1.);", "}" ].join("\n"); var flatFragmentSource = [ "precision mediump float;\n", "varying vec4 color;", "void main() {", " gl_FragColor = vec4(color.rgb, 1.0);", "}" ].join("\n"); var litVertexSource = [ "attribute vec3 pos;", "attribute vec3 normal;", "attribute vec4 colorIn;", "", "varying vec4 color;", "", "uniform mat4 mvp;", "uniform mat3 normalMatrix;", "uniform vec4 ambient;", "uniform float shininess;", "uniform vec3 light_0_direction;", "uniform vec4 light_0_diffuse;", "uniform vec4 light_0_specular;", "uniform vec3 light_1_direction;", "uniform vec4 light_1_diffuse;", "uniform vec3 light_2_direction;", "uniform vec4 light_2_diffuse;", "", "vec3 worldNormal;", "", "vec4 SpecularLight(vec3 direction,", " vec4 diffuseColor,", " vec4 specularColor) {", " vec3 lightDir = normalize(direction);", " float diffuse = max(0., dot(worldNormal, lightDir));", " float specular = 0.;", " if (diffuse > 0.) {", " vec3 halfv = normalize(lightDir + vec3(0., 0., 1.));", " specular = pow(max(0., dot(halfv, worldNormal)), shininess);", " }", " return diffuse * diffuseColor * colorIn + specular * specularColor;", "}", "", "vec4 DiffuseLight(vec3 direction, vec4 diffuseColor) {", " vec3 lightDir = normalize(direction);", " float diffuse = max(0., dot(worldNormal, lightDir));", " return diffuse * diffuseColor * colorIn;", "}", "", "void main() {", " worldNormal = normalize(normalMatrix * normal);", "", " gl_Position = mvp * vec4(pos, 1.);", "", " color = ambient * colorIn;", " color += SpecularLight(light_0_direction, light_0_diffuse,", " light_0_specular);", " color += DiffuseLight(light_1_direction, light_1_diffuse);", " color += DiffuseLight(light_2_direction, light_2_diffuse);", "}", ].join("\n"); var fadeVertexSource = [ "precision mediump float;\n", "attribute vec2 pos;", "", "varying vec4 color;", "", "uniform float minFade;", "", "void main() {", " color = vec4(minFade, minFade, minFade, 1.);", " gl_Position = vec4(pos, 0., 1.);", "}", ].join("\n"); // globals var ground = null; var fadeVBO = null; var shapes = null; var modelview = new Matrix4x4(); var projection = new Matrix4x4(); var mvp = new Matrix4x4(); var normalMatrix = Array(9); var currentCamTrackStartTick = 0; var nextCamTrackStartTick = 0; var sTick = 0; var currentCamTrack = 0; var litShader = null; var flatShader = null; var fadeShader = null; var random = { randomSeed: 0, seed: function(seed) { this.randomSeed = seed; }, uInt: function() { this.randomSeed = (this.randomSeed * 0x343fd + 0x269ec3) & 0xffffffff; return (this.randomSeed >> 16) & 0xffff; } }; function glslNameToJs(name) { return name.replace(/_(.)/g, function(_, p1) { return p1.toUpperCase(); }); } function Shader(vertex, fragment) { this.program = gl.createProgram(); var vs = gl.createShader(gl.VERTEX_SHADER); gl.shaderSource(vs, vertex); gl.compileShader(vs); if (!gl.getShaderParameter(vs, gl.COMPILE_STATUS)) { var infoLog = gl.getShaderInfoLog(vs); log("Error compiling vertex shader:" + infoLog); } gl.attachShader(this.program, vs); gl.deleteShader(vs); var fs = gl.createShader(gl.FRAGMENT_SHADER); gl.shaderSource(fs, fragment); gl.compileShader(fs); if (!gl.getShaderParameter(fs, gl.COMPILE_STATUS)) { var infoLog = gl.getShaderInfoLog(fs); log("Error compiling fragment shader:" + infoLog); } gl.attachShader(this.program, fs); gl.deleteShader(fs); gl.linkProgram(this.program); gl.useProgram(this.program); // log(gl.getProgramInfoLog(this.program)); // find uniforms and attributes var re = /(uniform|attribute)\s+\S+\s+(\S+)\s*;/g; var match = null; while ((match = re.exec(vertex + '\n' + fragment)) != null) { var glslName = match[2]; var jsName = glslNameToJs(glslName); var loc = -1; if (match[1] == "uniform") { this[jsName + "Loc"] = this.getUniform(glslName); } else if (match[1] == "attribute") { this[jsName + "Loc"] = this.getAttribute(glslName); } if (loc >= 0) { this[jsName + "Loc"] = loc; } } } Shader.prototype.bind = function() { gl.useProgram(this.program); if (this.mvpLoc != undefined) { // TODO(kwaters): hack mvp.loadIdentity(); mvp.multiply(modelview); mvp.multiply(projection); gl.uniformMatrix4fv(this.mvpLoc, gl.FALSE, mvp.elements); } if (this.normalMatrixLoc !== undefined) { gl.uniformMatrix3fv(this.normalMatrixLoc, gl.FALSE, computeNormalMatrix(modelview, normalMatrix)); } }; Shader.prototype.getAttribute = function(name) { return gl.getAttribLocation(this.program, name); }; Shader.prototype.getUniform = function(name) { return gl.getUniformLocation(this.program, name); }; function GlObject(shader, vertices, colors, normals) { this.shader = shader; this.count = (vertices.length / 3) | 0; this.vbo = gl.createBuffer(); var vertexArray = new Float32Array(vertices); var colorArray = new Uint8Array(colors); this.vertexOffset = 0; this.colorOffset = vertexArray.byteLength; this.normalOffset = this.colorOffset + colorArray.byteLength; var sizeInBytes = this.normalOffset; var normalArray = null; if (normals != undefined) { normalArray = new Float32Array(normals); sizeInBytes += normalArray.byteLength; } gl.bindBuffer(gl.ARRAY_BUFFER, this.vbo); gl.bufferData(gl.ARRAY_BUFFER, sizeInBytes, gl.STATIC_DRAW); gl.bufferSubData(gl.ARRAY_BUFFER, this.vertexOffset, vertexArray); gl.bufferSubData(gl.ARRAY_BUFFER, this.colorOffset, colorArray); if (normals != undefined) { gl.bufferSubData(gl.ARRAY_BUFFER, this.normalOffset, normalArray); } this.vao = glvao.createVertexArrayOES(); glvao.bindVertexArrayOES(this.vao); gl.bindBuffer(gl.ARRAY_BUFFER, this.vbo); gl.vertexAttribPointer(this.shader.posLoc, 3, gl.FLOAT, false, 0, this.vertexOffset); gl.enableVertexAttribArray(this.shader.posLoc); gl.vertexAttribPointer(this.shader.colorInLoc, 4, gl.UNSIGNED_BYTE, true, 0, this.colorOffset); gl.enableVertexAttribArray(this.shader.colorInLoc); if (this.shader.normalLoc !== undefined) { gl.vertexAttribPointer(this.shader.normalLoc, 3, gl.FLOAT, false, 0, this.normalOffset); gl.enableVertexAttribArray(this.shader.normalLoc); } glvao.bindVertexArrayOES(null); } GlObject.prototype.free = function() { glvao.deleteVertexArrayOES(this.vao); this.vao = null; }; GlObject.prototype.draw = function() { this.shader.bind(); glvao.bindVertexArrayOES(this.vao); gl.drawArrays(gl.TRIANGLES, 0, this.count); }; function createGroundPlane(shader) { var scale = 4; var xBegin = -15, xEnd = 15; var yBegin = -15, yEnd = 15; var triangleCount = (yEnd - yBegin) * (xEnd - xBegin) * 2; var colors = []; var vertices = []; for (var y = yBegin; y < yEnd; ++y) { for (var x = xBegin; x < xEnd; ++x) { var color = (random.uInt() & 0x4f) + 81; for (var i = 0; i < 6; ++i) { colors.push(color, color, color, 0); } for (var a = 0; a < 6; ++a) { var xm = x + ((0x1c >> a) & 1); var ym = y + ((0x31 >> a) & 1); var m = Math.cos(xm * 2) * Math.sin(ym * 4) * 0.75; vertices.push(xm * scale + m, ym * scale + m, 0.); } } } return new GlObject(shader, vertices, colors); } function clamp(x) { return x < 0 ? 0 : (x > 255 ? 255 : (x | 0)); } function vector3Sub(dest, v1, v2) { dest[0] = v1[0] - v2[0]; dest[1] = v1[1] - v2[1]; dest[2] = v1[2] - v2[2]; } function superShapeMap(point, r1, r2, t, p) { point[0] = Math.cos(t) * Math.cos(p) / r1 / r2; point[1] = Math.sin(t) * Math.cos(p) / r1 / r2; point[2] = Math.sin(p) / r2; } function ssFunc(t, p) { return Math.pow(Math.pow(Math.abs(Math.cos(p[0] * t / 4)) / p[1], p[4]) + Math.pow(Math.abs(Math.sin(p[0] * t / 4)) / p[2], p[5]), 1. / p[3]); } function createSuperShape(shader, params) { var resol1 = params[params.length - 3]; var resol2 = params[params.length - 2]; // latitude 0 to pi/2 for no mirrored bottom // (latitudeBegin==0 for -pi/2 to pi/2 originally) var latitudeBegin = (resol2 / 4) | 0; var latitudeEnd = (resol2 / 2) | 0; // non-inclusive var longitudeCount = resol1; var vertices = []; var colors = []; var normals = []; var baseColor = Array(3); for (var i = 0; i < 3; ++i) { baseColor[i] = (random.uInt() % 155 + 100) / 255.; } var currentVertex = 0; for (var longitude = 0; longitude < longitudeCount; ++longitude) { for (var latitude = latitudeBegin; latitude < latitudeEnd; ++latitude) { var t1 = -Math.PI + longitude * 2 * Math.PI / resol1; var t2 = -Math.PI + (longitude + 1) * 2 * Math.PI / resol1; var p1 = -Math.PI / 2 + latitude * 2 * Math.PI / resol2; var p2 = -Math.PI / 2 + (latitude + 1) * 2 * Math.PI / resol2; var r0 = ssFunc(t1, params); var r1 = ssFunc(p1, params.slice(6, 12)); var r2 = ssFunc(t2, params); var r3 = ssFunc(p2, params.slice(6, 12)); if (r0 != 0 && r1 != 0 && r2 != 0 && r3 != 0) { var pa = Array(3), pb = Array(3), pc = Array(3), pd = Array(3); superShapeMap(pa, r0, r1, t1, p1); superShapeMap(pb, r2, r1, t2, p1); superShapeMap(pc, r2, r3, t2, p2); superShapeMap(pd, r0, r3, t1, p2); // kludge to set lower edge of the object to fixed level if (latitude == latitudeBegin + 1) { pa[2] = pb[2] = 0; } var v1 = Array(3), v2 = Array(3), n = Array(3); vector3Sub(v1, pb, pa); vector3Sub(v2, pd, pa); // Calculate normal with cross product. n[0] = v1[1] * v2[2] - v1[2] * v2[1]; n[1] = v1[2] * v2[0] - v1[0] * v2[2]; n[2] = v1[0] * v2[1] - v1[1] * v2[0]; /* Pre-normalization of the normals is disabled here because * they will be normalized anyway later due to automatic * normalization (NORMALIZE). It is enabled because the * objects are scaled with scale. */ // Note we have to normalize by hand in the shader // var ca = pa[2] + 0.5; for (var i = 0; i < 6; ++i) { normals.push(n[0], n[1], n[2]); } for (var i = 0; i < 6; ++i) { colors.push(clamp(ca * baseColor[0] * 255)); colors.push(clamp(ca * baseColor[1] * 255)); colors.push(clamp(ca * baseColor[2] * 255)); colors.push(0); } vertices.push(pa[0], pa[1], pa[2]); vertices.push(pb[0], pb[1], pb[2]); vertices.push(pd[0], pd[1], pd[2]); vertices.push(pb[0], pb[1], pb[2]); vertices.push(pc[0], pc[1], pc[2]); vertices.push(pd[0], pd[1], pd[2]); } } } return new GlObject(shader, vertices, colors, normals); } function prepareFrame(width, height) { // Note: the viewport is automatically set up to cover the entire Canvas. gl.clearColor(.1, .2, .3, 1.); gl.clear(gl.DEPTH_BUFFER_BIT | gl.COLOR_BUFFER_BIT); projection.loadIdentity(); projection.perspective(45, width / height, .5, 100); modelview.loadIdentity(); } Matrix4x4.prototype.transform3 = function(v) { var e = this.elements; return [e[0] * v[0] + e[4] * v[1] + e[8] * v[2], e[1] * v[0] + e[5] * v[1] + e[9] * v[2], e[2] * v[0] + e[6] * v[1] + e[10] * v[2]]; }; function configureLightAndMaterial() { var light0Direction = modelview.transform3([-4., 1., 1.]); var light1Direction = modelview.transform3([1., -2., -1.]); var light2Direction = modelview.transform3([-1., 0., -4.]); litShader.bind(); gl.uniform3f(litShader.light0DirectionLoc, light0Direction[0], light0Direction[1], light0Direction[2]); gl.uniform3f(litShader.light1DirectionLoc, light1Direction[0], light1Direction[1], light1Direction[2]); gl.uniform3f(litShader.light2DirectionLoc, light2Direction[0], light2Direction[1], light2Direction[2]); } function drawModels(zScale) { var translationScale = 9; modelview.scale(1, 1, zScale); random.seed(9); for (var y = -5; y <= 5; ++y) { for (var x = -5; x <= 5; ++x) { var curShape = random.uInt() % shapeParams.length; var buildingScale = shapeParams[curShape][shapeParams[0].length - 1]; modelview.push(); modelview.translate(x * translationScale, y * translationScale, 0); var rv = random.uInt() % 360; // TODO(kwaters): bug in rotate // modelview.rotate(random.uInt() % 360, 0, 0, 1); modelview.rotate(-rv, 0, 0, 1); modelview.scale(buildingScale, buildingScale, buildingScale); shapes[curShape].draw(); modelview.pop(); } } var ship = shapes[shapes.length - 1]; for (var x = -2; x <= 2; ++x) { var shipScale100 = translationScale * 500; var offs100 = x * shipScale100 + (sTick % shipScale100); var offs = 0.01 * offs100; modelview.push(); modelview.translate(offs, -4., 2.); ship.draw(); modelview.pop(); modelview.push(); modelview.translate(-4., offs, 4.); modelview.rotate(-90., 0., 0., 1.); ship.draw(); modelview.pop(); } } function computeNormalMatrix(matrix, normal) { var e = matrix.elements; var det = (e[0 * 4 + 0] * (e[1 * 4 + 1] * e[2 * 4 + 2] - e[2 * 4 + 1] * e[1 * 4 + 2]) - e[0 * 4 + 1] * (e[1 * 4 + 0] * e[2 * 4 + 2] - e[1 * 4 + 2] * e[2 * 4 + 0]) + e[0 * 4 + 2] * (e[1 * 4 + 0] * e[2 * 4 + 1] - e[1 * 4 + 1] * e[2 * 4 + 0])); var invDet = 1. / det; normal[0 * 3 + 0] = invDet * (e[1 * 4 + 1] * e[2 * 4 + 2] - e[2 * 4 + 1] * e[1 * 4 + 2]); normal[1 * 3 + 0] = invDet * -(e[0 * 4 + 1] * e[2 * 4 + 2] - e[0 * 4 + 2] * e[2 * 4 + 1]); normal[2 * 3 + 0] = invDet * (e[0 * 4 + 1] * e[1 * 4 + 2] - e[0 * 4 + 2] * e[1 * 4 + 1]); normal[0 * 3 + 1] = invDet * -(e[1 * 4 + 0] * e[2 * 4 + 2] - e[1 * 4 + 2] * e[2 * 4 + 0]); normal[1 * 3 + 1] = invDet * (e[0 * 4 + 0] * e[2 * 4 + 2] - e[0 * 4 + 2] * e[2 * 4 + 0]); normal[2 * 3 + 1] = invDet * -(e[0 * 4 + 0] * e[1 * 4 + 2] - e[1 * 4 + 0] * e[0 * 4 + 2]); normal[0 * 3 + 2] = invDet * (e[1 * 4 + 0] * e[2 * 4 + 1] - e[2 * 4 + 0] * e[1 * 4 + 1]); normal[1 * 3 + 2] = invDet * -(e[0 * 4 + 0] * e[2 * 4 + 1] - e[2 * 4 + 0] * e[0 * 4 + 1]); normal[2 * 3 + 2] = invDet * (e[0 * 4 + 0] * e[1 * 4 + 1] - e[1 * 4 + 0] * e[0 * 4 + 1]); return normal; } /* Following gluLookAt implementation is adapted from the * Mesa 3D Graphics library. http://www.mesa3d.org */ Matrix4x4.prototype.lookAt = function(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ) { // log("lookAt"); // log([eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ]); /* Z vector */ var z = Array(3); z[0] = eyeX - centerX; z[1] = eyeY - centerY; z[2] = eyeZ - centerZ; var mag = Math.sqrt(z[0] * z[0] + z[1] * z[1] + z[2] * z[2]); if (mag) { z[0] /= mag; z[1] /= mag; z[2] /= mag; } /* Y vector */ var y = Array(3); y[0] = upX; y[1] = upY; y[2] = upZ; /* X vector = Y cross Z */ var x = Array(3); x[0] = y[1] * z[2] - y[2] * z[1]; x[1] = -y[0] * z[2] + y[2] * z[0]; x[2] = y[0] * z[1] - y[1] * z[0]; /* Recompute Y = Z cross X */ y[0] = z[1] * x[2] - z[2] * x[1]; y[1] = -z[0] * x[2] + z[2] * x[0]; y[2] = z[0] * x[1] - z[1] * x[0]; /* mpichler, 19950515 */ /* cross product gives area of parallelogram, which is < 1.0 for * non-perpendicular unit-length vectors; so normalize x, y here */ mag = Math.sqrt(x[0] * x[0] + x[1] * x[1] + x[2] * x[2]); if (mag) { x[0] /= mag; x[1] /= mag; x[2] /= mag; } mag = Math.sqrt(y[0] * y[0] + y[1] * y[1] + y[2] * y[2]); if (mag) { y[0] /= mag; y[1] /= mag; y[2] /= mag; } var lookAt = new Matrix4x4(); lookAt.elements[0 * 4 + 0] = x[0]; lookAt.elements[1 * 4 + 0] = x[1]; lookAt.elements[2 * 4 + 0] = x[2]; lookAt.elements[3 * 4 + 0] = 0.; lookAt.elements[0 * 4 + 1] = y[0]; lookAt.elements[1 * 4 + 1] = y[1]; lookAt.elements[2 * 4 + 1] = y[2]; lookAt.elements[3 * 4 + 1] = 0.; lookAt.elements[0 * 4 + 2] = z[0]; lookAt.elements[1 * 4 + 2] = z[1]; lookAt.elements[2 * 4 + 2] = z[2]; lookAt.elements[3 * 4 + 2] = 0.; lookAt.elements[0 * 4 + 3] = 0.; lookAt.elements[1 * 4 + 3] = 0.; lookAt.elements[2 * 4 + 3] = 0.; lookAt.elements[3 * 4 + 3] = 1.; // log(lookAt.elements); lookAt = lookAt.multiply(this); this.elements = lookAt.elements; /* this.multiply(lookAt); */ this.translate(-eyeX, -eyeY, -eyeZ); // log(this.elements); return this; }; Matrix4x4.prototype.push = function() { this.stack = this.stack || []; this.stack.push(this.elements.slice()); }; Matrix4x4.prototype.pop = function() { this.elements = this.stack.pop(); }; function camTrack() { // log('camTrack'); // log(sTick); nextCamTrackStartTick = currentCamTrackStartTick + camTracks[currentCamTrack].len * CAMTRACK_LEN; while (nextCamTrackStartTick <= sTick) { ++currentCamTrack; if (currentCamTrack >= camTracks.length) { currentCamTrack = 0; } currentCamTrackStartTick = nextCamTrackStartTick; nextCamTrackStartTick = currentCamTrackStartTick + camTracks[currentCamTrack].len * CAMTRACK_LEN; // log(currentCamTrack); } var cam = camTracks[currentCamTrack]; var currentCamTick = sTick - currentCamTrackStartTick; var trackPos = currentCamTick / (CAMTRACK_LEN * cam.len); var lerp = Array(5); for (var a = 0; a < 5; ++a) { lerp[a] = 0.01 * (cam.src[a] + cam.dest[a] * trackPos); } var cX, cY, cZ, eX, eY, eZ; if (cam.dist) { var dist = cam.dist * 0.1; cX = lerp[0]; cY = lerp[1]; cZ = lerp[2]; eX = cX - Math.cos(lerp[3]) * dist; eY = cY - Math.sin(lerp[3]) * dist; eZ = cZ - lerp[4]; } else { eX = lerp[0]; eY = lerp[1]; eZ = lerp[2]; cX = eX + Math.cos(lerp[3]); cY = eY + Math.sin(lerp[3]); cZ = eZ + lerp[4]; } modelview.lookAt(eX, eY, eZ, cX, cY, cZ, 0, 0, 1); } function drawGroundPlane() { gl.disable(gl.CULL_FACE); gl.disable(gl.DEPTH_TEST); gl.enable(gl.BLEND); gl.blendFunc(gl.ZERO, gl.SRC_COLOR); ground.draw(); gl.disable(gl.BLEND); gl.enable(gl.DEPTH_TEST); } function createFadeQuad() { var vertices = new Float32Array([ -1., -1., 1., -1., -1., 1., 1., -1., 1., 1., -1., 1.]); fadeVBO = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, fadeVBO); gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW); } function drawFadeQuad() { if (fadeVBO == null) { createFadeQuad(); } var beginFade = sTick - currentCamTrackStartTick; var endFade = nextCamTrackStartTick - sTick; var minFade = beginFade < endFade ? beginFade : endFade; if (minFade < 1024) { gl.disable(gl.DEPTH_TEST); gl.enable(gl.BLEND); gl.blendFunc(gl.ZERO, gl.SRC_COLOR); fadeShader.bind(); gl.uniform1f(fadeShader.minFadeLoc, minFade / 1024.); gl.bindBuffer(gl.ARRAY_BUFFER, fadeVBO); var vertexOffset = 0; gl.vertexAttribPointer(fadeShader.posLoc, 2, gl.FLOAT, false, 0, vertexOffset); gl.enableVertexAttribArray(fadeShader.posLoc); gl.drawArrays(gl.TRIANGLES, 0, 6); gl.disableVertexAttribArray(fadeShader.posLoc); gl.disable(gl.BLEND); gl.enable(gl.DEPTH_TEST); } } function appInit() { currentCamTrackStartTick = 0; nextCamTrackStartTick = 0; sTick = 0; currentCamTrack = 0; fadeVBO = null; gl.enable(gl.DEPTH_TEST); gl.disable(gl.CULL_FACE); random.seed(15); flatShader = new Shader(flatVertexSource, flatFragmentSource); litShader = new Shader(litVertexSource, flatFragmentSource); fadeShader = new Shader(fadeVertexSource, flatFragmentSource); // bind non-changing lighting parameters litShader.bind(); gl.uniform4f(litShader.ambientLoc, .2, .2, .2, 1.); gl.uniform4f(litShader.light0DiffuseLoc, 1., .4, 0, 1.); gl.uniform4f(litShader.light1DiffuseLoc, .07, .14, .35, 1.); gl.uniform4f(litShader.light2DiffuseLoc, .07, .17, .14, 1.); gl.uniform4f(litShader.light0SpecularLoc, 1., 1., 1., 1.); gl.uniform1f(litShader.shininessLoc, 60.); shapes = Array(shapeParams.length); for (var i = 0; i < shapes.length; ++i) { shapes[i] = createSuperShape(litShader, shapeParams[i]) } ground = createGroundPlane(flatShader); } function appRender(tick, width, height) { // Actual tick value is "blurred" a little bit. sTick = (sTick + tick) >> 1; prepareFrame(width, height); camTrack(); configureLightAndMaterial(); modelview.push(); drawModels(-1); modelview.pop(); drawGroundPlane(); drawModels(1); drawFadeQuad(); }