UNPKG

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
// 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);