Comparing version 0.8.0 to 0.9.0
@@ -5,6 +5,2 @@ # Release notes | ||
* Optimize constructors/updates for renderbuffers and framebuffers | ||
* Cubic frame buffer objects | ||
* More test cases for framebuffers | ||
* Implement a standard method for handling context creation errors | ||
@@ -14,9 +10,17 @@ * Add a mechanism for managing webgl extensions | ||
+ Allow users to disable extensions for testing/mocking | ||
* Add a mechanism for users to specify minimum resource requirements (texture size, varying units, etc.) | ||
* Performance monitoring hooks | ||
* Cubic frame buffer objects | ||
* Context loss handling | ||
* Context loss | ||
* Write comparison suite | ||
* More performance monitoring hooks | ||
+ Track currently used resource requirements | ||
+ Output should be serializable -> works as input to constructor checks | ||
* More validation | ||
+ Should not be possible to write regl code that crashes on some systems | ||
* Benchmark suite | ||
@@ -26,21 +30,30 @@ + Dashboard for test cases and benchmarks | ||
* A pretty printer for the generated code | ||
* Documentation | ||
+ All interface methods must be documented | ||
+ Examples for all major features | ||
+ Set up/quick start guides | ||
+ Examples on codepen/requirebin/regl.party | ||
+ Live coding videos on youtube | ||
+ Talks? (what conferences can we present these results at?) | ||
+ Core library modules need better comments | ||
+ Work flow for development and testing needs documentation | ||
* Testing | ||
+ Instancing | ||
+ Texture generation | ||
+ Framebuffers | ||
+ Element buffer rendering primitives | ||
+ Float textures / framebuffers and their filtering modes | ||
+ Constant attributes | ||
+ Viewport change notifications | ||
+ RAF/frame behavior | ||
+ Complete coverage for all code generation pathways | ||
+ Test weird invocation sequences | ||
+ RAF/regl.frame behavior | ||
+ Initialization pathways | ||
+ General code coverage | ||
* Build a website (preview at [regl.party](http://regl.party)) | ||
* Helper modules | ||
+ A camera helper module to make getting started with 3D code easier | ||
+ Debugging tools for inspecting the state of framebuffers, textures, buffers | ||
* Recipe book/example set | ||
+ Minecraft example | ||
+ Globe | ||
+ Tile based 2D rendering | ||
+ Compound scene | ||
@@ -50,5 +63,22 @@ + Shadow mapping | ||
+ Turing patterns | ||
+ Spring/cloth physics | ||
+ Asset loading (obj, ply, etc.) | ||
## Next | ||
## 0.9.0 | ||
* Add performance monitoring hooks for commands. Now tracks draw call count, cpu time and gpu time (if disjoint timer extension supported). | ||
* Performance monitoring hooks for commands can be enabled/disabled using the `profile` property. | ||
* Finish API documentation for framebuffers | ||
* Optimize constructors/updates for framebuffers | ||
* More test cases for framebuffers | ||
* Clean up renderbuffer constructor and improve test coverage | ||
* Added `resize` method to textures, renderbuffers and framebuffers | ||
* Added global performance monitoring hooks via `regl.stats` | ||
* Rename `count` context variable to `tick` | ||
* Remove `deltaTime` context variable | ||
* Uniform validation fixes | ||
* Texture construction fixes | ||
* Improved test coverage for uniform variables | ||
## 0.8.0 | ||
@@ -55,0 +85,0 @@ |
@@ -47,3 +47,3 @@ var check = require('./util/check') | ||
module.exports = function wrapBufferState (gl) { | ||
module.exports = function wrapBufferState (gl, stats) { | ||
var bufferCount = 0 | ||
@@ -66,2 +66,6 @@ var bufferSet = {} | ||
REGLBuffer.prototype.destroy = function () { | ||
destroy(this) | ||
} | ||
var streamPool = [] | ||
@@ -166,7 +170,7 @@ | ||
function destroy (buffer) { | ||
stats.bufferCount-- | ||
var handle = buffer.buffer | ||
check(handle, 'buffer must not be deleted already') | ||
if (gl.isBuffer(handle)) { | ||
gl.deleteBuffer(handle) | ||
} | ||
gl.deleteBuffer(handle) | ||
buffer.buffer = null | ||
@@ -177,2 +181,4 @@ delete bufferSet[buffer.id] | ||
function createBuffer (options, type, deferInit) { | ||
stats.bufferCount++ | ||
var buffer = new REGLBuffer(type) | ||
@@ -179,0 +185,0 @@ bufferSet[buffer.id] = buffer |
var check = require('./util/check') | ||
var isTypedArray = require('./util/is-typed-array') | ||
var isNDArrayLike = require('./util/is-ndarray') | ||
var values = require('./util/values') | ||
@@ -24,3 +25,6 @@ var primTypes = require('./constants/primitives.json') | ||
module.exports = function wrapElementsState (gl, extensions, bufferState) { | ||
module.exports = function wrapElementsState (gl, extensions, bufferState, stats) { | ||
var elementSet = {} | ||
var elementCount = 0 | ||
var elementTypes = { | ||
@@ -36,2 +40,4 @@ 'uint8': GL_UNSIGNED_BYTE, | ||
function REGLElementBuffer (buffer) { | ||
this.id = elementCount++ | ||
elementSet[this.id] = this | ||
this.buffer = buffer | ||
@@ -144,5 +150,15 @@ this.primType = GL_TRIANGLES | ||
function destroyElements (elements) { | ||
stats.elementsCount-- | ||
check(elements.buffer !== null, 'must not double destroy elements') | ||
delete elementSet[elements.id] | ||
elements.buffer.destroy() | ||
elements.buffer = null | ||
} | ||
function createElements (options) { | ||
var buffer = bufferState.create(null, GL_ELEMENT_ARRAY_BUFFER, true) | ||
var elements = new REGLElementBuffer(buffer._buffer) | ||
stats.elementsCount++ | ||
@@ -247,5 +263,3 @@ function reglElements (options) { | ||
reglElements.destroy = function () { | ||
check(elements.buffer !== null, 'must not double destroy elements') | ||
buffer.destroy() | ||
elements.buffer = null | ||
destroyElements(elements) | ||
} | ||
@@ -266,4 +280,7 @@ | ||
return null | ||
}, | ||
clear: function () { | ||
values(elementSet).forEach(destroyElements) | ||
} | ||
} | ||
} |
@@ -24,2 +24,3 @@ module.exports = function createExtensionCache (gl) { | ||
'ext_srgb', | ||
'ext_disjoint_timer_query', | ||
@@ -26,0 +27,0 @@ 'angle_instanced_arrays', |
@@ -10,3 +10,2 @@ var check = require('./util/check') | ||
var GL_TEXTURE_2D = 0x0DE1 | ||
var GL_TEXTURE_CUBE_MAP = 0x8513 | ||
var GL_TEXTURE_CUBE_MAP_POSITIVE_X = 0x8515 | ||
@@ -19,9 +18,24 @@ | ||
var GL_UNSIGNED_BYTE = 0x1401 | ||
var GL_FLOAT = 0x1406 | ||
var GL_FRAMEBUFFER_COMPLETE = 0x8CD5 | ||
var GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT = 0x8CD6 | ||
var GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT = 0x8CD7 | ||
var GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS = 0x8CD9 | ||
var GL_FRAMEBUFFER_UNSUPPORTED = 0x8CDD | ||
var GL_HALF_FLOAT_OES = 0x8D61 | ||
var GL_RGBA = 0x1908 | ||
var GL_ALPHA = 0x1906 | ||
var GL_RGB = 0x1907 | ||
var GL_LUMINANCE = 0x1909 | ||
var GL_LUMINANCE_ALPHA = 0x190A | ||
var GL_DEPTH_COMPONENT = 0x1902 | ||
var colorTextureFormatEnums = [ | ||
GL_ALPHA, | ||
GL_LUMINANCE, | ||
GL_LUMINANCE_ALPHA, | ||
GL_RGB, | ||
GL_RGBA | ||
] | ||
var GL_RGBA4 = 0x8056 | ||
@@ -32,4 +46,2 @@ var GL_RGB5_A1 = 0x8057 | ||
var GL_STENCIL_INDEX8 = 0x8D48 | ||
var GL_DEPTH_COMPONENT = 0x1902 | ||
var GL_DEPTH_STENCIL = 0x84F9 | ||
@@ -44,8 +56,19 @@ | ||
var GL_FRAMEBUFFER_COMPLETE = 0x8CD5 | ||
var GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT = 0x8CD6 | ||
var GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT = 0x8CD7 | ||
var GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS = 0x8CD9 | ||
var GL_FRAMEBUFFER_UNSUPPORTED = 0x8CDD | ||
var colorRenderbufferFormatEnums = [ | ||
GL_RGBA4, | ||
GL_RGB5_A1, | ||
GL_RGB565, | ||
GL_SRGB8_ALPHA8_EXT, | ||
GL_RGBA16F_EXT, | ||
GL_RGB16F_EXT, | ||
GL_RGBA32F_EXT | ||
] | ||
var statusCode = {} | ||
statusCode[GL_FRAMEBUFFER_COMPLETE] = 'complete' | ||
statusCode[GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT] = 'incomplete attachment' | ||
statusCode[GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS] = 'incomplete dimensions' | ||
statusCode[GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT] = 'incomplete, missing attachment' | ||
statusCode[GL_FRAMEBUFFER_UNSUPPORTED] = 'unsupported' | ||
module.exports = function wrapFBOState ( | ||
@@ -56,3 +79,4 @@ gl, | ||
textureState, | ||
renderbufferState) { | ||
renderbufferState, | ||
stats) { | ||
var framebufferState = { | ||
@@ -64,80 +88,41 @@ current: null, | ||
var statusCode = {} | ||
statusCode[GL_FRAMEBUFFER_COMPLETE] = 'complete' | ||
statusCode[GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT] = 'incomplete attachment' | ||
statusCode[GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS] = 'incomplete dimensions' | ||
statusCode[GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT] = 'incomplete, missing attachment' | ||
statusCode[GL_FRAMEBUFFER_UNSUPPORTED] = 'unsupported' | ||
var colorTextureFormats = ['rgba'] | ||
var colorRenderbufferFormats = ['rgba4', 'rgb565', 'rgb5 a1'] | ||
var colorTextureFormats = { | ||
'rgba': GL_RGBA | ||
} | ||
var colorRenderbufferFormats = { | ||
'rgba4': GL_RGBA4, | ||
'rgb565': GL_RGB565, | ||
'rgb5 a1': GL_RGB5_A1 | ||
} | ||
if (extensions.ext_srgb) { | ||
colorRenderbufferFormats['srgba'] = GL_SRGB8_ALPHA8_EXT | ||
colorRenderbufferFormats.push('srgba') | ||
} | ||
if (extensions.ext_color_buffer_half_float) { | ||
colorRenderbufferFormats['rgba16f'] = GL_RGBA16F_EXT | ||
colorRenderbufferFormats['rgb16f'] = GL_RGB16F_EXT | ||
colorRenderbufferFormats.push('rgba16f', 'rgb16f') | ||
} | ||
if (extensions.webgl_color_buffer_float) { | ||
colorRenderbufferFormats['rgba32f'] = GL_RGBA32F_EXT | ||
colorRenderbufferFormats.push('rgba32f') | ||
} | ||
var depthRenderbufferFormatEnums = [GL_DEPTH_COMPONENT16] | ||
var stencilRenderbufferFormatEnums = [GL_STENCIL_INDEX8] | ||
var depthStencilRenderbufferFormatEnums = [GL_DEPTH_STENCIL] | ||
var depthTextureFormatEnums = [] | ||
var stencilTextureFormatEnums = [] | ||
var depthStencilTextureFormatEnums = [] | ||
if (extensions.webgl_depth_texture) { | ||
depthTextureFormatEnums.push(GL_DEPTH_COMPONENT) | ||
depthStencilTextureFormatEnums.push(GL_DEPTH_STENCIL) | ||
} | ||
var colorFormats = extend(extend({}, | ||
colorTextureFormats), | ||
colorRenderbufferFormats) | ||
var colorTextureFormatEnums = values(colorTextureFormats) | ||
var colorRenderbufferFormatEnums = values(colorRenderbufferFormats) | ||
var highestPrecision = GL_UNSIGNED_BYTE | ||
var colorTypes = { | ||
'uint8': GL_UNSIGNED_BYTE | ||
} | ||
var colorTypes = ['uint8'] | ||
if (extensions.oes_texture_half_float) { | ||
highestPrecision = colorTypes['half float'] = GL_HALF_FLOAT_OES | ||
colorTypes.push('half float') | ||
} | ||
if (extensions.oes_texture_float) { | ||
highestPrecision = colorTypes.float = GL_FLOAT | ||
colorTypes.push('float') | ||
} | ||
colorTypes.best = highestPrecision | ||
var DRAW_BUFFERS = (function () { | ||
var result = new Array(limits.maxDrawbuffers) | ||
for (var i = 0; i <= limits.maxDrawbuffers; ++i) { | ||
var row = result[i] = new Array(i) | ||
for (var j = 0; j < i; ++j) { | ||
row[j] = GL_COLOR_ATTACHMENT0 + j | ||
} | ||
} | ||
return result | ||
})() | ||
function FramebufferAttachment (target, level, texture, renderbuffer) { | ||
function FramebufferAttachment (target, texture, renderbuffer) { | ||
this.target = target | ||
this.level = level | ||
this.texture = texture | ||
this.renderbuffer = renderbuffer | ||
var w = 0 | ||
var h = 0 | ||
if (texture) { | ||
w = texture.width | ||
h = texture.height | ||
} else if (renderbuffer) { | ||
w = renderbuffer.width | ||
h = renderbuffer.height | ||
} | ||
this.width = w | ||
this.height = h | ||
} | ||
@@ -156,30 +141,20 @@ | ||
function incRefAndCheckShape (attachment, framebuffer) { | ||
var width = framebuffer.width | ||
var height = framebuffer.height | ||
function incRefAndCheckShape (attachment, width, height) { | ||
if (!attachment) { | ||
return | ||
} | ||
if (attachment.texture) { | ||
var texture = attachment.texture._texture | ||
var tw = Math.max(1, texture.params.width >> attachment.level) | ||
var th = Math.max(1, texture.params.height >> attachment.level) | ||
width = width || tw | ||
height = height || th | ||
var tw = Math.max(1, texture.width) | ||
var th = Math.max(1, texture.height) | ||
check(tw === width && th === height, | ||
'inconsistent width/height for supplied texture') | ||
check(texture.pollId < 0, | ||
'polling fbo textures not supported') | ||
texture.refCount += 1 | ||
} else { | ||
var renderbuffer = attachment.renderbuffer._renderbuffer | ||
width = width || renderbuffer.width | ||
height = height || renderbuffer.height | ||
check( | ||
renderbuffer.width === width && renderbuffer.height === height, | ||
'inconsistent width/height for renderbuffer') | ||
check( | ||
colorRenderbufferFormatEnums.indexOf(renderbuffer.format) >= 0, | ||
'renderbuffer format not compatible with color channels') | ||
renderbuffer.refCount += 1 | ||
} | ||
framebuffer.width = width | ||
framebuffer.height = height | ||
} | ||
@@ -195,3 +170,3 @@ | ||
attachment.texture._texture.texture, | ||
attachment.level) | ||
0) | ||
} else { | ||
@@ -214,40 +189,4 @@ gl.framebufferRenderbuffer( | ||
function tryUpdateAttachment ( | ||
attachment, | ||
isTexture, | ||
format, | ||
type, | ||
width, | ||
height) { | ||
if (attachment.texture) { | ||
var texture = attachment.texture | ||
if (isTexture) { | ||
texture({ | ||
format: format, | ||
type: type, | ||
width: width, | ||
height: height | ||
}) | ||
texture._texture.refCount += 1 | ||
return true | ||
} | ||
} else { | ||
var renderbuffer = attachment.renderbuffer | ||
if (!isTexture) { | ||
renderbuffer({ | ||
format: format, | ||
width: width, | ||
height: height | ||
}) | ||
renderbuffer._renderbuffer.refCount += 1 | ||
return true | ||
} | ||
} | ||
decRef(attachment) | ||
return false | ||
} | ||
function parseAttachment (attachment) { | ||
var target = GL_TEXTURE_2D | ||
var level = 0 | ||
var texture = null | ||
@@ -259,7 +198,4 @@ var renderbuffer = null | ||
data = attachment.data | ||
if ('level' in attachment) { | ||
level = attachment.level | 0 | ||
} | ||
if ('target' in attachment) { | ||
target = attachment.target | 0 | ||
// target = attachment.target | 0 | ||
} | ||
@@ -270,18 +206,15 @@ } | ||
var type = attachment._reglType | ||
if (type === 'texture') { | ||
texture = attachment | ||
if (texture._texture.target === GL_TEXTURE_CUBE_MAP) { | ||
check( | ||
target >= GL_TEXTURE_CUBE_MAP_POSITIVE_X && | ||
target < GL_TEXTURE_CUBE_MAP_POSITIVE_X + 6, | ||
'invalid cube map target') | ||
} else { | ||
check(target === GL_TEXTURE_2D) | ||
} | ||
// TODO check miplevel is consistent | ||
var type = data._reglType | ||
if (type === 'texture2d') { | ||
texture = data | ||
check(target === GL_TEXTURE_2D) | ||
} else if (type === 'textureCube') { | ||
texture = data | ||
check( | ||
target >= GL_TEXTURE_CUBE_MAP_POSITIVE_X && | ||
target < GL_TEXTURE_CUBE_MAP_POSITIVE_X + 6, | ||
'invalid cube map target') | ||
} else if (type === 'renderbuffer') { | ||
renderbuffer = attachment | ||
renderbuffer = data | ||
target = GL_RENDERBUFFER | ||
level = 0 | ||
} else { | ||
@@ -291,5 +224,31 @@ check.raise('invalid regl object for attachment') | ||
return new FramebufferAttachment(target, level, texture, renderbuffer) | ||
return new FramebufferAttachment(target, texture, renderbuffer) | ||
} | ||
function allocAttachment ( | ||
width, | ||
height, | ||
isTexture, | ||
format, | ||
type) { | ||
if (isTexture) { | ||
var texture = textureState.create2D({ | ||
width: width, | ||
height: height, | ||
format: format, | ||
type: type | ||
}) | ||
texture._texture.refCount = 0 | ||
return new FramebufferAttachment(GL_TEXTURE_2D, texture, null) | ||
} else { | ||
var rb = renderbufferState.create({ | ||
width: width, | ||
height: height, | ||
format: format | ||
}) | ||
rb._renderbuffer.refCount = 0 | ||
return new FramebufferAttachment(GL_RENDERBUFFER, null, rb) | ||
} | ||
} | ||
function unwrapAttachment (attachment) { | ||
@@ -299,2 +258,12 @@ return attachment && (attachment.texture || attachment.renderbuffer) | ||
function resizeAttachment (attachment, w, h) { | ||
if (attachment) { | ||
if (attachment.texture) { | ||
attachment.texture.resize(w, h) | ||
} else if (attachment.renderbuffer) { | ||
attachment.renderbuffer.resize(w, h) | ||
} | ||
} | ||
} | ||
var framebufferCount = 0 | ||
@@ -307,3 +276,3 @@ var framebufferSet = {} | ||
this.framebuffer = null | ||
this.framebuffer = gl.createFramebuffer() | ||
this.width = 0 | ||
@@ -316,16 +285,25 @@ this.height = 0 | ||
this.depthStencilAttachment = null | ||
} | ||
this.ownsColor = false | ||
this.ownsDepthStencil = false | ||
function decFBORefs (framebuffer) { | ||
framebuffer.colorAttachments.forEach(decRef) | ||
decRef(framebuffer.depthAttachment) | ||
decRef(framebuffer.stencilAttachment) | ||
decRef(framebuffer.depthStencilAttachment) | ||
} | ||
function refresh (framebuffer) { | ||
if (!gl.isFramebuffer(framebuffer.framebuffer)) { | ||
framebuffer.framebuffer = gl.createFramebuffer() | ||
} | ||
framebufferState.dirty = true | ||
function destroy (framebuffer) { | ||
var handle = framebuffer.framebuffer | ||
check(handle, 'must not double destroy framebuffer') | ||
gl.deleteFramebuffer(handle) | ||
framebuffer.framebuffer = null | ||
stats.framebufferCount-- | ||
delete framebufferSet[framebuffer.id] | ||
} | ||
function updateFramebuffer (framebuffer) { | ||
var i | ||
gl.bindFramebuffer(GL_FRAMEBUFFER, framebuffer.framebuffer) | ||
var colorAttachments = framebuffer.colorAttachments | ||
for (var i = 0; i < colorAttachments.length; ++i) { | ||
for (i = 0; i < colorAttachments.length; ++i) { | ||
attach(GL_COLOR_ATTACHMENT0 + i, colorAttachments[i]) | ||
@@ -340,7 +318,2 @@ } | ||
if (extensions.webgl_draw_buffers) { | ||
extensions.webgl_draw_buffers.drawBuffersWEBGL( | ||
DRAW_BUFFERS[colorAttachments.length]) | ||
} | ||
// Check status code | ||
@@ -352,26 +325,17 @@ var status = gl.checkFramebufferStatus(GL_FRAMEBUFFER) | ||
} | ||
} | ||
function decFBORefs (framebuffer) { | ||
framebuffer.colorAttachments.forEach(decRef) | ||
decRef(framebuffer.depthAttachment) | ||
decRef(framebuffer.stencilAttachment) | ||
decRef(framebuffer.depthStencilAttachment) | ||
gl.bindFramebuffer(GL_FRAMEBUFFER, framebufferState.next) | ||
framebufferState.current = framebufferState.next | ||
} | ||
function destroy (framebuffer) { | ||
var handle = framebuffer.framebuffer | ||
check(handle, 'must not double destroy framebuffer') | ||
if (gl.isFramebuffer(handle)) { | ||
gl.deleteFramebuffer(handle) | ||
} | ||
} | ||
function createFBO (options) { | ||
function createFBO (a0, a1) { | ||
var framebuffer = new REGLFramebuffer() | ||
stats.framebufferCount++ | ||
function reglFramebuffer (input) { | ||
function reglFramebuffer (a, b) { | ||
var i | ||
var options = input || {} | ||
check(framebufferState.next !== framebuffer, | ||
'can not update framebuffer which is currently in use') | ||
var extDrawBuffers = extensions.webgl_draw_buffers | ||
@@ -381,130 +345,137 @@ | ||
var height = 0 | ||
if ('shape' in options) { | ||
var shape = options.shape | ||
check(Array.isArray(shape) && shape.length >= 2, | ||
'invalid shape for framebuffer') | ||
width = shape[0] | ||
height = shape[1] | ||
} else { | ||
if ('radius' in options) { | ||
width = height = options.radius | ||
} | ||
if ('width' in options) { | ||
width = options.width | ||
} | ||
if ('height' in options) { | ||
height = options.height | ||
} | ||
} | ||
// colorType, numColors | ||
var colorBuffers = null | ||
var ownsColor = false | ||
if ('colorBuffers' in options || 'colorBuffer' in options) { | ||
var colorInputs = options.colorBuffers || options.colorBuffer | ||
if (!Array.isArray(colorInputs)) { | ||
colorInputs = [colorInputs] | ||
} | ||
var needsDepth = true | ||
var needsStencil = true | ||
framebuffer.width = width | ||
framebuffer.height = height | ||
var colorBuffer = null | ||
var colorTexture = true | ||
var colorFormat = 'rgba' | ||
var colorType = 'uint8' | ||
var colorCount = 1 | ||
if (colorInputs.length > 1) { | ||
check(extDrawBuffers, 'multiple render targets not supported') | ||
} | ||
check(colorInputs.length >= 0, | ||
'must specify at least one color attachment') | ||
var depthBuffer = null | ||
var stencilBuffer = null | ||
var depthStencilBuffer = null | ||
var depthStencilTexture = false | ||
// Wrap color attachments | ||
colorBuffers = colorInputs.map(parseAttachment) | ||
if (typeof a === 'number') { | ||
width = a | 0 | ||
height = (b | 0) || width | ||
} else if (!a) { | ||
width = height = 1 | ||
} else { | ||
check.type(a, 'object', 'invalid arguments for framebuffer') | ||
var options = a | ||
// Check head node | ||
for (i = 0; i < colorBuffers.length; ++i) { | ||
var colorAttachment = colorBuffers[i] | ||
check.framebufferFormat( | ||
colorAttachment, | ||
colorTextureFormatEnums, | ||
colorRenderbufferFormatEnums) | ||
incRefAndCheckShape( | ||
colorAttachment, | ||
framebuffer) | ||
if ('shape' in options) { | ||
var shape = options.shape | ||
check(Array.isArray(shape) && shape.length >= 2, | ||
'invalid shape for framebuffer') | ||
width = shape[0] | ||
height = shape[1] | ||
} else { | ||
if ('radius' in options) { | ||
width = height = options.radius | ||
} | ||
if ('width' in options) { | ||
width = options.width | ||
} | ||
if ('height' in options) { | ||
height = options.height | ||
} | ||
} | ||
width = framebuffer.width | ||
height = framebuffer.height | ||
} else { | ||
var colorTexture = true | ||
var colorFormat = 'rgba' | ||
var colorType = 'uint8' | ||
var colorCount = 1 | ||
ownsColor = true | ||
if ('color' in options || | ||
'colors' in options) { | ||
colorBuffer = | ||
options.color || | ||
options.colors | ||
if (Array.isArray(colorBuffer)) { | ||
check( | ||
colorBuffer.length === 1 || extDrawBuffers, | ||
'multiple render targets not supported') | ||
} | ||
} | ||
framebuffer.width = width = width || gl.drawingBufferWidth | ||
framebuffer.height = height = height || gl.drawingBufferHeight | ||
if (!colorBuffer) { | ||
if ('colorCount' in options) { | ||
colorCount = options.colorCount | 0 | ||
check(colorCount > 0, 'invalid color buffer count') | ||
} | ||
if ('format' in options) { | ||
colorFormat = options.format | ||
check.parameter(colorFormat, colorFormats, 'invalid color format') | ||
colorTexture = colorFormat in colorTextureFormats | ||
} | ||
if ('colorTexture' in options) { | ||
colorTexture = !!options.colorTexture | ||
colorFormat = 'rgba4' | ||
} | ||
if ('type' in options) { | ||
check(colorTexture, | ||
'colorType can not be set for renderbuffer targets') | ||
colorType = options.type | ||
check.parameter(colorType, colorTypes, 'invalid color type') | ||
if ('colorType' in options) { | ||
check.oneOf( | ||
options.colorType, colorTypes, | ||
'invalid color type') | ||
colorType = options.colorType | ||
if (!colorTexture) { | ||
if (colorType === 'half float') { | ||
if (extensions.ext_color_buffer_half_float) { | ||
colorFormat = 'rgba16f' | ||
} | ||
} else if (colorType === 'float') { | ||
if (extensions.webgl_color_buffer_float) { | ||
colorFormat = 'rgba32f' | ||
} | ||
} | ||
} | ||
} | ||
if ('colorFormat' in options) { | ||
colorFormat = options.colorFormat | ||
if (colorTextureFormats.indexOf(colorFormat) >= 0) { | ||
colorTexture = true | ||
} else if (colorRenderbufferFormats.indexOf(colorFormat) >= 0) { | ||
colorTexture = false | ||
} else { | ||
if (colorTexture) { | ||
check.oneOf( | ||
options.colorFormat, colorTextureFormats, | ||
'invalid color format for texture') | ||
} else { | ||
check.oneOf( | ||
options.colorFormat, colorRenderbufferFormats, | ||
'invalid color format for renderbuffer') | ||
} | ||
} | ||
} | ||
} | ||
if ('colorCount' in options) { | ||
colorCount = options.colorCount | 0 | ||
check(colorCount >= 0, 'color count must be positive') | ||
if ('depthTexture' in options || 'depthStencilTexture' in options) { | ||
depthStencilTexture = !!(options.depthTexture || | ||
options.depthStencilTexture) | ||
check(!depthStencilTexture || extensions.webgl_depth_texture, | ||
'webgl_depth_texture extension not supported') | ||
} | ||
// Reuse color buffer array if we own it | ||
if (framebuffer.ownsColor) { | ||
colorBuffers = framebuffer.colorAttachments | ||
while (colorBuffers.length > colorCount) { | ||
decRef(colorBuffers.pop()) | ||
if ('depth' in options) { | ||
if (typeof options.depth === 'boolean') { | ||
needsDepth = options.depth | ||
} else { | ||
depthBuffer = options.depth | ||
needsStencil = false | ||
} | ||
} else { | ||
colorBuffers = [] | ||
} | ||
// update buffers in place, remove incompatible buffers | ||
for (i = 0; i < colorBuffers.length; ++i) { | ||
if (!tryUpdateAttachment( | ||
colorBuffers[i], | ||
colorTexture, | ||
colorFormat, | ||
colorType, | ||
width, | ||
height)) { | ||
colorBuffers[i--] = colorBuffers[colorBuffers.length - 1] | ||
colorBuffers.pop() | ||
if ('stencil' in options) { | ||
if (typeof options.stencil === 'boolean') { | ||
needsStencil = options.stencil | ||
} else { | ||
stencilBuffer = options.stencil | ||
needsDepth = false | ||
} | ||
} | ||
// Then append new buffers | ||
while (colorBuffers.length < colorCount) { | ||
if (colorTexture) { | ||
colorBuffers.push(new FramebufferAttachment( | ||
GL_TEXTURE_2D, | ||
0, | ||
textureState.create2D({ | ||
format: colorFormat, | ||
type: colorType, | ||
width: width, | ||
height: height | ||
}, GL_TEXTURE_2D), | ||
null)) | ||
if ('depthStencil' in options) { | ||
if (typeof options.depthStencil === 'boolean') { | ||
needsDepth = needsStencil = options.depthStencil | ||
} else { | ||
colorBuffers.push(new FramebufferAttachment( | ||
GL_RENDERBUFFER, | ||
0, | ||
null, | ||
renderbufferState.create({ | ||
format: colorFormat, | ||
width: width, | ||
height: height | ||
}))) | ||
depthStencilBuffer = options.depthStencil | ||
needsDepth = false | ||
needsStencil = false | ||
} | ||
@@ -514,159 +485,112 @@ } | ||
check(colorBuffers.length > 0, 'must specify at least one color buffer') | ||
// parse attachments | ||
var colorAttachments = null | ||
var depthAttachment = null | ||
var stencilAttachment = null | ||
var depthStencilAttachment = null | ||
framebuffer.width = width | ||
framebuffer.height = height | ||
var depthBuffer = null | ||
var stencilBuffer = null | ||
var depthStencilBuffer = null | ||
var ownsDepthStencil = false | ||
var depthStencilCount = 0 | ||
if ('depthBuffer' in options) { | ||
depthBuffer = parseAttachment(options.depthBuffer) | ||
check.framebufferFormat( | ||
depthBuffer, | ||
depthTextureFormatEnums, | ||
depthRenderbufferFormatEnums) | ||
depthStencilCount += 1 | ||
// Set up color attachments | ||
if (Array.isArray(colorBuffer)) { | ||
colorAttachments = colorBuffer.map(parseAttachment) | ||
} else if (colorBuffer) { | ||
colorAttachments = [parseAttachment(colorBuffer)] | ||
} else { | ||
colorAttachments = new Array(colorCount) | ||
for (i = 0; i < colorCount; ++i) { | ||
colorAttachments[i] = allocAttachment( | ||
width, | ||
height, | ||
colorTexture, | ||
colorFormat, | ||
colorType) | ||
} | ||
} | ||
if ('stencilBuffer' in options) { | ||
stencilBuffer = parseAttachment(options.stencilBuffer) | ||
check.framebufferFormat( | ||
stencilBuffer, | ||
stencilTextureFormatEnums, | ||
stencilRenderbufferFormatEnums) | ||
depthStencilCount += 1 | ||
} | ||
if ('depthStencilBuffer' in options) { | ||
depthStencilBuffer = parseAttachment(options.depthStencilBuffer) | ||
check.framebufferFormat( | ||
depthStencilBuffer, | ||
depthStencilTextureFormatEnums, | ||
depthStencilRenderbufferFormatEnums) | ||
depthStencilCount += 1 | ||
} | ||
if (!(depthBuffer || stencilBuffer || depthStencilBuffer)) { | ||
var depth = true | ||
var stencil = false | ||
var useTexture = false | ||
check(colorAttachments.length <= limits.maxColorAttachments, | ||
'too many color attachments, not supported') | ||
if ('depth' in options) { | ||
depth = !!options.depth | ||
} | ||
if ('stencil' in options) { | ||
stencil = !!options.stencil | ||
} | ||
if ('depthTexture' in options) { | ||
useTexture = !!options.depthTexture | ||
} | ||
width = width || colorAttachments[0].width | ||
height = height || colorAttachments[0].height | ||
var curDepthStencil = | ||
framebuffer.depthAttachment || | ||
framebuffer.stencilAttachment || | ||
framebuffer.depthStencilAttachment | ||
var nextDepthStencil = null | ||
if (depthBuffer) { | ||
depthAttachment = parseAttachment(depthBuffer) | ||
} else if (needsDepth && !needsStencil) { | ||
depthAttachment = allocAttachment( | ||
width, | ||
height, | ||
depthStencilTexture, | ||
'depth', | ||
'uint32') | ||
} | ||
if (depth || stencil) { | ||
ownsDepthStencil = true | ||
if (stencilBuffer) { | ||
stencilAttachment = parseAttachment(stencilBuffer) | ||
} else if (needsStencil && !needsDepth) { | ||
stencilAttachment = allocAttachment( | ||
width, | ||
height, | ||
false, | ||
'stencil', | ||
'uint8') | ||
} | ||
if (useTexture) { | ||
check(extensions.webgl_depth_texture, | ||
'depth texture extension not supported') | ||
var depthTextureFormat | ||
check(depth, 'stencil only textures not supported') | ||
if (stencil) { | ||
depthTextureFormat = 'depth stencil' | ||
} else { | ||
depthTextureFormat = 'depth' | ||
} | ||
if (framebuffer.ownsDepthStencil && curDepthStencil.texture) { | ||
curDepthStencil.texture({ | ||
format: depthTextureFormat, | ||
width: width, | ||
height: height | ||
}) | ||
curDepthStencil.texture._texture.refCount += 1 | ||
nextDepthStencil = curDepthStencil | ||
} else { | ||
nextDepthStencil = new FramebufferAttachment( | ||
GL_TEXTURE_2D, | ||
0, | ||
textureState.create({ | ||
format: depthTextureFormat, | ||
width: width, | ||
height: height | ||
}, GL_TEXTURE_2D), | ||
null) | ||
} | ||
} else { | ||
var depthRenderbufferFormat | ||
if (depth) { | ||
if (stencil) { | ||
depthRenderbufferFormat = 'depth stencil' | ||
} else { | ||
depthRenderbufferFormat = 'depth' | ||
} | ||
} else { | ||
depthRenderbufferFormat = 'stencil' | ||
} | ||
if (framebuffer.ownsDepthStencil && curDepthStencil.renderbuffer) { | ||
curDepthStencil.renderbuffer({ | ||
format: depthRenderbufferFormat, | ||
width: width, | ||
height: height | ||
}) | ||
curDepthStencil.renderbuffer._renderbuffer.refCount += 1 | ||
nextDepthStencil = curDepthStencil | ||
} else { | ||
nextDepthStencil = new FramebufferAttachment( | ||
GL_RENDERBUFFER, | ||
0, | ||
null, | ||
renderbufferState.create({ | ||
format: depthRenderbufferFormat, | ||
width: width, | ||
height: height | ||
})) | ||
} | ||
} | ||
if (depthStencilBuffer) { | ||
depthStencilAttachment = parseAttachment(depthStencilBuffer) | ||
} else if (!depthBuffer && !stencilBuffer && needsStencil && needsDepth) { | ||
depthStencilAttachment = allocAttachment( | ||
width, | ||
height, | ||
depthStencilTexture, | ||
'depth stencil', | ||
'depth stencil') | ||
} | ||
if (depth) { | ||
if (stencil) { | ||
depthStencilBuffer = nextDepthStencil | ||
} else { | ||
depthBuffer = nextDepthStencil | ||
} | ||
} else { | ||
stencilBuffer = nextDepthStencil | ||
} | ||
} | ||
} else { | ||
check(depthStencilCount === 1, | ||
'can specify only one of depth, stencil or depthStencil attachment') | ||
check( | ||
(!!depthBuffer) + (!!stencilBuffer) + (!!depthStencilBuffer) <= 1, | ||
'invalid framebuffer configuration, can specify exactly one depth/stencil attachment') | ||
incRefAndCheckShape( | ||
depthBuffer || | ||
stencilBuffer || | ||
depthStencilBuffer, | ||
framebuffer) | ||
for (i = 0; i < colorAttachments.length; ++i) { | ||
incRefAndCheckShape(colorAttachments[i], width, height) | ||
check(!colorAttachments[i] || | ||
(colorAttachments[i].texture && | ||
colorTextureFormatEnums.indexOf(colorAttachments[i].texture._texture.format) >= 0) || | ||
(colorAttachments[i].renderbuffer && | ||
colorRenderbufferFormatEnums.indexOf(colorAttachments[i].renderbuffer._renderbuffer.format) >= 0), | ||
'framebuffer color attachment ' + i + ' is invalid') | ||
} | ||
incRefAndCheckShape(depthAttachment, width, height) | ||
check(!depthAttachment || | ||
(depthAttachment.texture && | ||
depthAttachment.texture._texture.format === GL_DEPTH_COMPONENT) || | ||
(depthAttachment.renderbuffer && | ||
depthAttachment.renderbuffer._renderbuffer.format === GL_DEPTH_COMPONENT16), | ||
'invalid depth attachment for framebuffer object') | ||
incRefAndCheckShape(stencilAttachment, width, height) | ||
check(!stencilAttachment || | ||
(stencilAttachment.renderbuffer && | ||
stencilAttachment.renderbuffer._renderbuffer.format === GL_STENCIL_INDEX8), | ||
'invalid stencil attachment for framebuffer object') | ||
incRefAndCheckShape(depthStencilAttachment, width, height) | ||
check(!depthStencilAttachment || | ||
(depthStencilAttachment.texture && | ||
depthStencilAttachment.texture._texture.format === GL_DEPTH_STENCIL) || | ||
(depthStencilAttachment.renderbuffer && | ||
depthStencilAttachment.renderbuffer._renderbuffer.format === GL_DEPTH_STENCIL), | ||
'invalid depth-stencil attachment for framebuffer object') | ||
// decrement references | ||
decFBORefs(framebuffer) | ||
framebuffer.colorAttachments = colorBuffers | ||
framebuffer.depthAttachment = depthBuffer | ||
framebuffer.stencilAttachment = stencilBuffer | ||
framebuffer.depthStencilAttachment = depthStencilBuffer | ||
framebuffer.ownsColor = ownsColor | ||
framebuffer.ownsDepthStencil = ownsDepthStencil | ||
framebuffer.width = width | ||
framebuffer.height = height | ||
reglFramebuffer.color = colorBuffers.map(unwrapAttachment) | ||
reglFramebuffer.depth = unwrapAttachment(depthBuffer) | ||
reglFramebuffer.stencil = unwrapAttachment(stencilBuffer) | ||
reglFramebuffer.depthStencil = unwrapAttachment(depthStencilBuffer) | ||
framebuffer.colorAttachments = colorAttachments | ||
framebuffer.depthAttachment = depthAttachment | ||
framebuffer.stencilAttachment = stencilAttachment | ||
framebuffer.depthStencilAttachment = depthStencilAttachment | ||
refresh(framebuffer) | ||
reglFramebuffer.color = colorAttachments.map(unwrapAttachment) | ||
reglFramebuffer.depth = unwrapAttachment(depthAttachment) | ||
reglFramebuffer.stencil = unwrapAttachment(stencilAttachment) | ||
reglFramebuffer.depthStencil = unwrapAttachment(depthStencilAttachment) | ||
@@ -676,11 +600,42 @@ reglFramebuffer.width = framebuffer.width | ||
updateFramebuffer(framebuffer) | ||
return reglFramebuffer | ||
} | ||
reglFramebuffer(options) | ||
function resize (w_, h_) { | ||
check(framebufferState.next !== framebuffer, | ||
'can not resize a framebuffer which is currently in use') | ||
var w = w_ | 0 | ||
var h = (h_ | 0) || w | ||
if (w === framebuffer.width && h === framebuffer.height) { | ||
return reglFramebuffer | ||
} | ||
// resize all buffers | ||
var colorAttachments = framebuffer.colorAttachments | ||
for (var i = 0; i < colorAttachments.length; ++i) { | ||
resizeAttachment(colorAttachments[i], w, h) | ||
} | ||
resizeAttachment(framebuffer.depthAttachment, w, h) | ||
resizeAttachment(framebuffer.stencilAttachment, w, h) | ||
resizeAttachment(framebuffer.depthStencilAttachment, w, h) | ||
framebuffer.width = reglFramebuffer.width = w | ||
framebuffer.height = reglFramebuffer.height = h | ||
updateFramebuffer(framebuffer) | ||
return reglFramebuffer | ||
} | ||
reglFramebuffer(a0, a1) | ||
reglFramebuffer.resize = resize | ||
reglFramebuffer._reglType = 'framebuffer' | ||
reglFramebuffer._framebuffer = framebuffer | ||
reglFramebuffer._destroy = function () { | ||
reglFramebuffer.destroy = function () { | ||
destroy(framebuffer) | ||
decFBORefs(framebuffer) | ||
} | ||
@@ -691,10 +646,2 @@ | ||
function refreshCache () { | ||
values(framebufferSet).forEach(refresh) | ||
} | ||
function clearCache () { | ||
values(framebufferSet).forEach(destroy) | ||
} | ||
return extend(framebufferState, { | ||
@@ -711,5 +658,6 @@ getFramebuffer: function (object) { | ||
create: createFBO, | ||
clear: clearCache, | ||
refresh: refreshCache | ||
clear: function () { | ||
values(framebufferSet).forEach(destroy) | ||
} | ||
}) | ||
} |
@@ -10,14 +10,21 @@ var check = require('./util/check') | ||
function readPixels (input) { | ||
var options = input || {} | ||
// TODO check framebuffer state supports read | ||
var x = 0 | ||
var y = 0 | ||
var width = context.framebufferWidth | ||
var height = context.framebufferHeight | ||
var data = null | ||
if (isTypedArray(input)) { | ||
options = { | ||
data: options | ||
} | ||
data = input | ||
} else if (arguments.length === 2) { | ||
options = { | ||
width: arguments[0] | 0, | ||
height: arguments[1] | 0 | ||
} | ||
} else if (typeof input !== 'object') { | ||
options = {} | ||
width = arguments[0] | 0 | ||
height = arguments[1] | 0 | ||
} else if (input) { | ||
check.type(input, 'object', 'invalid arguments to regl.read()') | ||
x = input.x | 0 | ||
y = input.y | 0 | ||
width = input.width || context.framebufferWidth | ||
height = input.height || context.framebufferHeight | ||
data = input.data || null | ||
} | ||
@@ -28,10 +35,6 @@ | ||
// Read viewport state | ||
var x = options.x || 0 | ||
var y = options.y || 0 | ||
var width = options.width || context.viewportWidth | ||
var height = options.height || context.viewportHeight | ||
// TODO: | ||
// float color buffers | ||
// implementation specific formats | ||
// TODO handle color buffer float | ||
// Compute size | ||
@@ -41,3 +44,3 @@ var size = width * height * 4 | ||
// Allocate data | ||
var data = options.data || new Uint8Array(size) | ||
data = data || new Uint8Array(size) | ||
@@ -44,0 +47,0 @@ // Type check |
@@ -20,3 +20,3 @@ var check = require('./util/check') | ||
module.exports = function (gl, extensions, limits) { | ||
module.exports = function (gl, extensions, limits, stats) { | ||
var formatTypes = { | ||
@@ -47,7 +47,7 @@ 'rgba4': GL_RGBA4, | ||
function REGLRenderbuffer () { | ||
function REGLRenderbuffer (renderbuffer) { | ||
this.id = renderbufferCount++ | ||
this.refCount = 1 | ||
this.renderbuffer = null | ||
this.renderbuffer = renderbuffer | ||
@@ -60,3 +60,3 @@ this.format = GL_RGBA4 | ||
REGLRenderbuffer.prototype.decRef = function () { | ||
if (--this.refCount === 0) { | ||
if (--this.refCount <= 0) { | ||
destroy(this) | ||
@@ -66,14 +66,2 @@ } | ||
function refresh (rb) { | ||
if (!gl.isRenderbuffer(rb.renderbuffer)) { | ||
rb.renderbuffer = gl.createRenderbuffer() | ||
} | ||
gl.bindRenderbuffer(GL_RENDERBUFFER, rb.renderbuffer) | ||
gl.renderbufferStorage( | ||
GL_RENDERBUFFER, | ||
rb.format, | ||
rb.width, | ||
rb.height) | ||
} | ||
function destroy (rb) { | ||
@@ -83,56 +71,104 @@ var handle = rb.renderbuffer | ||
gl.bindRenderbuffer(GL_RENDERBUFFER, null) | ||
if (gl.isRenderbuffer(handle)) { | ||
gl.deleteRenderbuffer(handle) | ||
} | ||
gl.deleteRenderbuffer(handle) | ||
rb.renderbuffer = null | ||
rb.refCount = 0 | ||
delete renderbufferSet[rb.id] | ||
stats.renderbufferCount-- | ||
} | ||
function createRenderbuffer (input) { | ||
var renderbuffer = new REGLRenderbuffer() | ||
function createRenderbuffer (a, b) { | ||
var renderbuffer = new REGLRenderbuffer(gl.createRenderbuffer()) | ||
renderbufferSet[renderbuffer.id] = renderbuffer | ||
stats.renderbufferCount++ | ||
function reglRenderbuffer (input) { | ||
var options = input || {} | ||
function reglRenderbuffer (a, b) { | ||
var w = 0 | ||
var h = 0 | ||
if ('shape' in options) { | ||
var shape = options.shape | ||
check(Array.isArray(shape) && shape.length >= 2, | ||
'invalid renderbuffer shape') | ||
w = shape[0] | 0 | ||
h = shape[1] | 0 | ||
} else { | ||
if ('radius' in options) { | ||
w = h = options.radius | 0 | ||
var format = GL_RGBA4 | ||
if (typeof a === 'object' && a) { | ||
var options = a | ||
if ('shape' in options) { | ||
var shape = options.shape | ||
check(Array.isArray(shape) && shape.length >= 2, | ||
'invalid renderbuffer shape') | ||
w = shape[0] | 0 | ||
h = shape[1] | 0 | ||
} else { | ||
if ('radius' in options) { | ||
w = h = options.radius | 0 | ||
} | ||
if ('width' in options) { | ||
w = options.width | 0 | ||
} | ||
if ('height' in options) { | ||
h = options.height | 0 | ||
} | ||
} | ||
if ('width' in options) { | ||
w = options.width | 0 | ||
if ('format' in options) { | ||
check.parameter(options.format, formatTypes, | ||
'invalid renderbuffer format') | ||
format = formatTypes[options.format] | ||
} | ||
if ('height' in options) { | ||
h = options.height | 0 | ||
} else if (typeof a === 'number') { | ||
w = a | 0 | ||
if (typeof b === 'number') { | ||
h = b | 0 | ||
} else { | ||
h = w | ||
} | ||
} else if (!a) { | ||
w = h = 1 | ||
} else { | ||
check.raise('invalid arguments to renderbuffer constructor') | ||
} | ||
var s = limits.maxRenderbufferSize | ||
check(w >= 0 && h >= 0 && w <= s && h <= s, | ||
// check shape | ||
check( | ||
w > 0 && h > 0 && | ||
w <= limits.maxRenderbufferSize && h <= limits.maxRenderbufferSize, | ||
'invalid renderbuffer size') | ||
reglRenderbuffer.width = renderbuffer.width = Math.max(w, 1) | ||
reglRenderbuffer.height = renderbuffer.height = Math.max(h, 1) | ||
renderbuffer.format = GL_RGBA4 | ||
if ('format' in options) { | ||
var format = options.format | ||
check.parameter(format, formatTypes, 'invalid render buffer format') | ||
renderbuffer.format = formatTypes[format] | ||
if (w === renderbuffer.width && | ||
h === renderbuffer.height && | ||
format === renderbuffer.format) { | ||
return | ||
} | ||
refresh(renderbuffer) | ||
reglRenderbuffer.width = renderbuffer.width = w | ||
reglRenderbuffer.height = renderbuffer.height = h | ||
renderbuffer.format = format | ||
gl.bindRenderbuffer(GL_RENDERBUFFER, renderbuffer.renderbuffer) | ||
gl.renderbufferStorage(GL_RENDERBUFFER, format, w, h) | ||
return reglRenderbuffer | ||
} | ||
reglRenderbuffer(input) | ||
function resize (w_, h_) { | ||
var w = w_ | 0 | ||
var h = (h_ | 0) || w | ||
if (w === renderbuffer.width && h === renderbuffer.height) { | ||
return reglRenderbuffer | ||
} | ||
// check shape | ||
check( | ||
w > 0 && h > 0 && | ||
w <= limits.maxRenderbufferSize && h <= limits.maxRenderbufferSize, | ||
'invalid renderbuffer size') | ||
reglRenderbuffer.width = renderbuffer.width = w | ||
reglRenderbuffer.height = renderbuffer.height = h | ||
gl.bindRenderbuffer(GL_RENDERBUFFER, renderbuffer.renderbuffer) | ||
gl.renderbufferStorage(GL_RENDERBUFFER, renderbuffer.format, w, h) | ||
return reglRenderbuffer | ||
} | ||
reglRenderbuffer(a, b) | ||
reglRenderbuffer.resize = resize | ||
reglRenderbuffer._reglType = 'renderbuffer' | ||
@@ -147,15 +183,8 @@ reglRenderbuffer._renderbuffer = renderbuffer | ||
function refreshRenderbuffers () { | ||
values(renderbufferSet).forEach(refresh) | ||
} | ||
function destroyRenderbuffers () { | ||
values(renderbufferSet).forEach(destroy) | ||
} | ||
return { | ||
create: createRenderbuffer, | ||
refresh: refreshRenderbuffers, | ||
clear: destroyRenderbuffers | ||
clear: function () { | ||
values(renderbufferSet).forEach(destroy) | ||
} | ||
} | ||
} |
@@ -10,3 +10,3 @@ var check = require('./util/check') | ||
module.exports = function wrapShaderState (gl, stringStore) { | ||
module.exports = function wrapShaderState (gl, stringStore, stats) { | ||
// =================================================== | ||
@@ -145,8 +145,4 @@ // glsl compilation and linking | ||
programCache = {} | ||
}, | ||
refresh: function () { | ||
fragShaders = {} | ||
vertShaders = {} | ||
programList.forEach(linkProgram) | ||
stats.shaderCount = 0 | ||
}, | ||
@@ -158,2 +154,4 @@ | ||
stats.shaderCount++ | ||
var cache = programCache[fragId] | ||
@@ -160,0 +158,0 @@ if (!cache) { |
@@ -8,2 +8,3 @@ var check = require('./util/check') | ||
var convertToHalfFloat = require('./util/to-half-float') | ||
var isArrayLike = require('./util/is-array-like') | ||
@@ -154,14 +155,6 @@ var dtypes = require('./constants/arraytypes.json') | ||
} | ||
var width = arr.length | ||
if (width === 0 || !Array.isArray(arr[0])) { | ||
if (width === 0 || !isArrayLike(arr[0])) { | ||
return false | ||
} | ||
var height = arr[0].length | ||
for (var i = 1; i < width; ++i) { | ||
if (!Array.isArray(arr[i]) || arr[i].length !== height) { | ||
return false | ||
} | ||
} | ||
return true | ||
@@ -300,3 +293,3 @@ } | ||
module.exports = function createTextureSet ( | ||
gl, extensions, limits, reglPoll, contextState) { | ||
gl, extensions, limits, reglPoll, contextState, stats) { | ||
// ------------------------------------------------------- | ||
@@ -708,3 +701,3 @@ // Initialize constants and parameter tables here | ||
var c = 1 | ||
if (Array.isArray(data[0][0])) { | ||
if (isArrayLike(data[0][0])) { | ||
c = data[0][0].length | ||
@@ -724,3 +717,3 @@ flatten3DData(image, data, w, h, c) | ||
if (image.type === GL_FLOAT) { | ||
check(limits.extenstions.indexOf('oes_texture_float') >= 0, | ||
check(limits.extensions.indexOf('oes_texture_float') >= 0, | ||
'oes_texture_float extension not enabled') | ||
@@ -823,2 +816,3 @@ } else if (image.type === GL_HALF_FLOAT_OES) { | ||
imgData = mipmap.images[0] = allocImage() | ||
copyFlags(imgData, mipmap) | ||
parseImage(imgData, options) | ||
@@ -1018,2 +1012,3 @@ mipmap.mipmask = 1 | ||
this.mipmask = 0 | ||
this.internalformat = GL_RGBA | ||
@@ -1061,2 +1056,3 @@ this.id = textureCount++ | ||
delete textureSet[texture.id] | ||
stats.textureCount-- | ||
} | ||
@@ -1097,3 +1093,3 @@ | ||
decRef: function () { | ||
if (--this.refCount === 0) { | ||
if (--this.refCount <= 0) { | ||
destroy(this) | ||
@@ -1107,2 +1103,3 @@ } | ||
textureSet[texture.id] = texture | ||
stats.textureCount++ | ||
@@ -1136,2 +1133,3 @@ function reglTexture2D (a, b) { | ||
check.texture2D(texInfo, mipData, limits) | ||
texture.internalformat = mipData.internalformat | ||
@@ -1193,5 +1191,32 @@ reglTexture2D.width = mipData.width | ||
function resize (w_, h_) { | ||
var w = w_ | 0 | ||
var h = (h_ | 0) || w | ||
if (w === texture.width && h === texture.height) { | ||
return reglTexture2D | ||
} | ||
reglTexture2D.width = texture.width = w | ||
reglTexture2D.height = texture.height = h | ||
tempBind(texture) | ||
gl.texImage2D( | ||
GL_TEXTURE_2D, | ||
0, | ||
texture.format, | ||
w, | ||
h, | ||
0, | ||
texture.format, | ||
texture.type, | ||
null) | ||
tempRestore() | ||
return reglTexture2D | ||
} | ||
reglTexture2D(a, b) | ||
reglTexture2D.subimage = subimage | ||
reglTexture2D.resize = resize | ||
reglTexture2D._reglType = 'texture2d' | ||
@@ -1209,2 +1234,3 @@ reglTexture2D._texture = texture | ||
textureSet[texture.id] = texture | ||
stats.cubeCount++ | ||
@@ -1264,2 +1290,3 @@ var faces = new Array(6) | ||
check.textureCube(texture, texInfo, faces, limits) | ||
texture.internalformat = faces[0].internalformat | ||
@@ -1328,5 +1355,34 @@ reglTextureCube.width = faces[0].width | ||
function resize (w_, h_) { | ||
var w = w_ | 0 | ||
var h = (h_ | 0) || w | ||
if (w === texture.width && h === texture.height) { | ||
return reglTextureCube | ||
} | ||
reglTextureCube.width = texture.width = w | ||
reglTextureCube.height = texture.height = h | ||
tempBind(texture) | ||
for (var i = 0; i < 6; ++i) { | ||
gl.texImage2D( | ||
GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, | ||
0, | ||
texture.format, | ||
w, | ||
h, | ||
0, | ||
texture.format, | ||
texture.type, | ||
null) | ||
} | ||
tempRestore() | ||
return reglTextureCube | ||
} | ||
reglTextureCube(a0, a1, a2, a3, a4, a5) | ||
reglTextureCube.subimage = subimage | ||
reglTextureCube.resize = resize | ||
reglTextureCube._reglType = 'textureCube' | ||
@@ -1349,2 +1405,5 @@ reglTextureCube._texture = texture | ||
values(textureSet).forEach(destroy) | ||
stats.cubeCount = 0 | ||
stats.textureCount = 0 | ||
} | ||
@@ -1351,0 +1410,0 @@ |
@@ -104,3 +104,3 @@ // Error checking and parameter validation. | ||
var error = new Error() | ||
var stack = error.stack.toString() | ||
var stack = (error.stack || error).toString() | ||
var pat = /compileProcedure.*\n\s*at.*\((.*)\)/.exec(stack) | ||
@@ -119,3 +119,3 @@ if (pat) { | ||
var error = new Error() | ||
var stack = error.stack | ||
var stack = (error.stack || error).toString() | ||
var pat = /at REGLCommand.*\n\s+at.*\((.*)\)/.exec(stack) | ||
@@ -377,3 +377,3 @@ if (pat) { | ||
checkOneOf( | ||
attachment.texture._texture.params.internalformat, | ||
attachment.texture._texture.internalformat, | ||
texFormats, | ||
@@ -380,0 +380,0 @@ 'unsupported texture format for attachment') |
@@ -167,2 +167,3 @@ var extend = require('./extend') | ||
var proc = Function.apply(null, linkedNames.concat(src)) | ||
// console.log('src: ', src) | ||
return proc.apply(null, linkedValues) | ||
@@ -169,0 +170,0 @@ } |
{ | ||
"name": "regl", | ||
"version": "0.8.0", | ||
"description": "WebGL", | ||
"version": "0.9.0", | ||
"description": "Regl is a fast functional reactive abstraction for WebGL.", | ||
"main": "regl.js", | ||
@@ -19,2 +19,3 @@ "directories": { | ||
"canvas-orbit-camera": "^1.0.2", | ||
"control-panel": "^1.2.0", | ||
"falafel": "^1.2.0", | ||
@@ -30,2 +31,3 @@ "faucet": "0.0.1", | ||
"indexhtmlify": "^1.2.1", | ||
"install": "^0.8.1", | ||
"istanbul": "^0.4.3", | ||
@@ -36,2 +38,4 @@ "mkdirp": "^0.5.1", | ||
"parse-dds": "^1.2.1", | ||
"primitive-sphere": "^2.0.0", | ||
"regl-stats-widget": "0.0.1", | ||
"resl": "^1.0.0", | ||
@@ -47,2 +51,3 @@ "runscript": "^1.1.0", | ||
"scripts": { | ||
"mytest": "standard | snazzy && tape test/stats.js", | ||
"test": "standard | snazzy && tape test/util/index.js | faucet", | ||
@@ -49,0 +54,0 @@ "test-browser": "budo test/util/browser.js --open", |
445
README.md
@@ -61,3 +61,3 @@ # regl | ||
// regl.frame() wraps requestAnimationFrame and also handles viewport changes | ||
regl.frame(() => { | ||
regl.frame(({time}) => { | ||
// clear contents of the drawing buffer | ||
@@ -72,5 +72,5 @@ regl.clear({ | ||
color: [ | ||
Math.cos(Date.now() * 0.001), | ||
Math.sin(Date.now() * 0.0008), | ||
Math.cos(Date.now() * 0.003), | ||
Math.cos(time * 0.001), | ||
Math.sin(time * 0.0008), | ||
Math.cos(time * 0.003), | ||
1 | ||
@@ -116,10 +116,435 @@ ] | ||
**TODO** implement spinning textured cube in each of the following frameworks | ||
In this section, we show how you can implement a spinning textured cube in `regl`, and compare it with other WebGL frameworks. | ||
* vs WebGL | ||
* vs gl-* modules from stack.gl | ||
* gl-react | ||
* vs TWGL | ||
* vs THREE.js | ||
![](images/cube_example.png) | ||
#### [`regl`](https://mikolalysenko.github.io/regl/www/gallery/cube.js.html) | ||
```javascript | ||
const regl = require('../regl')() | ||
const mat4 = require('gl-mat4') | ||
var cubePosition = [ | ||
[-0.5, +0.5, +0.5], [+0.5, +0.5, +0.5], [+0.5, -0.5, +0.5], [-0.5, -0.5, +0.5], // positive z face. | ||
[+0.5, +0.5, +0.5], [+0.5, +0.5, -0.5], [+0.5, -0.5, -0.5], [+0.5, -0.5, +0.5], // positive x face | ||
[+0.5, +0.5, -0.5], [-0.5, +0.5, -0.5], [-0.5, -0.5, -0.5], [+0.5, -0.5, -0.5], // negative z face | ||
[-0.5, +0.5, -0.5], [-0.5, +0.5, +0.5], [-0.5, -0.5, +0.5], [-0.5, -0.5, -0.5], // negative x face. | ||
[-0.5, +0.5, -0.5], [+0.5, +0.5, -0.5], [+0.5, +0.5, +0.5], [-0.5, +0.5, +0.5], // top face | ||
[-0.5, -0.5, -0.5], [+0.5, -0.5, -0.5], [+0.5, -0.5, +0.5], [-0.5, -0.5, +0.5] // bottom face | ||
] | ||
var cubeUv = [ | ||
[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0], // positive z face. | ||
[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0], // positive x face. | ||
[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0], // negative z face. | ||
[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0], // negative x face. | ||
[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0], // top face | ||
[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0] // bottom face | ||
] | ||
const cubeElements = [ | ||
[2, 1, 0], [2, 0, 3], // positive z face. | ||
[6, 5, 4], [6, 4, 7], // positive x face. | ||
[10, 9, 8], [10, 8, 11], // negative z face. | ||
[14, 13, 12], [14, 12, 15], // negative x face. | ||
[18, 17, 16], [18, 16, 19], // top face. | ||
[20, 21, 22], [23, 20, 22] // bottom face | ||
] | ||
const drawCube = regl({ | ||
frag: ` | ||
precision mediump float; | ||
varying vec2 vUv; | ||
uniform sampler2D tex; | ||
void main () { | ||
gl_FragColor = texture2D(tex,vUv); | ||
}`, | ||
vert: ` | ||
precision mediump float; | ||
attribute vec3 position; | ||
attribute vec2 uv; | ||
varying vec2 vUv; | ||
uniform mat4 projection, view; | ||
void main() { | ||
vUv = uv; | ||
gl_Position = projection * view * vec4(position, 1); | ||
}`, | ||
attributes: { | ||
position: cubePosition, | ||
uv: cubeUv | ||
}, | ||
elements: cubeElements, | ||
uniforms: { | ||
view: ({tick}) => { | ||
const t = 0.01 * tick | ||
return mat4.lookAt([], | ||
[5 * Math.cos(t), 2.5 * Math.sin(t), 5 * Math.sin(t)], | ||
[0, 0.0, 0], | ||
[0, 1, 0]) | ||
}, | ||
projection: ({viewportWidth, viewportHeight}) => | ||
mat4.perspective([], | ||
Math.PI / 4, | ||
viewportWidth / viewportHeight, | ||
0.01, | ||
10), | ||
tex: regl.prop('texture') | ||
} | ||
}) | ||
require('resl')({ | ||
manifest: { | ||
texture: { | ||
type: 'image', | ||
src: 'assets/lena.png', | ||
parser: (data) => regl.texture({ | ||
data: data, | ||
mag: 'linear', | ||
min: 'linear' | ||
}) | ||
} | ||
}, | ||
onDone: ({texture}) => { | ||
regl.frame(() => { | ||
regl.clear({ | ||
color: [0, 0, 0, 255], | ||
depth: 1 | ||
}) | ||
drawCube({texture}) | ||
}) | ||
} | ||
}) | ||
``` | ||
#### [Raw WebGL](https://mikolalysenko.github.io/regl/compare/webgl_cube.html) | ||
```html | ||
<!doctype html> | ||
<html> | ||
<head> | ||
<title>WebGL Textured Cube Demo</title> | ||
<script type="text/javascript"> | ||
/* global Image, alert, requestAnimationFrame */ | ||
var canvas | ||
var gl | ||
var cubePositionBuffer | ||
var cubeUvBuffer | ||
var cubeElementsBuffers | ||
var cubeTexture | ||
var shaderProgram | ||
var cubePositionAttribute | ||
var cubeUvAttribute | ||
var projectionUniformLocation | ||
var viewUniformLocation | ||
var tick | ||
function start () { | ||
canvas = document.getElementById('glcanvas') | ||
tick = 0 | ||
initWebGL(canvas) | ||
if (gl) { | ||
gl.clearColor(0.0, 0.0, 0.0, 1.0) | ||
gl.enable(gl.DEPTH_TEST) | ||
gl.enable(gl.CULL_FACE) | ||
initShaders() | ||
initBuffers() | ||
initTextures() | ||
// start RAF | ||
requestAnimationFrame(drawScene) | ||
} | ||
} | ||
function initWebGL () { | ||
try { | ||
gl = canvas.getContext('experimental-webgl') | ||
} catch (e) { | ||
} | ||
if (!gl) { | ||
alert('Unable to initialize WebGL. Your browser may not support it.') | ||
} | ||
} | ||
function initBuffers () { | ||
cubePositionBuffer = gl.createBuffer() | ||
gl.bindBuffer(gl.ARRAY_BUFFER, cubePositionBuffer) | ||
var cubePosition = [ | ||
// positive z face. | ||
-0.5, +0.5, +0.5, | ||
+0.5, +0.5, +0.5, | ||
+0.5, -0.5, +0.5, | ||
-0.5, -0.5, +0.5, | ||
// positive x face | ||
+0.5, +0.5, +0.5, | ||
+0.5, +0.5, -0.5, | ||
+0.5, -0.5, -0.5, | ||
+0.5, -0.5, +0.5, | ||
// negative z face | ||
+0.5, +0.5, -0.5, | ||
-0.5, +0.5, -0.5, | ||
-0.5, -0.5, -0.5, | ||
+0.5, -0.5, -0.5, | ||
// negative x face. | ||
-0.5, +0.5, -0.5, | ||
-0.5, +0.5, +0.5, | ||
-0.5, -0.5, +0.5, | ||
-0.5, -0.5, -0.5, | ||
// top face | ||
-0.5, +0.5, -0.5, | ||
+0.5, +0.5, -0.5, | ||
+0.5, +0.5, +0.5, | ||
-0.5, +0.5, +0.5, | ||
// bottom face | ||
-0.5, -0.5, -0.5, | ||
+0.5, -0.5, -0.5, | ||
+0.5, -0.5, +0.5, | ||
-0.5, -0.5, +0.5 | ||
] | ||
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(cubePosition), gl.STATIC_DRAW) | ||
cubeUvBuffer = gl.createBuffer() | ||
gl.bindBuffer(gl.ARRAY_BUFFER, cubeUvBuffer) | ||
var cubeUv = [ | ||
// positive z face. | ||
0.0, 0.0, | ||
1.0, 0.0, | ||
1.0, 1.0, | ||
0.0, 1.0, | ||
// positive x face. | ||
0.0, 0.0, | ||
1.0, 0.0, | ||
1.0, 1.0, | ||
0.0, 1.0, | ||
// negative z face. | ||
0.0, 0.0, | ||
1.0, 0.0, | ||
1.0, 1.0, | ||
0.0, 1.0, | ||
// negative x face. | ||
0.0, 0.0, | ||
1.0, 0.0, | ||
1.0, 1.0, | ||
0.0, 1.0, | ||
// top face | ||
0.0, 0.0, | ||
1.0, 0.0, | ||
1.0, 1.0, | ||
0.0, 1.0, | ||
// bottom face | ||
0.0, 0.0, | ||
1.0, 0.0, | ||
1.0, 1.0, | ||
0.0, 1.0 | ||
] | ||
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(cubeUv), gl.STATIC_DRAW) | ||
cubeElementsBuffers = gl.createBuffer() | ||
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeElementsBuffers) | ||
var cubeElements = [ | ||
// positive z face. | ||
2, 1, 0, | ||
2, 0, 3, | ||
// positive x face. | ||
6, 5, 4, | ||
6, 4, 7, | ||
// negative z face. | ||
10, 9, 8, | ||
10, 8, 11, | ||
// negative x face. | ||
14, 13, 12, | ||
14, 12, 15, | ||
// top face. | ||
18, 17, 16, | ||
18, 16, 19, | ||
// bottom face | ||
20, 21, 22, | ||
23, 20, 22 | ||
] | ||
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(cubeElements), gl.STATIC_DRAW) | ||
} | ||
function initTextures () { | ||
cubeTexture = gl.createTexture() | ||
var cubeImage = new Image() | ||
cubeImage.onload = function () { | ||
gl.bindTexture(gl.TEXTURE_2D, cubeTexture) | ||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, cubeImage) | ||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR) | ||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR) | ||
} | ||
cubeImage.src = '../example/assets/lena.png' | ||
} | ||
function drawScene () { | ||
tick++ | ||
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT) | ||
// bind buffers. | ||
gl.bindBuffer(gl.ARRAY_BUFFER, cubePositionBuffer) | ||
gl.vertexAttribPointer(cubePositionAttribute, 3, gl.FLOAT, false, 0, 0) | ||
gl.bindBuffer(gl.ARRAY_BUFFER, cubeUvBuffer) | ||
gl.vertexAttribPointer(cubeUvAttribute, 2, gl.FLOAT, false, 0, 0) | ||
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeElementsBuffers) | ||
// set texture. | ||
gl.activeTexture(gl.TEXTURE0) | ||
gl.bindTexture(gl.TEXTURE_2D, cubeTexture) | ||
// set uniforms | ||
gl.uniform1i(gl.getUniformLocation(shaderProgram, 'tex'), 0) | ||
const t = 0.01 * tick | ||
var perspectiveMatrix = perspective(45, 640.0 / 480.0, 0.1, 100.0) | ||
gl.uniformMatrix4fv(projectionUniformLocation, false, new Float32Array(perspectiveMatrix)) | ||
gl.uniformMatrix4fv(viewUniformLocation, false, new Float32Array( | ||
lookAt( | ||
[5 * Math.cos(t), 2.5 * Math.sin(t), 5 * Math.sin(t)], | ||
[0, 0.0, 0], | ||
[0, 1, 0]))) | ||
gl.drawElements(gl.TRIANGLES, 36, gl.UNSIGNED_SHORT, 0) | ||
requestAnimationFrame(drawScene) | ||
} | ||
function initShaders () { | ||
var fragmentShader = getShader(gl, 'shader-fs') | ||
var vertexShader = getShader(gl, 'shader-vs') | ||
shaderProgram = gl.createProgram() | ||
gl.attachShader(shaderProgram, vertexShader) | ||
gl.attachShader(shaderProgram, fragmentShader) | ||
gl.linkProgram(shaderProgram) | ||
if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { | ||
alert('Unable to initialize the shader program: ' + gl.getProgramInfoLog(shaderProgram)) | ||
} | ||
gl.useProgram(shaderProgram) | ||
cubePositionAttribute = gl.getAttribLocation(shaderProgram, 'position') | ||
gl.enableVertexAttribArray(cubePositionAttribute) | ||
cubeUvAttribute = gl.getAttribLocation(shaderProgram, 'uv') | ||
gl.enableVertexAttribArray(cubeUvAttribute) | ||
projectionUniformLocation = gl.getUniformLocation(shaderProgram, 'projection') | ||
viewUniformLocation = gl.getUniformLocation(shaderProgram, 'view') | ||
} | ||
function getShader (gl, id) { | ||
var e = document.getElementById(id) | ||
if (!e) { | ||
return null | ||
} | ||
var sourceCode = e.firstChild.textContent | ||
var shader | ||
if (e.type === 'x-shader/x-fragment') { | ||
shader = gl.createShader(gl.FRAGMENT_SHADER) | ||
} else if (e.type === 'x-shader/x-vertex') { | ||
shader = gl.createShader(gl.VERTEX_SHADER) | ||
} else { | ||
return null | ||
} | ||
gl.shaderSource(shader, sourceCode) | ||
gl.compileShader(shader) | ||
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { | ||
alert('An error occurred compiling the shaders: ' + gl.getShaderInfoLog(shader)) | ||
return null | ||
} | ||
return shader | ||
} | ||
// Taken from gl-mat4 | ||
// https://github.com/stackgl/gl-mat4/blob/master/lookAt.js | ||
function lookAt (eye, center, up) { | ||
var x0, x1, x2, y0, y1, y2, z0, z1, z2, len | ||
var eyex = eye[0] | ||
var eyey = eye[1] | ||
var eyez = eye[2] | ||
var upx = up[0] | ||
var upy = up[1] | ||
var upz = up[2] | ||
var centerx = center[0] | ||
var centery = center[1] | ||
var centerz = center[2] | ||
z0 = eyex - centerx | ||
z1 = eyey - centery | ||
z2 = eyez - centerz | ||
len = 1 / Math.sqrt(z0 * z0 + z1 * z1 + z2 * z2) | ||
z0 *= len | ||
z1 *= len | ||
z2 *= len | ||
x0 = upy * z2 - upz * z1 | ||
x1 = upz * z0 - upx * z2 | ||
x2 = upx * z1 - upy * z0 | ||
len = Math.sqrt(x0 * x0 + x1 * x1 + x2 * x2) | ||
if (!len) { | ||
x0 = 0 | ||
x1 = 0 | ||
x2 = 0 | ||
} else { | ||
len = 1 / len | ||
x0 *= len | ||
x1 *= len | ||
x2 *= len | ||
} | ||
y0 = z1 * x2 - z2 * x1 | ||
y1 = z2 * x0 - z0 * x2 | ||
y2 = z0 * x1 - z1 * x0 | ||
len = Math.sqrt(y0 * y0 + y1 * y1 + y2 * y2) | ||
if (!len) { | ||
y0 = 0 | ||
y1 = 0 | ||
y2 = 0 | ||
} else { | ||
len = 1 / len | ||
y0 *= len | ||
y1 *= len | ||
y2 *= len | ||
} | ||
return [ | ||
x0, y0, z0, 0, | ||
x1, y1, z1, 0, | ||
x2, y2, z2, 0, | ||
-(x0 * eyex + x1 * eyey + x2 * eyez), | ||
-(y0 * eyex + y1 * eyey + y2 * eyez), | ||
-(z0 * eyex + z1 * eyey + z2 * eyez), | ||
1 | ||
] | ||
} | ||
// Taken from gl-mat4 | ||
// https://github.com/stackgl/gl-mat4/blob/master/perspective.js | ||
function perspective (fovy, aspect, near, far) { | ||
var f = 1.0 / Math.tan(fovy / 2) | ||
var nf = 1 / (near - far) | ||
return [ | ||
f / aspect, 0.0, 0.0, 0.0, | ||
0.0, f, 0.0, 0.0, | ||
0.0, 0.0, (far + near) * nf, -1.0, | ||
0.0, 0.0, (2 * far * near) * nf, 0.0 | ||
] | ||
} | ||
</script> | ||
<script id="shader-fs" type="x-shader/x-fragment"> | ||
precision mediump float; | ||
varying vec2 vUv; | ||
uniform sampler2D tex; | ||
void main () { | ||
gl_FragColor = texture2D(tex,vUv); | ||
} | ||
</script> | ||
<script id="shader-vs" type="x-shader/x-vertex"> | ||
precision mediump float; | ||
attribute vec3 position; | ||
attribute vec2 uv; | ||
varying vec2 vUv; | ||
uniform mat4 projection, view; | ||
void main() { | ||
vUv = uv; | ||
gl_Position = projection * view * vec4(position, 1); | ||
} | ||
</script> | ||
</head> | ||
<body onload="start()"> | ||
<canvas id="glcanvas" width="640" height="480"> | ||
Your browser doesn't appear to support the <code><canvas></code> element. | ||
</canvas> | ||
</body> | ||
</html> | ||
``` | ||
#### [stack.gl]() | ||
TODO | ||
#### [gl-react]() | ||
TODO | ||
#### [TWGL]() | ||
TODO | ||
#### [three.js]() | ||
TODO | ||
### Benchmarks | ||
@@ -126,0 +551,0 @@ You can run benchmarks locally using `npm run bench` or check them out here: |
99
regl.js
@@ -19,2 +19,4 @@ var check = require('./lib/util/check') | ||
var createCore = require('./lib/core') | ||
var createStats = require('./lib/stats') | ||
var createTimer = require('./lib/timer') | ||
@@ -40,8 +42,9 @@ var GL_COLOR_BUFFER_BIT = 16384 | ||
var stringStore = createStringStore() | ||
var stats = createStats() | ||
var extensionState = wrapExtensions(gl) | ||
var extensions = extensionState.extensions | ||
var timer = createTimer(gl, extensions) | ||
var START_TIME = clock() | ||
var LAST_TIME = START_TIME | ||
var WIDTH = gl.drawingBufferWidth | ||
@@ -51,4 +54,3 @@ var HEIGHT = gl.drawingBufferHeight | ||
var contextState = { | ||
count: 0, | ||
deltaTime: 0, | ||
tick: 0, | ||
time: 0, | ||
@@ -73,4 +75,4 @@ viewportWidth: WIDTH, | ||
var limits = wrapLimits(gl, extensions) | ||
var bufferState = wrapBuffers(gl) | ||
var elementState = wrapElements(gl, extensions, bufferState) | ||
var bufferState = wrapBuffers(gl, stats) | ||
var elementState = wrapElements(gl, extensions, bufferState, stats) | ||
var attributeState = wrapAttributes( | ||
@@ -82,3 +84,3 @@ gl, | ||
stringStore) | ||
var shaderState = wrapShaders(gl, stringStore) | ||
var shaderState = wrapShaders(gl, stringStore, stats) | ||
var textureState = wrapTextures( | ||
@@ -89,4 +91,5 @@ gl, | ||
poll, | ||
contextState) | ||
var renderbufferState = wrapRenderbuffers(gl, extensions, limits) | ||
contextState, | ||
stats) | ||
var renderbufferState = wrapRenderbuffers(gl, extensions, limits, stats) | ||
var framebufferState = wrapFramebuffers( | ||
@@ -97,3 +100,4 @@ gl, | ||
textureState, | ||
renderbufferState) | ||
renderbufferState, | ||
stats) | ||
var readPixels = wrapRead(gl, poll, contextState) | ||
@@ -114,3 +118,4 @@ | ||
drawState, | ||
contextState) | ||
contextState, | ||
timer) | ||
@@ -127,11 +132,11 @@ var nextState = core.next | ||
// increment frame count | ||
contextState.count += 1 | ||
contextState.tick += 1 | ||
var now = clock() | ||
contextState.deltaTime = (now - LAST_TIME) / 1000.0 | ||
contextState.time = (now - START_TIME) / 1000.0 | ||
LAST_TIME = now | ||
// Update time | ||
contextState.time = (clock() - START_TIME) / 1000.0 | ||
refresh() | ||
// poll for changes | ||
poll() | ||
// fire a callback for all pending rafs | ||
for (var i = 0; i < rafCallbacks.length; ++i) { | ||
@@ -141,2 +146,8 @@ var cb = rafCallbacks[i] | ||
} | ||
// flush all pending webgl calls | ||
gl.flush() | ||
if (timer) { | ||
timer.update() | ||
} | ||
} | ||
@@ -182,4 +193,9 @@ | ||
textureState.clear() | ||
elementState.clear() | ||
bufferState.clear() | ||
if (timer) { | ||
timer.clear() | ||
} | ||
if (options.onDestroy) { | ||
@@ -243,4 +259,10 @@ options.onDestroy() | ||
var compiled = core.compile(opts, attributes, uniforms, context) | ||
var stats = { | ||
gpuTime: 0.0, | ||
cpuTime: 0.0, | ||
count: 0 | ||
} | ||
var compiled = core.compile(opts, attributes, uniforms, context, stats) | ||
var draw = compiled.draw | ||
@@ -289,7 +311,7 @@ var batch = compiled.batch | ||
return REGLCommand | ||
} | ||
return extend(REGLCommand, { | ||
stats: stats | ||
}) | ||
function poll () { | ||
core.procs.poll() | ||
// return REGLCommand | ||
} | ||
@@ -303,5 +325,4 @@ | ||
var clearFlags = 0 | ||
core.procs.poll() | ||
poll() | ||
var c = options.color | ||
@@ -350,10 +371,9 @@ if (c) { | ||
function refresh () { | ||
// reset viewport | ||
// poll viewport | ||
function pollViewport () { | ||
var viewport = nextState.viewport | ||
var scissorBox = nextState.scissor_box | ||
viewport[0] = viewport[1] = scissorBox[0] = scissorBox[1] = 0 | ||
contextState.viewportWidth = | ||
contextState.frameBufferWidth = | ||
contextState.framebufferWidth = | ||
contextState.drawingBufferWidth = | ||
@@ -363,8 +383,19 @@ viewport[2] = | ||
contextState.viewportHeight = | ||
contextState.frameBufferWidth = | ||
contextState.framebufferHeight = | ||
contextState.drawingBufferHeight = | ||
viewport[3] = | ||
scissorBox[3] = gl.drawingBufferHeight | ||
} | ||
function poll () { | ||
pollViewport() | ||
core.procs.poll() | ||
} | ||
function refresh () { | ||
pollViewport() | ||
core.procs.refresh() | ||
if (timer) { | ||
timer.update() | ||
} | ||
} | ||
@@ -419,4 +450,14 @@ | ||
_gl: gl, | ||
_refresh: refresh | ||
_refresh: refresh, | ||
poll: function () { | ||
core.procs.poll() | ||
if (timer) { | ||
timer.update() | ||
} | ||
}, | ||
// regl Statistics Information | ||
stats: stats | ||
}) | ||
} |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
2439754
101
14848
650
35