markgojs
Version:
Interactive diagrams, charts, and graphs, such as trees, flowcharts, orgcharts, UML, BPMN, or business diagrams
824 lines (689 loc) • 27.8 kB
JavaScript
// 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();
}