diff --git a/example.html b/example.html
index 25f737c..a1317ae 100644
--- a/example.html
+++ b/example.html
@@ -16,8 +16,6 @@
document.body.appendChild(heatmap.canvas);
var paintAtCoord = function(x, y){
- //heatmap.addPoint(x, y, 100, 10/255);
-
var count = 0;
while(count < 200){
var xoff = Math.random()*2-1;
@@ -30,7 +28,7 @@
xoff/=ls; yoff/=ls;
xoff*=1-l; yoff*=1-l;
count += 1;
- heatmap.addPoint(x+xoff*50, y+yoff*50, 20, 1/300);
+ heatmap.addPoint(x+xoff*50, y+yoff*50, 30, 2/300);
}
}
diff --git a/webgl-heatmap.coffee b/webgl-heatmap.coffee
index 726b86b..13007a7 100644
--- a/webgl-heatmap.coffee
+++ b/webgl-heatmap.coffee
@@ -1,3 +1,459 @@
+nukeVendorPrefix = ->
+ if window.WebGLRenderingContext?
+ vendors = ['WEBKIT', 'MOZ', 'MS', 'O']
+ vendorRe = /^WEBKIT_(.*)|MOZ_(.*)|MS_(.*)|O_(.*)/
+
+ getExtension = WebGLRenderingContext.prototype.getExtension
+ WebGLRenderingContext.prototype.getExtension = (name) ->
+ match = name.match vendorRe
+ if match != null
+ name = match[1]
+
+ extobj = getExtension.call @, name
+ if extobj == null
+ for vendor in vendors
+ extobj = getExtension.call @, vendor + '_' + name
+ if extobj != null
+ return extobj
+ return null
+ else
+ return extobj
+
+ getSupportedExtensions = WebGLRenderingContext.prototype.getSupportedExtensions
+ WebGLRenderingContext.prototype.getSupportedExtensions = ->
+ supported = getSupportedExtensions.call @
+ result = []
+
+ for extension in supported
+ match = extension.match vendorRe
+ if match != null
+ extension = match[1]
+
+ if extension not in result
+ result.push extension
+
+ return result
+
+textureFloatShims = ->
+ createSourceCanvas = ->
+ canvas = document.createElement 'canvas'
+ canvas.width = 2
+ canvas.height = 2
+ ctx = canvas.getContext '2d'
+ imageData = ctx.getImageData(0, 0, 2, 2)
+ imageData.data.set(new Uint8ClampedArray([
+ 0,0,0,0,
+ 255,255,255,255,
+ 0,0,0,0,
+ 255,255,255,255,
+ ]))
+ ctx.putImageData(imageData, 0, 0)
+ return canvas
+
+ createSourceCanvas()
+
+ checkFloatLinear = (gl, sourceType) ->
+ ## drawing program ##
+ program = gl.createProgram()
+ vertexShader = gl.createShader(gl.VERTEX_SHADER)
+ gl.attachShader(program, vertexShader)
+ gl.shaderSource(vertexShader, '''
+ attribute vec2 position;
+ void main(){
+ gl_Position = vec4(position, 0.0, 1.0);
+ }
+ ''')
+
+ gl.compileShader(vertexShader)
+ if not gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)
+ throw gl.getShaderInfoLog(vertexShader)
+
+ fragmentShader = gl.createShader(gl.FRAGMENT_SHADER)
+ gl.attachShader(program, fragmentShader)
+ gl.shaderSource(fragmentShader, '''
+ uniform sampler2D source;
+ void main(){
+ gl_FragColor = texture2D(source, vec2(1.0, 1.0));
+ }
+ ''')
+ gl.compileShader(fragmentShader)
+ if not gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)
+ throw gl.getShaderInfoLog(fragmentShader)
+
+ gl.linkProgram(program)
+ if not gl.getProgramParameter(program, gl.LINK_STATUS)
+ throw gl.getProgramInfoLog(program)
+
+ gl.useProgram(program)
+
+ cleanup = ->
+ gl.deleteShader(fragmentShader)
+ gl.deleteShader(vertexShader)
+ gl.deleteProgram(program)
+ gl.deleteBuffer(buffer)
+ gl.deleteTexture(source)
+ gl.deleteTexture(target)
+ gl.deleteFramebuffer(framebuffer)
+
+ gl.bindBuffer(gl.ARRAY_BUFFER, null)
+ gl.useProgram(null)
+ gl.bindTexture(gl.TEXTURE_2D, null)
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null)
+
+ ## target FBO ##
+ target = gl.createTexture()
+ gl.bindTexture(gl.TEXTURE_2D, target)
+ gl.texImage2D(
+ gl.TEXTURE_2D,
+ 0,
+ gl.RGBA,
+ 2, 2,
+ 0,
+ gl.RGBA,
+ gl.UNSIGNED_BYTE,
+ null,
+ )
+
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
+
+ framebuffer = gl.createFramebuffer()
+ gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer)
+ gl.framebufferTexture2D(
+ gl.FRAMEBUFFER,
+ gl.COLOR_ATTACHMENT0,
+ gl.TEXTURE_2D,
+ target,
+ 0
+ )
+
+ ## source texture ##
+ sourceCanvas = createSourceCanvas()
+ source = gl.createTexture()
+ gl.bindTexture(gl.TEXTURE_2D, source)
+ gl.texImage2D(
+ gl.TEXTURE_2D,
+ 0,
+ gl.RGBA,
+ gl.RGBA,
+ sourceType,
+ sourceCanvas,
+ )
+
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
+
+ ## create VBO ##
+ vertices = new Float32Array([
+ 1, 1,
+ -1, 1,
+ -1, -1,
+
+ 1, 1,
+ -1, -1,
+ 1, -1,
+ ])
+ buffer = gl.createBuffer()
+ gl.bindBuffer(gl.ARRAY_BUFFER, buffer)
+ gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW)
+ positionLoc = gl.getAttribLocation(program, 'position')
+ sourceLoc = gl.getUniformLocation(program, 'source')
+ gl.enableVertexAttribArray(positionLoc)
+ gl.vertexAttribPointer(positionLoc, 2, gl.FLOAT, false, 0, 0)
+ gl.uniform1i(sourceLoc, 0)
+ gl.drawArrays(gl.TRIANGLES, 0, 6)
+
+ readBuffer = new Uint8Array(4*4)
+ gl.readPixels(0, 0, 2, 2, gl.RGBA, gl.UNSIGNED_BYTE, readBuffer)
+
+ result = Math.abs(readBuffer[0] - 127) < 10
+
+ cleanup()
+ return result
+
+ checkTexture = (gl, targetType) ->
+ target = gl.createTexture()
+ gl.bindTexture(gl.TEXTURE_2D, target)
+ gl.texImage2D(
+ gl.TEXTURE_2D,
+ 0,
+ gl.RGBA,
+ 2, 2,
+ 0,
+ gl.RGBA,
+ targetType,
+ null,
+ )
+
+ if gl.getError() == 0
+ gl.deleteTexture(target)
+ return true
+ else
+ gl.deleteTexture(target)
+ return false
+
+ checkColorBuffer = (gl, targetType) ->
+ target = gl.createTexture()
+ gl.bindTexture(gl.TEXTURE_2D, target)
+ gl.texImage2D(
+ gl.TEXTURE_2D,
+ 0,
+ gl.RGBA,
+ 2, 2,
+ 0,
+ gl.RGBA,
+ targetType,
+ null,
+ )
+
+ framebuffer = gl.createFramebuffer()
+ gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer)
+ gl.framebufferTexture2D(
+ gl.FRAMEBUFFER,
+ gl.COLOR_ATTACHMENT0,
+ gl.TEXTURE_2D,
+ target,
+ 0
+ )
+
+ check = gl.checkFramebufferStatus(gl.FRAMEBUFFER)
+
+ gl.deleteTexture(target)
+ gl.deleteFramebuffer(framebuffer)
+ gl.bindTexture(gl.TEXTURE_2D, null)
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null)
+
+ if check == gl.FRAMEBUFFER_COMPLETE
+ return true
+ else
+ return false
+
+ shimExtensions = []
+ shimLookup = {}
+ unshimExtensions = []
+
+ checkSupport = ->
+ canvas = document.createElement 'canvas'
+ gl = null
+ try
+ gl = canvas.getContext 'experimental-webgl'
+ if(gl == null)
+ gl = canvas.getContext 'webgl'
+
+ if gl?
+ singleFloatExt = gl.getExtension 'OES_texture_float'
+ if singleFloatExt == null
+ if checkTexture gl, gl.FLOAT
+ singleFloatTexturing = true
+ shimExtensions.push 'OES_texture_float'
+ shimLookup.OES_texture_float = {shim:true}
+ else
+ singleFloatTexturing = false
+ unshimExtensions.push 'OES_texture_float'
+ else
+ if checkTexture gl, gl.FLOAT
+ singleFloatTexturing = true
+ shimExtensions.push 'OES_texture_float'
+ else
+ singleFloatTexturing = false
+ unshimExtensions.push 'OES_texture_float'
+
+ if singleFloatTexturing
+ extobj = gl.getExtension 'WEBGL_color_buffer_float'
+ if extobj == null
+ if checkColorBuffer gl, gl.FLOAT
+ shimExtensions.push 'WEBGL_color_buffer_float'
+ shimLookup.WEBGL_color_buffer_float = {
+ shim: true
+ RGBA32F_EXT: 0x8814
+ RGB32F_EXT: 0x8815
+ FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE_EXT: 0x8211
+ UNSIGNED_NORMALIZED_EXT: 0x8C17
+ }
+ else
+ unshimExtensions.push 'WEBGL_color_buffer_float'
+ else
+ if checkColorBuffer gl, gl.FLOAT
+ shimExtensions.push 'WEBGL_color_buffer_float'
+ else
+ unshimExtensions.push 'WEBGL_color_buffer_float'
+
+ extobj = gl.getExtension 'OES_texture_float_linear'
+ if extobj == null
+ if checkFloatLinear gl, gl.FLOAT
+ shimExtensions.push 'OES_texture_float_linear'
+ shimLookup.OES_texture_float_linear = {shim:true}
+ else
+ unshimExtensions.push 'OES_texture_float_linear'
+ else
+ if checkFloatLinear gl, gl.FLOAT
+ shimExtensions.push 'OES_texture_float_linear'
+ else
+ unshimExtensions.push 'OES_texture_float_linear'
+
+ halfFloatExt = gl.getExtension 'OES_texture_half_float'
+ if halfFloatExt == null
+ if checkTexture(gl, 0x8D61)
+ halfFloatTexturing = true
+ shimExtensions.push 'OES_texture_half_float'
+ halfFloatExt = shimLookup.OES_texture_half_float = {
+ HALF_FLOAT_OES: 0x8D61
+ shim:true
+ }
+ else
+ halfFloatTexturing = false
+ unshimExtensions.push 'OES_texture_half_float'
+ else
+ if checkTexture(gl, halfFloatExt.HALF_FLOAT_OES)
+ halfFloatTexturing = true
+ shimExtensions.push 'OES_texture_half_float'
+ else
+ halfFloatTexturing = false
+ unshimExtensions.push 'OES_texture_half_float'
+
+ if halfFloatTexturing
+ extobj = gl.getExtension 'EXT_color_buffer_half_float'
+ if extobj == null
+ if checkColorBuffer gl, halfFloatExt.HALF_FLOAT_OES
+ shimExtensions.push 'EXT_color_buffer_half_float'
+ shimLookup.EXT_color_buffer_half_float = {
+ shim: true
+ RGBA16F_EXT: 0x881A
+ RGB16F_EXT: 0x881B
+ FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE_EXT: 0x8211
+ UNSIGNED_NORMALIZED_EXT: 0x8C17
+ }
+ else
+ unshimExtensions.push 'EXT_color_buffer_half_float'
+ else
+ if checkColorBuffer gl, halfFloatExt.HALF_FLOAT_OES
+ shimExtensions.push 'EXT_color_buffer_half_float'
+ else
+ unshimExtensions.push 'EXT_color_buffer_half_float'
+
+ extobj = gl.getExtension 'OES_texture_half_float_linear'
+ if extobj == null
+ if checkFloatLinear gl, halfFloatExt.HALF_FLOAT_OES
+ shimExtensions.push 'OES_texture_half_float_linear'
+ shimLookup.OES_texture_half_float_linear = {shim:true}
+ else
+ unshimExtensions.push 'OES_texture_half_float_linear'
+ else
+ if checkFloatLinear gl, halfFloatExt.HALF_FLOAT_OES
+ shimExtensions.push 'OES_texture_half_float_linear'
+ else
+ unshimExtensions.push 'OES_texture_half_float_linear'
+
+ if window.WebGLRenderingContext?
+ checkSupport()
+
+ unshimLookup = {}
+ for name in unshimExtensions
+ unshimLookup[name] = true
+
+ getExtension = WebGLRenderingContext.prototype.getExtension
+ WebGLRenderingContext.prototype.getExtension = (name) ->
+ extobj = shimLookup[name]
+ if extobj == undefined
+ if unshimLookup[name]
+ return null
+ else
+ return getExtension.call @, name
+ else
+ return extobj
+
+ getSupportedExtensions = WebGLRenderingContext.prototype.getSupportedExtensions
+ WebGLRenderingContext.prototype.getSupportedExtensions = ->
+ supported = getSupportedExtensions.call(@)
+ result = []
+
+ for extension in supported
+ if unshimLookup[extension] == undefined
+ result.push(extension)
+
+ for extension in shimExtensions
+ if extension not in result
+ result.push extension
+
+ return result
+
+ WebGLRenderingContext.prototype.getFloatExtension = (spec) ->
+ spec.prefer ?= ['half']
+ spec.require ?= []
+ spec.throws ?= true
+
+ singleTexture = @getExtension 'OES_texture_float'
+ halfTexture = @getExtension 'OES_texture_half_float'
+ singleFramebuffer = @getExtension 'WEBGL_color_buffer_float'
+ halfFramebuffer = @getExtension 'EXT_color_buffer_half_float'
+ singleLinear = @getExtension 'OES_texture_float_linear'
+ halfLinear = @getExtension 'OES_texture_half_float_linear'
+
+ single = {
+ texture: singleTexture != null
+ filterable: singleLinear != null
+ renderable: singleFramebuffer != null
+ score: 0
+ precision: 'single'
+ half: false
+ single: true
+ type: @FLOAT
+ }
+
+ half = {
+ texture: halfTexture != null
+ filterable: halfLinear != null
+ renderable: halfFramebuffer != null
+ score: 0
+ precision: 'half'
+ half: true
+ single: false
+ type: halfTexture?.HALF_FLOAT_OES ? null
+ }
+
+ candidates = []
+ if single.texture
+ candidates.push(single)
+ if half.texture
+ candidates.push(half)
+
+ result = []
+ for candidate in candidates
+ use = true
+ for name in spec.require
+ if candidate[name] == false
+ use = false
+ if use
+ result.push candidate
+
+ for candidate in result
+ for preference, i in spec.prefer
+ importance = Math.pow 2, spec.prefer.length - i - 1
+ if candidate[preference]
+ candidate.score += importance
+
+ result.sort (a, b) ->
+ if a.score == b.score then 0
+ else if a.score < b.score then 1
+ else if a.score > b.score then -1
+
+ if result.length == 0
+ if throws
+ throw 'No floating point texture support that is ' + spec.require.join(', ')
+ else
+ return null
+ else
+ result = result[0]
+ return {
+ filterable: result.filterable
+ renderable: result.renderable
+ type: result.type
+ precision: result.precision
+ }
+
+nukeVendorPrefix()
+textureFloatShims()
+
class Shader
constructor: (@gl, {vertex, fragment}) ->
@program = @gl.createProgram()
@@ -106,8 +562,12 @@ class Framebuffer
class Texture
constructor: (@gl, params={}) ->
- @channels = @gl[(params.channels ? 'rgb').toUpperCase()]
- @type = @gl[(params.type ? 'unsigned_byte').toUpperCase()]
+ @channels = @gl[(params.channels ? 'rgba').toUpperCase()]
+
+ if typeof(params.type) == 'number'
+ @type = params.type
+ else
+ @type = @gl[(params.type ? 'unsigned_byte').toUpperCase()]
switch @channels
when @gl.RGBA then @chancount = 4
@@ -163,11 +623,9 @@ class Texture
class Node
constructor: (@gl, @width, @height) ->
- @texture = new Texture(@gl, type:'float').bind(0).setSize(@width, @height).nearest().clampToEdge()
- try
- @fbo = new Framebuffer(@gl).bind().color(@texture).unbind()
- catch error
- throw 'Floating point render target not supported'
+ floatExt = @gl.getFloatExtension require: ['renderable']
+ @texture = new Texture(@gl, type:floatExt.type).bind(0).setSize(@width, @height).nearest().clampToEdge()
+ @fbo = new Framebuffer(@gl).bind().color(@texture).unbind()
use: -> @fbo.bind()
bind: (unit) -> @texture.bind(unit)
@@ -395,9 +853,6 @@ class WebGLHeatmap
@gl = WebGLDebugUtils.makeDebugContext @gl, (err, funcName, args) ->
throw WebGLDebugUtils.glEnumToString(err) + " was caused by call to: " + funcName
- if not @gl.getExtension('OES_texture_float')
- throw 'No floating point texture support'
-
@gl.enableVertexAttribArray 0
@gl.blendFunc @gl.ONE, @gl.ONE
diff --git a/webgl-heatmap.js b/webgl-heatmap.js
index 8b3fab7..dcb4a74 100644
--- a/webgl-heatmap.js
+++ b/webgl-heatmap.js
@@ -1,6 +1,445 @@
// Generated by CoffeeScript 1.3.3
(function() {
- var Framebuffer, Heights, Node, Shader, Texture, WebGLHeatmap, fragmentShaderBlit, vertexShaderBlit;
+ var Framebuffer, Heights, Node, Shader, Texture, WebGLHeatmap, fragmentShaderBlit, nukeVendorPrefix, textureFloatShims, vertexShaderBlit,
+ __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
+
+ nukeVendorPrefix = function() {
+ var getExtension, getSupportedExtensions, vendorRe, vendors;
+ if (window.WebGLRenderingContext != null) {
+ vendors = ['WEBKIT', 'MOZ', 'MS', 'O'];
+ vendorRe = /^WEBKIT_(.*)|MOZ_(.*)|MS_(.*)|O_(.*)/;
+ getExtension = WebGLRenderingContext.prototype.getExtension;
+ WebGLRenderingContext.prototype.getExtension = function(name) {
+ var extobj, match, vendor, _i, _len;
+ match = name.match(vendorRe);
+ if (match !== null) {
+ name = match[1];
+ }
+ extobj = getExtension.call(this, name);
+ if (extobj === null) {
+ for (_i = 0, _len = vendors.length; _i < _len; _i++) {
+ vendor = vendors[_i];
+ extobj = getExtension.call(this, vendor + '_' + name);
+ if (extobj !== null) {
+ return extobj;
+ }
+ }
+ return null;
+ } else {
+ return extobj;
+ }
+ };
+ getSupportedExtensions = WebGLRenderingContext.prototype.getSupportedExtensions;
+ return WebGLRenderingContext.prototype.getSupportedExtensions = function() {
+ var extension, match, result, supported, _i, _len;
+ supported = getSupportedExtensions.call(this);
+ result = [];
+ for (_i = 0, _len = supported.length; _i < _len; _i++) {
+ extension = supported[_i];
+ match = extension.match(vendorRe);
+ if (match !== null) {
+ extension = match[1];
+ }
+ if (__indexOf.call(result, extension) < 0) {
+ result.push(extension);
+ }
+ }
+ return result;
+ };
+ }
+ };
+
+ textureFloatShims = function() {
+ var checkColorBuffer, checkFloatLinear, checkSupport, checkTexture, createSourceCanvas, getExtension, getSupportedExtensions, name, shimExtensions, shimLookup, unshimExtensions, unshimLookup, _i, _len;
+ createSourceCanvas = function() {
+ var canvas, ctx, imageData;
+ canvas = document.createElement('canvas');
+ canvas.width = 2;
+ canvas.height = 2;
+ ctx = canvas.getContext('2d');
+ imageData = ctx.getImageData(0, 0, 2, 2);
+ imageData.data.set(new Uint8ClampedArray([0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255]));
+ ctx.putImageData(imageData, 0, 0);
+ return canvas;
+ };
+ createSourceCanvas();
+ checkFloatLinear = function(gl, sourceType) {
+ var buffer, cleanup, fragmentShader, framebuffer, positionLoc, program, readBuffer, result, source, sourceCanvas, sourceLoc, target, vertexShader, vertices;
+ program = gl.createProgram();
+ vertexShader = gl.createShader(gl.VERTEX_SHADER);
+ gl.attachShader(program, vertexShader);
+ gl.shaderSource(vertexShader, 'attribute vec2 position;\nvoid main(){\n gl_Position = vec4(position, 0.0, 1.0);\n}');
+ gl.compileShader(vertexShader);
+ if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
+ throw gl.getShaderInfoLog(vertexShader);
+ }
+ fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
+ gl.attachShader(program, fragmentShader);
+ gl.shaderSource(fragmentShader, 'uniform sampler2D source;\nvoid main(){\n gl_FragColor = texture2D(source, vec2(1.0, 1.0));\n}');
+ gl.compileShader(fragmentShader);
+ if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
+ throw gl.getShaderInfoLog(fragmentShader);
+ }
+ gl.linkProgram(program);
+ if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
+ throw gl.getProgramInfoLog(program);
+ }
+ gl.useProgram(program);
+ cleanup = function() {
+ gl.deleteShader(fragmentShader);
+ gl.deleteShader(vertexShader);
+ gl.deleteProgram(program);
+ gl.deleteBuffer(buffer);
+ gl.deleteTexture(source);
+ gl.deleteTexture(target);
+ gl.deleteFramebuffer(framebuffer);
+ gl.bindBuffer(gl.ARRAY_BUFFER, null);
+ gl.useProgram(null);
+ gl.bindTexture(gl.TEXTURE_2D, null);
+ return gl.bindFramebuffer(gl.FRAMEBUFFER, null);
+ };
+ target = gl.createTexture();
+ gl.bindTexture(gl.TEXTURE_2D, target);
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 2, 2, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
+ framebuffer = gl.createFramebuffer();
+ gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
+ gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, target, 0);
+ sourceCanvas = createSourceCanvas();
+ source = gl.createTexture();
+ gl.bindTexture(gl.TEXTURE_2D, source);
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, sourceType, sourceCanvas);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
+ vertices = new Float32Array([1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1]);
+ buffer = gl.createBuffer();
+ gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
+ gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
+ positionLoc = gl.getAttribLocation(program, 'position');
+ sourceLoc = gl.getUniformLocation(program, 'source');
+ gl.enableVertexAttribArray(positionLoc);
+ gl.vertexAttribPointer(positionLoc, 2, gl.FLOAT, false, 0, 0);
+ gl.uniform1i(sourceLoc, 0);
+ gl.drawArrays(gl.TRIANGLES, 0, 6);
+ readBuffer = new Uint8Array(4 * 4);
+ gl.readPixels(0, 0, 2, 2, gl.RGBA, gl.UNSIGNED_BYTE, readBuffer);
+ result = Math.abs(readBuffer[0] - 127) < 10;
+ cleanup();
+ return result;
+ };
+ checkTexture = function(gl, targetType) {
+ var target;
+ target = gl.createTexture();
+ gl.bindTexture(gl.TEXTURE_2D, target);
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 2, 2, 0, gl.RGBA, targetType, null);
+ if (gl.getError() === 0) {
+ gl.deleteTexture(target);
+ return true;
+ } else {
+ gl.deleteTexture(target);
+ return false;
+ }
+ };
+ checkColorBuffer = function(gl, targetType) {
+ var check, framebuffer, target;
+ target = gl.createTexture();
+ gl.bindTexture(gl.TEXTURE_2D, target);
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 2, 2, 0, gl.RGBA, targetType, null);
+ framebuffer = gl.createFramebuffer();
+ gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
+ gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, target, 0);
+ check = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
+ gl.deleteTexture(target);
+ gl.deleteFramebuffer(framebuffer);
+ gl.bindTexture(gl.TEXTURE_2D, null);
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
+ if (check === gl.FRAMEBUFFER_COMPLETE) {
+ return true;
+ } else {
+ return false;
+ }
+ };
+ shimExtensions = [];
+ shimLookup = {};
+ unshimExtensions = [];
+ checkSupport = function() {
+ var canvas, extobj, gl, halfFloatExt, halfFloatTexturing, singleFloatExt, singleFloatTexturing;
+ canvas = document.createElement('canvas');
+ gl = null;
+ try {
+ gl = canvas.getContext('experimental-webgl');
+ if (gl === null) {
+ gl = canvas.getContext('webgl');
+ }
+ } catch (_error) {}
+ if (gl != null) {
+ singleFloatExt = gl.getExtension('OES_texture_float');
+ if (singleFloatExt === null) {
+ if (checkTexture(gl, gl.FLOAT)) {
+ singleFloatTexturing = true;
+ shimExtensions.push('OES_texture_float');
+ shimLookup.OES_texture_float = {
+ shim: true
+ };
+ } else {
+ singleFloatTexturing = false;
+ unshimExtensions.push('OES_texture_float');
+ }
+ } else {
+ if (checkTexture(gl, gl.FLOAT)) {
+ singleFloatTexturing = true;
+ shimExtensions.push('OES_texture_float');
+ } else {
+ singleFloatTexturing = false;
+ unshimExtensions.push('OES_texture_float');
+ }
+ }
+ if (singleFloatTexturing) {
+ extobj = gl.getExtension('WEBGL_color_buffer_float');
+ if (extobj === null) {
+ if (checkColorBuffer(gl, gl.FLOAT)) {
+ shimExtensions.push('WEBGL_color_buffer_float');
+ shimLookup.WEBGL_color_buffer_float = {
+ shim: true,
+ RGBA32F_EXT: 0x8814,
+ RGB32F_EXT: 0x8815,
+ FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE_EXT: 0x8211,
+ UNSIGNED_NORMALIZED_EXT: 0x8C17
+ };
+ } else {
+ unshimExtensions.push('WEBGL_color_buffer_float');
+ }
+ } else {
+ if (checkColorBuffer(gl, gl.FLOAT)) {
+ shimExtensions.push('WEBGL_color_buffer_float');
+ } else {
+ unshimExtensions.push('WEBGL_color_buffer_float');
+ }
+ }
+ extobj = gl.getExtension('OES_texture_float_linear');
+ if (extobj === null) {
+ if (checkFloatLinear(gl, gl.FLOAT)) {
+ shimExtensions.push('OES_texture_float_linear');
+ shimLookup.OES_texture_float_linear = {
+ shim: true
+ };
+ } else {
+ unshimExtensions.push('OES_texture_float_linear');
+ }
+ } else {
+ if (checkFloatLinear(gl, gl.FLOAT)) {
+ shimExtensions.push('OES_texture_float_linear');
+ } else {
+ unshimExtensions.push('OES_texture_float_linear');
+ }
+ }
+ }
+ halfFloatExt = gl.getExtension('OES_texture_half_float');
+ if (halfFloatExt === null) {
+ if (checkTexture(gl, 0x8D61)) {
+ halfFloatTexturing = true;
+ shimExtensions.push('OES_texture_half_float');
+ halfFloatExt = shimLookup.OES_texture_half_float = {
+ HALF_FLOAT_OES: 0x8D61,
+ shim: true
+ };
+ } else {
+ halfFloatTexturing = false;
+ unshimExtensions.push('OES_texture_half_float');
+ }
+ } else {
+ if (checkTexture(gl, halfFloatExt.HALF_FLOAT_OES)) {
+ halfFloatTexturing = true;
+ shimExtensions.push('OES_texture_half_float');
+ } else {
+ halfFloatTexturing = false;
+ unshimExtensions.push('OES_texture_half_float');
+ }
+ }
+ if (halfFloatTexturing) {
+ extobj = gl.getExtension('EXT_color_buffer_half_float');
+ if (extobj === null) {
+ if (checkColorBuffer(gl, halfFloatExt.HALF_FLOAT_OES)) {
+ shimExtensions.push('EXT_color_buffer_half_float');
+ shimLookup.EXT_color_buffer_half_float = {
+ shim: true,
+ RGBA16F_EXT: 0x881A,
+ RGB16F_EXT: 0x881B,
+ FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE_EXT: 0x8211,
+ UNSIGNED_NORMALIZED_EXT: 0x8C17
+ };
+ } else {
+ unshimExtensions.push('EXT_color_buffer_half_float');
+ }
+ } else {
+ if (checkColorBuffer(gl, halfFloatExt.HALF_FLOAT_OES)) {
+ shimExtensions.push('EXT_color_buffer_half_float');
+ } else {
+ unshimExtensions.push('EXT_color_buffer_half_float');
+ }
+ }
+ extobj = gl.getExtension('OES_texture_half_float_linear');
+ if (extobj === null) {
+ if (checkFloatLinear(gl, halfFloatExt.HALF_FLOAT_OES)) {
+ shimExtensions.push('OES_texture_half_float_linear');
+ return shimLookup.OES_texture_half_float_linear = {
+ shim: true
+ };
+ } else {
+ return unshimExtensions.push('OES_texture_half_float_linear');
+ }
+ } else {
+ if (checkFloatLinear(gl, halfFloatExt.HALF_FLOAT_OES)) {
+ return shimExtensions.push('OES_texture_half_float_linear');
+ } else {
+ return unshimExtensions.push('OES_texture_half_float_linear');
+ }
+ }
+ }
+ }
+ };
+ if (window.WebGLRenderingContext != null) {
+ checkSupport();
+ unshimLookup = {};
+ for (_i = 0, _len = unshimExtensions.length; _i < _len; _i++) {
+ name = unshimExtensions[_i];
+ unshimLookup[name] = true;
+ }
+ getExtension = WebGLRenderingContext.prototype.getExtension;
+ WebGLRenderingContext.prototype.getExtension = function(name) {
+ var extobj;
+ extobj = shimLookup[name];
+ if (extobj === void 0) {
+ if (unshimLookup[name]) {
+ return null;
+ } else {
+ return getExtension.call(this, name);
+ }
+ } else {
+ return extobj;
+ }
+ };
+ getSupportedExtensions = WebGLRenderingContext.prototype.getSupportedExtensions;
+ WebGLRenderingContext.prototype.getSupportedExtensions = function() {
+ var extension, result, supported, _j, _k, _len1, _len2;
+ supported = getSupportedExtensions.call(this);
+ result = [];
+ for (_j = 0, _len1 = supported.length; _j < _len1; _j++) {
+ extension = supported[_j];
+ if (unshimLookup[extension] === void 0) {
+ result.push(extension);
+ }
+ }
+ for (_k = 0, _len2 = shimExtensions.length; _k < _len2; _k++) {
+ extension = shimExtensions[_k];
+ if (__indexOf.call(result, extension) < 0) {
+ result.push(extension);
+ }
+ }
+ return result;
+ };
+ return WebGLRenderingContext.prototype.getFloatExtension = function(spec) {
+ var candidate, candidates, half, halfFramebuffer, halfLinear, halfTexture, i, importance, preference, result, single, singleFramebuffer, singleLinear, singleTexture, use, _j, _k, _l, _len1, _len2, _len3, _len4, _m, _ref, _ref1, _ref2, _ref3, _ref4, _ref5;
+ if ((_ref = spec.prefer) == null) {
+ spec.prefer = ['half'];
+ }
+ if ((_ref1 = spec.require) == null) {
+ spec.require = [];
+ }
+ if ((_ref2 = spec.throws) == null) {
+ spec.throws = true;
+ }
+ singleTexture = this.getExtension('OES_texture_float');
+ halfTexture = this.getExtension('OES_texture_half_float');
+ singleFramebuffer = this.getExtension('WEBGL_color_buffer_float');
+ halfFramebuffer = this.getExtension('EXT_color_buffer_half_float');
+ singleLinear = this.getExtension('OES_texture_float_linear');
+ halfLinear = this.getExtension('OES_texture_half_float_linear');
+ single = {
+ texture: singleTexture !== null,
+ filterable: singleLinear !== null,
+ renderable: singleFramebuffer !== null,
+ score: 0,
+ precision: 'single',
+ half: false,
+ single: true,
+ type: this.FLOAT
+ };
+ half = {
+ texture: halfTexture !== null,
+ filterable: halfLinear !== null,
+ renderable: halfFramebuffer !== null,
+ score: 0,
+ precision: 'half',
+ half: true,
+ single: false,
+ type: (_ref3 = halfTexture != null ? halfTexture.HALF_FLOAT_OES : void 0) != null ? _ref3 : null
+ };
+ candidates = [];
+ if (single.texture) {
+ candidates.push(single);
+ }
+ if (half.texture) {
+ candidates.push(half);
+ }
+ result = [];
+ for (_j = 0, _len1 = candidates.length; _j < _len1; _j++) {
+ candidate = candidates[_j];
+ use = true;
+ _ref4 = spec.require;
+ for (_k = 0, _len2 = _ref4.length; _k < _len2; _k++) {
+ name = _ref4[_k];
+ if (candidate[name] === false) {
+ use = false;
+ }
+ }
+ if (use) {
+ result.push(candidate);
+ }
+ }
+ for (_l = 0, _len3 = result.length; _l < _len3; _l++) {
+ candidate = result[_l];
+ _ref5 = spec.prefer;
+ for (i = _m = 0, _len4 = _ref5.length; _m < _len4; i = ++_m) {
+ preference = _ref5[i];
+ importance = Math.pow(2, spec.prefer.length - i - 1);
+ if (candidate[preference]) {
+ candidate.score += importance;
+ }
+ }
+ }
+ result.sort(function(a, b) {
+ if (a.score === b.score) {
+ return 0;
+ } else if (a.score < b.score) {
+ return 1;
+ } else if (a.score > b.score) {
+ return -1;
+ }
+ });
+ if (result.length === 0) {
+ if (throws) {
+ throw 'No floating point texture support that is ' + spec.require.join(', ');
+ } else {
+ return null;
+ }
+ } else {
+ result = result[0];
+ return {
+ filterable: result.filterable,
+ renderable: result.renderable,
+ type: result.type,
+ precision: result.precision
+ };
+ }
+ };
+ }
+ };
+
+ nukeVendorPrefix();
+
+ textureFloatShims();
Shader = (function() {
@@ -166,8 +605,12 @@
if (params == null) {
params = {};
}
- this.channels = this.gl[((_ref = params.channels) != null ? _ref : 'rgb').toUpperCase()];
- this.type = this.gl[((_ref1 = params.type) != null ? _ref1 : 'unsigned_byte').toUpperCase()];
+ this.channels = this.gl[((_ref = params.channels) != null ? _ref : 'rgba').toUpperCase()];
+ if (typeof params.type === 'number') {
+ this.type = params.type;
+ } else {
+ this.type = this.gl[((_ref1 = params.type) != null ? _ref1 : 'unsigned_byte').toUpperCase()];
+ }
switch (this.channels) {
case this.gl.RGBA:
this.chancount = 4;
@@ -246,17 +689,17 @@
Node = (function() {
function Node(gl, width, height) {
+ var floatExt;
this.gl = gl;
this.width = width;
this.height = height;
+ floatExt = this.gl.getFloatExtension({
+ require: ['renderable']
+ });
this.texture = new Texture(this.gl, {
- type: 'float'
+ type: floatExt.type
}).bind(0).setSize(this.width, this.height).nearest().clampToEdge();
- try {
- this.fbo = new Framebuffer(this.gl).bind().color(this.texture).unbind();
- } catch (error) {
- throw 'Floating point render target not supported';
- }
+ this.fbo = new Framebuffer(this.gl).bind().color(this.texture).unbind();
}
Node.prototype.use = function() {
@@ -467,9 +910,6 @@
throw WebGLDebugUtils.glEnumToString(err) + " was caused by call to: " + funcName;
});
}
- if (!this.gl.getExtension('OES_texture_float')) {
- throw 'No floating point texture support';
- }
this.gl.enableVertexAttribArray(0);
this.gl.blendFunc(this.gl.ONE, this.gl.ONE);
if (gradientTexture) {