embr
Version:
Embr is a helper library which attempts to enable sharing code between Plask and WebGL. It is considered experimental and under heavy development.
717 lines (641 loc) • 20.2 kB
JavaScript
// Generated by CoffeeScript 1.6.3
(function() {
var embr, gl, gl_enums, gl_mipmap_filters, setOpts;
embr = this.embr || {};
gl = gl_enums = gl_mipmap_filters = null;
if ((typeof module !== "undefined" && module !== null) && (module.exports != null)) {
module.exports = embr;
} else {
this.embr = embr;
}
embr.setContext = function(_gl) {
embr.gl = gl = _gl;
gl_mipmap_filters = [gl.NEAREST_MIPMAP_NEAREST, gl.LINEAR_MIPMAP_NEAREST, gl.NEAREST_MIPMAP_LINEAR, gl.LINEAR_MIPMAP_LINEAR];
embr.VboAttr.default_settings = {
size: 1,
type: gl.FLOAT,
usage: gl.STATIC_DRAW,
stride: 0,
offset: 0
};
embr.VboIndices.default_settings = {
usage: gl.STATIC_DRAW
};
embr.Texture.default_settings = {
target: gl.TEXTURE_2D,
unit: 0,
format: gl.RGBA,
formatInternal: gl.RGBA,
type: gl.UNSIGNED_BYTE,
filter: gl.NEAREST,
filterMin: null,
filterMag: null,
wrap: gl.CLAMP_TO_EDGE,
wrapS: null,
wrapT: null,
width: 0,
height: 0,
flipY: false
};
embr.Rbo.default_settings = {
target: gl.RENDERBUFFER,
formatInternal: gl.DEPTH_COMPONENT16,
width: 0,
height: 0
};
};
embr.getGLEnumName = function(e) {
var name;
if (gl_enums === null) {
gl_enums = {};
for (name in gl) {
if (typeof gl[name] === 'number') {
gl_enums[gl[name]] = name;
}
}
}
return gl_enums[e];
};
embr.checkError = function(gl, msg) {
var e, err, errs, names;
errs = [];
while ((err = gl.getError()) !== gl.NO_ERROR) {
errs.push(err);
}
if (errs.length > 0) {
names = (function() {
var _i, _len, _results;
_results = [];
for (_i = 0, _len = errs.length; _i < _len; _i++) {
e = errs[_i];
_results.push(embr.getGLEnumName(e));
}
return _results;
})();
throw "" + msg + ": " + (names.join(', '));
}
};
embr.wrapContextWithErrorChecks = function(gl) {
var name, prop, wrapFn, wrapped;
wrapFn = function(name, fn) {
return function() {
var res;
res = fn.apply(gl, arguments);
embr.checkError(gl, "GL Error in " + name);
return res;
};
};
wrapped = {};
for (name in gl) {
prop = gl[name];
wrapped[name] = typeof prop === 'function' ? wrapFn(name, prop) : prop;
}
return wrapped;
};
setOpts = function(src, dest, defaults) {
var name;
for (name in defaults) {
if (src[name] != null) {
dest[name] = src[name];
} else if (dest[name] === void 0) {
dest[name] = defaults[name];
}
}
};
embr.Program = (function() {
function Program(opts) {
this.program = null;
this.linked = false;
if (opts != null) {
this.compile(opts);
}
}
Program.prototype.compile = function(opts) {
var compileAndAttach, program;
if (opts == null) {
opts = {};
}
if (this.program != null) {
gl.deleteProgram(this.program);
}
program = this.program = gl.createProgram();
compileAndAttach = function(src, type) {
var shader;
shader = gl.createShader(type);
gl.shaderSource(shader, src);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
throw gl.getShaderInfoLog(shader);
}
gl.attachShader(program, shader);
return gl.deleteShader(shader);
};
if (opts.vertex != null) {
compileAndAttach(opts.vertex, gl.VERTEX_SHADER);
}
if (opts.fragment != null) {
compileAndAttach(opts.fragment, gl.FRAGMENT_SHADER);
}
return this;
};
Program.prototype.link = function() {
var i, info, is_array, location, makeUniformSetter, name, program, _i, _j, _ref, _ref1;
program = this.program;
gl.linkProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
throw gl.getProgramInfoLog(program);
}
makeUniformSetter = function(type, location, is_array) {
switch (type) {
case gl.BOOL:
case gl.INT:
case gl.SAMPLER_2D:
case gl.SAMPLER_CUBE:
if (is_array) {
return function(array) {
return gl.uniform1iv(location, array);
};
}
return function(value) {
return gl.uniform1i(location, value);
};
case gl.FLOAT:
if (is_array) {
return function(array) {
return gl.uniform1fv(location, array);
};
}
return function(value) {
return gl.uniform1f(location, value);
};
case gl.FLOAT_VEC2:
return function(array) {
return gl.uniform2fv(location, array);
};
case gl.FLOAT_VEC3:
return function(array) {
return gl.uniform3fv(location, array);
};
case gl.FLOAT_VEC4:
return function(array) {
return gl.uniform4fv(location, array);
};
case gl.FLOAT_MAT2:
return function(array) {
return gl.uniformMatrix2fv(location, false, array);
};
case gl.FLOAT_MAT3:
return function(array) {
return gl.uniformMatrix3fv(location, false, array);
};
case gl.FLOAT_MAT4:
return function(array) {
return gl.uniformMatrix4fv(location, false, array);
};
}
return function() {
throw "Unknown uniform type: " + type;
};
};
this.uniforms = {};
this.locations = {};
for (i = _i = 0, _ref = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS); 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) {
info = gl.getActiveUniform(program, i);
location = gl.getUniformLocation(program, info.name);
name = info.name;
is_array = name.slice(-3) === '[0]';
if (is_array) {
name = name.slice(0, -3);
}
this.uniforms[name] = makeUniformSetter(info.type, location, is_array);
this.locations[name] = location;
}
for (i = _j = 0, _ref1 = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES); 0 <= _ref1 ? _j < _ref1 : _j > _ref1; i = 0 <= _ref1 ? ++_j : --_j) {
info = gl.getActiveAttrib(program, i);
location = gl.getAttribLocation(program, info.name);
this.locations[info.name] = location;
}
this.linked = true;
return this;
};
Program.prototype.use = function(uniforms) {
var name, _base;
if (!this.linked) {
throw 'Program must be linked before use.';
}
gl.useProgram(this.program);
if (uniforms != null) {
for (name in uniforms) {
if (typeof (_base = this.uniforms)[name] === "function") {
_base[name](uniforms[name]);
}
}
}
return this;
};
Program.prototype.setUniform = function(name, value) {
var _base;
if (typeof (_base = this.uniforms)[name] === "function") {
_base[name](value);
}
return this;
};
Program.prototype.cleanup = function() {
gl.deleteProgram(this.program);
return this;
};
return Program;
})();
embr.VboAttr = (function() {
function VboAttr(name, opts) {
this.name = name;
this.buffer = null;
this.location = null;
this.length = null;
this.settings = {};
if (opts != null) {
this.set(opts);
}
}
VboAttr.prototype.get = function(name) {
return this.settings[name];
};
VboAttr.prototype.set = function(opts) {
var data, settings;
if (opts == null) {
opts = {};
}
settings = this.settings;
setOpts(opts, settings, embr.VboAttr.default_settings);
if (opts.data != null) {
if (this.buffer === null) {
this.buffer = gl.createBuffer();
}
data = opts.data;
if (!(data.buffer instanceof ArrayBuffer)) {
throw 'Data must be an ArrayBufferView.';
}
if (settings.stride > 0) {
this.length = Math.floor(data.byteLength / settings.stride);
} else {
this.length = Math.floor(data.length / settings.size);
}
gl.bindBuffer(gl.ARRAY_BUFFER, this.buffer);
gl.bufferData(gl.ARRAY_BUFFER, data, settings.usage);
}
return this;
};
VboAttr.prototype.enable = function() {
var settings;
if (this.location != null) {
settings = this.settings;
gl.bindBuffer(gl.ARRAY_BUFFER, this.buffer);
gl.vertexAttribPointer(this.location, settings.size, settings.type, false, settings.stride, settings.offset);
gl.enableVertexAttribArray(this.location);
}
return this;
};
VboAttr.prototype.disable = function() {
if (this.location != null) {
gl.enableVertexAttribArray(this.location);
}
return this;
};
VboAttr.prototype.cleanup = function() {
gl.deleteBuffer(this.buffer);
return this;
};
return VboAttr;
})();
embr.VboIndices = (function() {
function VboIndices(opts) {
this.buffer = null;
this.settings = {};
if (opts != null) {
this.set(opts);
}
}
VboIndices.prototype.get = function(name) {
return this.settings[name];
};
VboIndices.prototype.set = function(opts) {
var data, settings;
if (opts == null) {
opts = {};
}
settings = this.settings;
setOpts(opts, settings, embr.VboIndices.default_settings);
if (opts.data != null) {
if (this.buffer === null) {
this.buffer = gl.createBuffer();
}
data = opts.data;
if (!(data.buffer instanceof ArrayBuffer)) {
throw 'Data must be an ArrayBufferView.';
}
this.length = data.length;
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.buffer);
return gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, data, settings.usage);
}
};
VboIndices.prototype.bind = function() {
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.buffer);
return this;
};
VboIndices.prototype.unbind = function() {
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
return this;
};
VboIndices.prototype.cleanup = function() {
gl.deleteBuffer(this.buffer);
return this;
};
return VboIndices;
})();
embr.Vbo = (function() {
function Vbo(type) {
this.type = type;
this.program = null;
this.indices = null;
this.attributes = {};
}
Vbo.prototype.getAttr = function(name) {
return this.attributes[name];
};
Vbo.prototype.setAttr = function(attr) {
this.attributes[attr.name] = attr;
return this;
};
Vbo.prototype.getIndices = function() {
return this.indices;
};
Vbo.prototype.setIndices = function(indices) {
this.indices = indices;
return this;
};
Vbo.prototype.createAttr = function(name, opts) {
return this.setAttr(new embr.VboAttr(name, opts));
};
Vbo.prototype.createIndices = function(opts) {
return this.setIndices(new embr.VboIndices(opts));
};
Vbo.prototype.getProgram = function() {
return this.program;
};
Vbo.prototype.setProgram = function(program) {
var attr, name, _ref;
if (program.linked) {
this.program = program;
for (name in this.attributes) {
attr = this.attributes[name];
attr.location = (_ref = program.locations[attr.name]) != null ? _ref : null;
}
}
return this;
};
Vbo.prototype.draw = function() {
var attr, attributes, enabled_attrs, indices, min_length, name, _i, _len;
indices = this.indices;
attributes = this.attributes;
min_length = Number.MAX_VALUE;
enabled_attrs = [];
for (name in attributes) {
attr = attributes[name];
if ((attr.location != null) && attr.length > 0) {
attr.enable();
enabled_attrs.push(attr);
if (attr.length < min_length) {
min_length = attr.length;
}
}
}
if (enabled_attrs.length === 0) {
return;
}
if (indices != null) {
indices.bind();
gl.drawElements(this.type, indices.length, gl.UNSIGNED_SHORT, 0);
indices.unbind();
} else {
gl.drawArrays(this.type, 0, min_length);
}
for (_i = 0, _len = enabled_attrs.length; _i < _len; _i++) {
attr = enabled_attrs[_i];
attr.disable();
}
return this;
};
Vbo.prototype.cleanup = function() {
var name, _ref;
for (name in this.attributes) {
this.attributes[name].cleanup();
}
if ((_ref = this.indices) != null) {
_ref.cleanup();
}
return this;
};
return Vbo;
})();
embr.Texture = (function() {
function Texture(opts) {
this.texture = null;
this.settings = {};
if (opts != null) {
this.set(opts);
}
}
Texture.prototype.get = function(name) {
return this.settings[name];
};
Texture.prototype.set = function(opts) {
var createAndBind, filter, filterMag, filterMin, ph, pw, settings, target, wrapS, wrapT, _i, _len, _ref, _ref1, _ref2, _ref3,
_this = this;
if (opts == null) {
opts = {};
}
settings = this.settings;
pw = settings.width;
ph = settings.height;
setOpts(opts, settings, embr.Texture.default_settings);
target = settings.target;
createAndBind = function() {
if (_this.texture === null) {
_this.texture = gl.createTexture();
}
return _this.bind();
};
if (opts.data !== void 0 && settings.width > 0 && settings.height > 0) {
createAndBind();
if (pw === settings.width && ph === settings.height) {
gl.texSubImage2D(target, 0, 0, 0, settings.width, settings.height, settings.format, settings.type, opts.data);
} else {
gl.texImage2D(target, 0, settings.formatInternal, settings.width, settings.height, 0, settings.format, settings.type, opts.data);
}
} else if (opts.element != null) {
createAndBind();
if (settings.flipY) {
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
}
gl.texImage2D(target, 0, settings.formatInternal, settings.format, settings.type, opts.element);
}
if (this.texture != null) {
filterMin = (_ref = settings.filterMin) != null ? _ref : settings.filter;
filterMag = (_ref1 = settings.filterMag) != null ? _ref1 : settings.filter;
wrapS = (_ref2 = settings.wrapS) != null ? _ref2 : settings.wrap;
wrapT = (_ref3 = settings.wrapT) != null ? _ref3 : settings.wrap;
gl.texParameteri(target, gl.TEXTURE_MIN_FILTER, filterMin);
gl.texParameteri(target, gl.TEXTURE_MAG_FILTER, filterMag);
gl.texParameteri(target, gl.TEXTURE_WRAP_S, wrapS);
gl.texParameteri(target, gl.TEXTURE_WRAP_T, wrapT);
for (_i = 0, _len = gl_mipmap_filters.length; _i < _len; _i++) {
filter = gl_mipmap_filters[_i];
if (filterMin === filter) {
gl.generateMipmap(target);
break;
}
}
}
this.unbind();
return this;
};
Texture.prototype.bind = function(unit) {
if (unit != null) {
this.settings.unit = unit;
}
if (this.texture != null) {
gl.activeTexture(gl.TEXTURE0 + this.settings.unit);
gl.bindTexture(this.settings.target, this.texture);
}
return this;
};
Texture.prototype.unbind = function() {
if (this.texture != null) {
gl.activeTexture(gl.TEXTURE0 + this.settings.unit);
gl.bindTexture(this.settings.target, null);
}
return this;
};
Texture.prototype.cleanup = function() {
gl.deleteTexture(this.texture);
return this;
};
return Texture;
})();
embr.Rbo = (function() {
function Rbo(opts) {
this.buffer = gl.createRenderbuffer();
this.settings = {};
if (opts != null) {
this.set(opts);
}
}
Rbo.prototype.get = function(name) {
return this.settings[name];
};
Rbo.prototype.set = function(opts) {
var ph, pw, settings;
if (opts == null) {
opts = {};
}
settings = this.settings;
pw = settings.width;
ph = settings.height;
setOpts(opts, settings, embr.Rbo.default_settings);
if (pw !== settings.width || ph !== settings.height) {
this.bind();
gl.renderbufferStorage(settings.target, settings.formatInternal, settings.width, settings.height);
}
return this;
};
Rbo.prototype.bind = function() {
gl.bindRenderbuffer(this.settings.target, this.buffer);
return this;
};
Rbo.prototype.unbind = function() {
gl.bindRenderbuffer(this.settings.target, null);
return this;
};
Rbo.prototype.cleanup = function() {
gl.deleteRenderbuffer(this.buffer);
return this;
};
return Rbo;
})();
embr.Fbo = (function() {
var fbo_status_suffixes;
function Fbo() {
this.buffer = gl.createFramebuffer();
this.textures = [];
this.renderbuffers = [];
}
Fbo.prototype.getNextColorAttachment = function() {
return gl.COLOR_ATTACHMENT0;
};
Fbo.prototype.attach = function(obj, attachment) {
this.bind();
obj.bind();
if (obj instanceof embr.Texture) {
attachment = attachment != null ? attachment : this.getNextColorAttachment();
gl.framebufferTexture2D(gl.FRAMEBUFFER, attachment, obj.settings.target, obj.texture, 0);
this.textures.push(obj);
} else if (obj instanceof embr.Rbo) {
attachment = attachment != null ? attachment : gl.DEPTH_ATTACHMENT;
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, attachment, obj.settings.target, obj.buffer);
this.renderbuffers.push(obj);
}
obj.unbind();
this.unbind();
return this;
};
fbo_status_suffixes = ['INCOMPLETE_ATTACHMENT', 'INCOMPLETE_MISSING_ATTACHMENT', 'INCOMPLETE_DIMENSIONS', 'UNSUPPORTED'];
Fbo.prototype.check = function() {
var name, status, suffix, _i, _len;
this.bind();
status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
if (status !== gl.FRAMEBUFFER_COMPLETE) {
for (_i = 0, _len = fbo_status_suffixes.length; _i < _len; _i++) {
suffix = fbo_status_suffixes[_i];
name = "FRAMEBUFFER_" + suffix;
if (status === gl[name]) {
throw "Framebuffer Status: " + name;
}
}
}
this.unbind();
return this;
};
Fbo.prototype.bind = function() {
gl.bindFramebuffer(gl.FRAMEBUFFER, this.buffer);
return this;
};
Fbo.prototype.unbind = function() {
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
return this;
};
Fbo.prototype.bindTexture = function(i, unit) {
this.textures[i].bind(unit);
return this;
};
Fbo.prototype.unbindTexture = function(i) {
this.textures[i].unbind();
return this;
};
Fbo.prototype.cleanup = function() {
var renderbuffer, texture, _i, _j, _len, _len1, _ref, _ref1;
gl.deleteFramebuffer(this.buffer);
_ref = this.textures;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
texture = _ref[_i];
texture.cleanup();
}
_ref1 = this.renderbuffers;
for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
renderbuffer = _ref1[_j];
renderbuffer.cleanup();
}
return this;
};
return Fbo;
})();
}).call(this);