Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added GL.Shader.fromURL(vsURL, fsURL) and GL.create({canvas: myCanvas}) #10

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 112 additions & 23 deletions src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,24 @@ var gl;
var GL = {
// ### Initialization
//
// `GL.create()` creates a new WebGL context and augments it with more
// methods. The alpha channel is disabled by default because it usually causes
// unintended transparencies in the canvas.
// `GL.create()` creates a new WebGL context and augments it with
// more methods. Uses the HTML canvas given in 'options' or creates
// a new one if necessary. The alpha channel is disabled by default
// because it usually causes unintended transparencies in the
// canvas.
create: function(options) {
options = options || {};
var canvas = document.createElement('canvas');
canvas.width = 800;
canvas.height = 600;
var canvas = options.canvas;
if (!canvas) {
canvas = document.createElement('canvas');
canvas.width = options.width || 800;
canvas.height = options.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 'WebGL not supported';
gl.viewport(0, 0, canvas.width, canvas.height);
addMatrixStack();
addImmediateMode();
addEventListeners();
Expand Down Expand Up @@ -210,6 +216,7 @@ function addImmediateMode() {
// augmented event object. The event object also has the properties `x`, `y`,
// `deltaX`, `deltaY`, and `dragging`.
function addEventListeners() {

var context = gl, oldX = 0, oldY = 0, buttons = {}, hasOld = false;
var has = Object.prototype.hasOwnProperty;
function isDragging() {
Expand All @@ -218,12 +225,14 @@ function addEventListeners() {
}
return false;
}
function augment(original) {
// Make a copy of original, a native `MouseEvent`, so we can overwrite
// WebKit's non-standard read-only `x` and `y` properties (which are just
// duplicates of `pageX` and `pageY`). We can't just use
// `Object.create(original)` because some `MouseEvent` functions must be
// called in the context of the original event object.

// Make a copy of original, a native `MouseEvent`, so we can overwrite
// WebKit's non-standard read-only `x` and `y` properties (which are just
// duplicates of `pageX` and `pageY`). We can't just use
// `Object.create(original)` because some `MouseEvent` functions must be
// called in the context of the original event object.
//
function duplicateEvent(original) {
var e = {};
for (var name in original) {
if (typeof original[name] == 'function') {
Expand All @@ -237,12 +246,22 @@ function addEventListeners() {
}
}
e.original = original;
e.x = e.pageX;
e.y = e.pageY;
e.preventDefault = function() {
e.original.preventDefault();
};
e.stopPropagation = function() {
e.original.stopPropagation();
};
return e;
}

function addRelativeCoords(e) {
for (var obj = gl.canvas; obj; obj = obj.offsetParent) {
e.x -= obj.offsetLeft;
e.y -= obj.offsetTop;
}
e.x = Math.round(e.x * gl.canvas.width / gl.canvas.offsetWidth);
e.y = Math.round(e.y * gl.canvas.height / gl.canvas.offsetHeight);
if (hasOld) {
e.deltaX = e.x - oldX;
e.deltaY = e.y - oldY;
Expand All @@ -253,15 +272,29 @@ function addEventListeners() {
}
oldX = e.x;
oldY = e.y;
}

function augmentMouseEvent(original) {
var e = duplicateEvent(original);
e.x = e.pageX;
e.y = e.pageY;
addRelativeCoords(e);
e.dragging = isDragging();
e.preventDefault = function() {
e.original.preventDefault();
};
e.stopPropagation = function() {
e.original.stopPropagation();
};
return e;
}

function augmentTouchEvent(original) {
var e = duplicateEvent(original);
if (e.targetTouches.length > 0) {
var touch = e.targetTouches[0];
e.x = touch.pageX;
e.y = touch.pageY;
addRelativeCoords(e);
e.dragging = true;
}
return e;
}

function mousedown(e) {
gl = context;
if (!isDragging()) {
Expand All @@ -272,16 +305,18 @@ function addEventListeners() {
off(gl.canvas, 'mouseup', mouseup);
}
buttons[e.which] = true;
e = augment(e);
e = augmentMouseEvent(e);
if (gl.onmousedown) gl.onmousedown(e);
e.preventDefault();
}

function mousemove(e) {
gl = context;
e = augment(e);
e = augmentMouseEvent(e);
if (gl.onmousemove) gl.onmousemove(e);
e.preventDefault();
}

function mouseup(e) {
gl = context;
buttons[e.which] = false;
Expand All @@ -292,22 +327,75 @@ function addEventListeners() {
on(gl.canvas, 'mousemove', mousemove);
on(gl.canvas, 'mouseup', mouseup);
}
e = augment(e);
e = augmentMouseEvent(e);
if (gl.onmouseup) gl.onmouseup(e);
e.preventDefault();
}

function mousewheel(e) {
gl = context;
e = augmentMouseEvent(e);
if (gl.onmousewheel) gl.onmousewheel(e);
e.preventDefault();
}

function touchstart(e) {
resetAll();
// Expand the event handlers to the document to handle dragging off canvas.
on(document, 'touchmove', touchmove);
on(document, 'touchend', touchend);
off(gl.canvas, 'touchmove', touchmove);
off(gl.canvas, 'touchend', touchend);
gl = context;
e = augmentTouchEvent(e);
if (gl.ontouchstart) gl.ontouchstart(e);
e.preventDefault();
}

function touchmove(e) {
gl = context;
if (e.targetTouches.length === 0) {
touchend(e);
}
e = augmentTouchEvent(e);
if (gl.ontouchmove) gl.ontouchmove(e);
e.preventDefault();
}

function touchend(e) {
// Shrink the event handlers back to the canvas when dragging ends.
off(document, 'touchmove', touchmove);
off(document, 'touchend', touchend);
on(gl.canvas, 'touchmove', touchmove);
on(gl.canvas, 'touchend', touchend);
gl = context;
e = augmentTouchEvent(e);
if (gl.ontouchend) gl.ontouchend(e);
e.preventDefault();
}

function reset() {
hasOld = false;
}

function resetAll() {
buttons = {};
hasOld = false;
}

// We can keep mouse and touch events enabled at the same time,
// because Google Chrome will apparently never fire both of them.

on(gl.canvas, 'mousedown', mousedown);
on(gl.canvas, 'mousemove', mousemove);
on(gl.canvas, 'mouseup', mouseup);
on(gl.canvas, 'mousewheel', mousewheel);
on(gl.canvas, 'DOMMouseScroll', mousewheel);
on(gl.canvas, 'mouseover', reset);
on(gl.canvas, 'mouseout', reset);
on(gl.canvas, 'touchstart', touchstart);
on(gl.canvas, 'touchmove', touchmove);
on(gl.canvas, 'touchend', touchend);
on(document, 'contextmenu', resetAll);
}

Expand Down Expand Up @@ -439,6 +527,7 @@ function addOtherMethods() {
options.near || 0.1, options.far || 1000);
gl.matrixMode(gl.MODELVIEW);
}
if (gl.onresize) gl.onresize();
if (gl.ondraw) gl.ondraw();
}
on(window, 'resize', resize);
Expand Down
31 changes: 31 additions & 0 deletions src/shader.js
Original file line number Diff line number Diff line change
Expand Up @@ -263,3 +263,34 @@ Shader.prototype = {
return this;
}
};

// ### GL.Shader.fromURL(vsURL, fsURL)
//
// Compiles a shader program using the provided vertex and fragment
// shaders. The shaders are loaded synchronously from the given URLs.
//
Shader.fromURL = function(vsURL, fsURL) {

var XMLHttpRequestGet = function (uri) {
var mHttpReq = new XMLHttpRequest();
mHttpReq.open("GET", uri + "?" + Math.random(), false);
mHttpReq.send(null);
if (mHttpReq.status !== 200) {
throw 'could not load ' + uri;
}
return mHttpReq.responseText;
};

var vsSource = XMLHttpRequestGet(vsURL);
var fsSource = XMLHttpRequestGet(fsURL);

return new Shader(vsSource, fsSource);
};

Shader.from = function(vsURLorID, fsURLorID) {
try {
return new Shader(vsURLorID, fsURLorID);
} catch (e) {
return Shader.fromURL(vsURLorID, fsURLorID);
}
};
45 changes: 26 additions & 19 deletions src/texture.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ function Texture(width, height, options) {
this.format = options.format || gl.RGBA;
this.type = options.type || gl.UNSIGNED_BYTE;
gl.bindTexture(gl.TEXTURE_2D, this.id);
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, options.filter || options.magFilter || gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, options.filter || options.minFilter || gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, options.wrap || options.wrapS || gl.CLAMP_TO_EDGE);
Expand Down Expand Up @@ -59,33 +59,40 @@ Texture.prototype = {
gl.bindTexture(gl.TEXTURE_2D, null);
},

// ### .drawTo(callback)
// ### .drawTo(callback[, options])
//
// Render all draw calls in `callback` to this texture. This method sets up
// a framebuffer with this texture as the color attachment and a renderbuffer
// as the depth attachment. It also temporarily changes the viewport to the
// size of the texture.
//
// Example usage:
// Render all draw calls in `callback` to this texture. This method
// sets up a framebuffer with this texture as the color attachment
// and a renderbuffer as the depth attachment. The viewport is
// temporarily changed to the size of the texture.
//
// The depth buffer can be omitted via `options` as shown in the
// example below:
//
// texture.drawTo(function() {
// gl.clearColor(1, 0, 0, 1);
// gl.clear(gl.COLOR_BUFFER_BIT);
// });
drawTo: function(callback) {
// }, { depth: false });
drawTo: function(callback, options) {

options = options || {};
var v = gl.getParameter(gl.VIEWPORT);
gl.viewport(0, 0, this.width, this.height);

framebuffer = framebuffer || gl.createFramebuffer();
renderbuffer = renderbuffer || gl.createRenderbuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer);
if (this.width != renderbuffer.width || this.height != renderbuffer.height) {
renderbuffer.width = this.width;
renderbuffer.height = this.height;
gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, this.width, this.height);
}
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.id, 0);
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, renderbuffer);
gl.viewport(0, 0, this.width, this.height);

if (!(options.depth === false)) {
renderbuffer = renderbuffer || gl.createRenderbuffer();
gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer);
if (this.width != renderbuffer.width || this.height != renderbuffer.height) {
renderbuffer.width = this.width;
renderbuffer.height = this.height;
gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, this.width, this.height);
}
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, renderbuffer);
}

callback();

Expand Down