main.js | |
---|---|
The internal | var gl;
var GL = { |
Initialization
| create: function(options) {
options = options || {};
var canvas = document.createElement('canvas');
canvas.width = 800;
canvas.height = 600;
if (!('alpha' in options)) options.alpha = false;
try { gl = canvas.getContext('webgl', options); } catch (e) {}
try { gl = gl || canvas.getContext('experimental-webgl', options); } catch (e) {}
if (!gl) throw new Error('WebGL not supported');
gl.HALF_FLOAT_OES = 0x8D61;
addMatrixStack();
addImmediateMode();
addEventListeners();
addOtherMethods();
return gl;
}, |
| keys: {}, |
Export all external classes. | Matrix: Matrix,
Indexer: Indexer,
Buffer: Buffer,
Mesh: Mesh,
HitTest: HitTest,
Raytracer: Raytracer,
Shader: Shader,
Texture: Texture,
Vector: Vector
}; |
Matrix stackImplement the OpenGL modelview and projection matrix stacks, along with some other useful GLU matrix functions. | function addMatrixStack() {
gl.MODELVIEW = ENUM | 1;
gl.PROJECTION = ENUM | 2;
var tempMatrix = new Matrix();
var resultMatrix = new Matrix();
gl.modelviewMatrix = new Matrix();
gl.projectionMatrix = new Matrix();
var modelviewStack = [];
var projectionStack = [];
var matrix, stack;
gl.matrixMode = function(mode) {
switch (mode) {
case gl.MODELVIEW:
matrix = 'modelviewMatrix';
stack = modelviewStack;
break;
case gl.PROJECTION:
matrix = 'projectionMatrix';
stack = projectionStack;
break;
default:
throw new Error('invalid matrix mode ' + mode);
}
};
gl.loadIdentity = function() {
Matrix.identity(gl[matrix]);
};
gl.loadMatrix = function(m) {
var from = m.m, to = gl[matrix].m;
for (var i = 0; i < 16; i++) {
to[i] = from[i];
}
};
gl.multMatrix = function(m) {
gl.loadMatrix(Matrix.multiply(gl[matrix], m, resultMatrix));
};
gl.perspective = function(fov, aspect, near, far) {
gl.multMatrix(Matrix.perspective(fov, aspect, near, far, tempMatrix));
};
gl.frustum = function(l, r, b, t, n, f) {
gl.multMatrix(Matrix.frustum(l, r, b, t, n, f, tempMatrix));
};
gl.ortho = function(l, r, b, t, n, f) {
gl.multMatrix(Matrix.ortho(l, r, b, t, n, f, tempMatrix));
};
gl.scale = function(x, y, z) {
gl.multMatrix(Matrix.scale(x, y, z, tempMatrix));
};
gl.translate = function(x, y, z) {
gl.multMatrix(Matrix.translate(x, y, z, tempMatrix));
};
gl.rotate = function(a, x, y, z) {
gl.multMatrix(Matrix.rotate(a, x, y, z, tempMatrix));
};
gl.lookAt = function(ex, ey, ez, cx, cy, cz, ux, uy, uz) {
gl.multMatrix(Matrix.lookAt(ex, ey, ez, cx, cy, cz, ux, uy, uz, tempMatrix));
};
gl.pushMatrix = function() {
stack.push(Array.prototype.slice.call(gl[matrix].m));
};
gl.popMatrix = function() {
var m = stack.pop();
gl[matrix].m = hasFloat32Array ? new Float32Array(m) : m;
};
gl.project = function(objX, objY, objZ, modelview, projection, viewport) {
modelview = modelview || gl.modelviewMatrix;
projection = projection || gl.projectionMatrix;
viewport = viewport || gl.getParameter(gl.VIEWPORT);
var point = projection.transformPoint(modelview.transformPoint(new Vector(objX, objY, objZ)));
return new Vector(
viewport[0] + viewport[2] * (point.x * 0.5 + 0.5),
viewport[1] + viewport[3] * (point.y * 0.5 + 0.5),
point.z * 0.5 + 0.5
);
};
gl.unProject = function(winX, winY, winZ, modelview, projection, viewport) {
modelview = modelview || gl.modelviewMatrix;
projection = projection || gl.projectionMatrix;
viewport = viewport || gl.getParameter(gl.VIEWPORT);
var point = new Vector(
(winX - viewport[0]) / viewport[2] * 2 - 1,
(winY - viewport[1]) / viewport[3] * 2 - 1,
winZ * 2 - 1
);
return Matrix.inverse(Matrix.multiply(projection, modelview, tempMatrix), resultMatrix).transformPoint(point);
};
gl.matrixMode(gl.MODELVIEW);
} |
Immediate modeProvide an implementation of OpenGL's deprecated immediate mode. This is
depricated for a reason: constantly re-specifying the geometry is a bad
idea for performance. You should use a | function addImmediateMode() {
var immediateMode = {
mesh: new Mesh({ coords: true, colors: true, triangles: false }),
mode: -1,
coord: [0, 0, 0, 0],
color: [1, 1, 1, 1],
pointSize: 1,
shader: new Shader('\
uniform float pointSize;\
varying vec4 color;\
varying vec4 coord;\
void main() {\
color = gl_Color;\
coord = gl_TexCoord;\
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\
gl_PointSize = pointSize;\
}\
', '\
uniform sampler2D texture;\
uniform float pointSize;\
uniform bool useTexture;\
varying vec4 color;\
varying vec4 coord;\
void main() {\
gl_FragColor = color;\
if (useTexture) gl_FragColor *= texture2D(texture, coord.xy);\
}\
')
};
gl.pointSize = function(pointSize) {
immediateMode.shader.uniforms({ pointSize: pointSize });
};
gl.begin = function(mode) {
if (immediateMode.mode != -1) throw new Error('mismatched gl.begin() and gl.end() calls');
immediateMode.mode = mode;
immediateMode.mesh.colors = [];
immediateMode.mesh.coords = [];
immediateMode.mesh.vertices = [];
};
gl.color = function(r, g, b, a) {
immediateMode.color = (arguments.length == 1) ? r.toArray().concat(1) : [r, g, b, a || 1];
};
gl.texCoord = function(s, t) {
immediateMode.coord = (arguments.length == 1) ? s.toArray(2) : [s, t];
};
gl.vertex = function(x, y, z) {
immediateMode.mesh.colors.push(immediateMode.color);
immediateMode.mesh.coords.push(immediateMode.coord);
immediateMode.mesh.vertices.push(arguments.length == 1 ? x.toArray() : [x, y, z]);
};
gl.end = function() {
if (immediateMode.mode == -1) throw new Error('mismatched gl.begin() and gl.end() calls');
immediateMode.mesh.compile();
immediateMode.shader.uniforms({
useTexture: !!gl.getParameter(gl.TEXTURE_BINDING_2D)
}).draw(immediateMode.mesh, immediateMode.mode);
immediateMode.mode = -1;
};
} |
Improved mouse eventsThis adds event listeners on the | function addEventListeners() {
var context = gl, oldX = 0, oldY = 0, buttons = {}, hasOld = false;
var has = Object.prototype.hasOwnProperty;
function isDragging() {
for (var b in buttons) {
if (has.call(buttons, b) && buttons[b]) return true;
}
return false;
}
function augment(original) { |
Make a copy of original, a native | var e = {};
for (var name in original) {
if (typeof original[name] == 'function') {
e[name] = (function(callback) {
return function() {
callback.apply(original, arguments);
};
})(original[name]);
} else {
e[name] = original[name];
}
}
e.original = original;
e.x = e.pageX;
e.y = e.pageY;
for (var obj = gl.canvas; obj; obj = obj.offsetParent) {
e.x -= obj.offsetLeft;
e.y -= obj.offsetTop;
}
if (hasOld) {
e.deltaX = e.x - oldX;
e.deltaY = e.y - oldY;
} else {
e.deltaX = 0;
e.deltaY = 0;
hasOld = true;
}
oldX = e.x;
oldY = e.y;
e.dragging = isDragging();
e.preventDefault = function() {
e.original.preventDefault();
};
e.stopPropagation = function() {
e.original.stopPropagation();
};
return e;
}
function mousedown(e) {
gl = context;
if (!isDragging()) { |
Expand the event handlers to the document to handle dragging off canvas. | on(document, 'mousemove', mousemove);
on(document, 'mouseup', mouseup);
off(gl.canvas, 'mousemove', mousemove);
off(gl.canvas, 'mouseup', mouseup);
}
buttons[e.which] = true;
e = augment(e);
if (gl.onmousedown) gl.onmousedown(e);
e.preventDefault();
}
function mousemove(e) {
gl = context;
e = augment(e);
if (gl.onmousemove) gl.onmousemove(e);
e.preventDefault();
}
function mouseup(e) {
gl = context;
buttons[e.which] = false;
if (!isDragging()) { |
Shrink the event handlers back to the canvas when dragging ends. | off(document, 'mousemove', mousemove);
off(document, 'mouseup', mouseup);
on(gl.canvas, 'mousemove', mousemove);
on(gl.canvas, 'mouseup', mouseup);
}
e = augment(e);
if (gl.onmouseup) gl.onmouseup(e);
e.preventDefault();
}
function reset() {
hasOld = false;
}
function resetAll() {
buttons = {};
hasOld = false;
}
on(gl.canvas, 'mousedown', mousedown);
on(gl.canvas, 'mousemove', mousemove);
on(gl.canvas, 'mouseup', mouseup);
on(gl.canvas, 'mouseover', reset);
on(gl.canvas, 'mouseout', reset);
on(document, 'contextmenu', resetAll);
} |
Automatic keyboard stateThe current keyboard state is stored in | function mapKeyCode(code) {
var named = {
8: 'BACKSPACE',
9: 'TAB',
13: 'ENTER',
16: 'SHIFT',
27: 'ESCAPE',
32: 'SPACE',
37: 'LEFT',
38: 'UP',
39: 'RIGHT',
40: 'DOWN'
};
return named[code] || (code >= 65 && code <= 90 ? String.fromCharCode(code) : null);
}
function on(element, name, callback) {
element.addEventListener(name, callback);
}
function off(element, name, callback) {
element.removeEventListener(name, callback);
}
on(document, 'keydown', function(e) {
if (!e.altKey && !e.ctrlKey && !e.metaKey) {
var key = mapKeyCode(e.keyCode);
if (key) GL.keys[key] = true;
GL.keys[e.keyCode] = true;
}
});
on(document, 'keyup', function(e) {
if (!e.altKey && !e.ctrlKey && !e.metaKey) {
var key = mapKeyCode(e.keyCode);
if (key) GL.keys[key] = false;
GL.keys[e.keyCode] = false;
}
});
function addOtherMethods() { |
Multiple contextsWhen using multiple contexts in one web page, | (function(context) {
gl.makeCurrent = function() {
gl = context;
};
})(gl); |
AnimationCall | gl.animate = function() {
var post =
window.requestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
function(callback) { setTimeout(callback, 1000 / 60); };
var time = new Date().getTime();
var context = gl;
function update() {
gl = context;
var now = new Date().getTime();
if (gl.onupdate) gl.onupdate((now - time) / 1000);
if (gl.ondraw) gl.ondraw();
post(update);
time = now;
}
update();
}; |
FullscreenProvide an easy way to get a fullscreen app running, including an automatic 3D perspective projection matrix by default. This should be called once. Just fullscreen, no automatic camera:
Adjusting field of view, near plane distance, and far plane distance:
Adding padding from the edge of the window: | gl.fullscreen = function(options) {
options = options || {};
var top = options.paddingTop || 0;
var left = options.paddingLeft || 0;
var right = options.paddingRight || 0;
var bottom = options.paddingBottom || 0;
if (!document.body) {
throw new Error('document.body doesn\'t exist yet (call gl.fullscreen() from ' +
'window.onload() or from inside the <body> tag)');
}
document.body.appendChild(gl.canvas);
document.body.style.overflow = 'hidden';
gl.canvas.style.position = 'absolute';
gl.canvas.style.left = left + 'px';
gl.canvas.style.top = top + 'px';
function resize() {
gl.canvas.width = window.innerWidth - left - right;
gl.canvas.height = window.innerHeight - top - bottom;
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
if (options.camera || !('camera' in options)) {
gl.matrixMode(gl.PROJECTION);
gl.loadIdentity();
gl.perspective(options.fov || 45, gl.canvas.width / gl.canvas.height,
options.near || 0.1, options.far || 1000);
gl.matrixMode(gl.MODELVIEW);
}
if (gl.ondraw) gl.ondraw();
}
on(window, 'resize', resize);
resize();
};
} |
A value to bitwise-or with new enums to make them distinguishable from the standard WebGL enums. | var ENUM = 0x12340000;
|