regl
Advanced tools
Comparing version 0.2.0 to 0.3.0
126
API.md
@@ -917,3 +917,2 @@ # REGL API | ||
--------------------------------------- | ||
@@ -1109,4 +1108,4 @@ ### Types | ||
| `poll` | If set, then each frame check if this texture needs to be reuploaded | Depends on the element type | | ||
| `data` | Image data for the texture | | ||
| `crossOrigin` | Cross origin resource sharing URL | | ||
| `data` | Image data for the texture | `null` | | ||
| `crossOrigin` | Cross origin resource sharing URL | `null` | | ||
@@ -1168,6 +1167,6 @@ * `shape` can be used as an array shortcut for `[width, height, channels]` of image | ||
| `'rgba arc interpolated alpha'` | `ext.COMPRESSED_RGBA_ATC_INTERPOLATED_ALPHA_WEBGL` | 4 | `'uint8'` | ✓ | [WEBGL_compressed_texture_atc](https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_atc/) | | ||
| 'rgb pvrtc 4bppv1' | `ext.COMPRESSED_RGB_PVRTC_4BPPV1_IMG` | 3 | `'uint8'` | ✓ | [WEBGL_compressed_texture_pvrtc](https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_pvrtc/) | | ||
| 'rgb pvrtc 2bppv1' | `ext.COMPRESSED_RGB_PVRTC_2BPPV1_IMG` | 3 | `'uint8'` | ✓ | [WEBGL_compressed_texture_pvrtc](https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_pvrtc/) | | ||
| 'rgba pvrtc 4bppv1' | `ext.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG` | 4 | `'uint8'` | ✓ | [WEBGL_compressed_texture_pvrtc](https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_pvrtc/) | | ||
| 'rgba pvrtc 2bppv1' | `ext.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG` | 4 | `'uint8'` | ✓ | [WEBGL_compressed_texture_pvrtc](https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_pvrtc/) | | ||
| `'rgb pvrtc 4bppv1'` | `ext.COMPRESSED_RGB_PVRTC_4BPPV1_IMG` | 3 | `'uint8'` | ✓ | [WEBGL_compressed_texture_pvrtc](https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_pvrtc/) | | ||
| `'rgb pvrtc 2bppv1'` | `ext.COMPRESSED_RGB_PVRTC_2BPPV1_IMG` | 3 | `'uint8'` | ✓ | [WEBGL_compressed_texture_pvrtc](https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_pvrtc/) | | ||
| `'rgba pvrtc 4bppv1'` | `ext.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG` | 4 | `'uint8'` | ✓ | [WEBGL_compressed_texture_pvrtc](https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_pvrtc/) | | ||
| `'rgba pvrtc 2bppv1'` | `ext.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG` | 4 | `'uint8'` | ✓ | [WEBGL_compressed_texture_pvrtc](https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_pvrtc/) | | ||
| `'rgb etc1'` | `ext.COMPRESSED_RGB_ETC1_WEBGL` | 3 | `'uint8'` | ✓ | [WEBGL_compressed_texture_etc1](https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_etc1/) | | ||
@@ -1223,9 +1222,89 @@ | ||
**NOT YET IMPLEMENTED** | ||
Example, | ||
```javascript | ||
var rb = regl.renderbuffer({ | ||
width: 16, | ||
height: 16, | ||
format: 'rgba4' | ||
}) | ||
``` | ||
| Property | Interpretation | Default | | ||
|----------|----------------|---------| | ||
| `'format'` | Sets the internal format of the render buffer | `'rgba4'` | | ||
| `'width'` | Sets the width of the render buffer in pixels | `1` | | ||
| `'height'` | Sets the height of the render buffer in pixels | `1` | | ||
| Format | Description | | ||
|--------|-------------| | ||
| `'rgba4'` | `gl.RGBA4` | | ||
| `'rgb565'` | `gl.RGB565` | | ||
| `'rgb5 a1'` | `gl.RGB5_A1` | | ||
| `'depth'` | `gl.DEPTH_COMPONENT16` | | ||
| `'stencil'` | `gl.STENCIL_INDEX8` | | ||
| `'srgba'` | `ext.SRGB8_ALPHA8_EXT`, only if [EXT_sRGB](https://www.khronos.org/registry/webgl/extensions/EXT_sRGB/) supported | | ||
**Relevant WebGL APIs** | ||
* [`gl.createRenderbuffer`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glCreateRenderbuffer.xml) | ||
* [`gl.deleteRenderbuffer`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glDeleteRenderbuffer.xml) | ||
* [`gl.renderbufferStorage`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glRenderbufferStorage.xml) | ||
* [`gl.bindRenderbuffer`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glBindRenderbuffer.xml) | ||
--------------------------------------- | ||
#### Frame buffers | ||
Example, | ||
**NOT YET IMPLEMENTED** | ||
```javascript | ||
var fbo = regl.framebuffer({ | ||
width: 256, | ||
height: 256, | ||
depth: true, | ||
stencil: true | ||
}) | ||
``` | ||
| Property | Description | Default | | ||
|----------|-------------|---------| | ||
| `width` | Sets the width of the framebuffer | `gl.drawingBufferWidth` | | ||
| `height` | Sets the height of the framebuffer | `gl.drawingBufferHeight` | | ||
| `format` | Sets the format of the color buffer | `'rgba'` | | ||
| `type` | Sets the type of the color buffer if it is a texture | `'uint8'` | | ||
| `colorCount` | Sets the number of color buffers | `1` | | ||
| `depth` | Toggles whether or not a depth buffer is included | `true` | | ||
| `stencil` | Toggles whether or not to use a stencil buffer | `false` | | ||
| `depthTexture` | Toggles whether depth/stencil attachments should be in texture | `false` | | ||
| `colorBuffers` | List of color buffers | | | ||
| `depthBuffer` | | | | ||
| `stencilBuffer` | | | | ||
| `depthStencilBuffer` | | | | ||
| Color format | Description | Attachment | | ||
|--------------|-------------|------------| | ||
| `'alpha'` | `gl.ALPHA` | Texture | | ||
| `'luminance'` | `gl.LUMINANCE` | Texture | | ||
| `'luminance alpha'` | `gl.LUMINANCE_ALPHA` | Texture | | ||
| `'rgb'` | `gl.RGB` | Texture | | ||
| `'rgba'` | `gl.RGBA` | Texture | | ||
| `'rgba4'` | `gl.RGBA4` | Renderbuffer | | ||
| `'rgb565'` | `gl.RGB565` | Renderbuffer | | ||
| `'rgb5 a1'` | `gl.RGB5_A1` | Renderbuffer | | ||
| Color type | Description | | ||
|------------|-------------| | ||
| `'best'` | Highest available precision | | ||
| `'uint8'` | `gl.UNSIGNED_BYTE` | | ||
| `'half float'` | 16 bit float | | ||
| `'float'` | 32 bit float` | | ||
**Relevant WebGL APIs** | ||
* [`gl.createFramebuffer`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glCreateFramebuffer.xml) | ||
* [`gl.deleteFramebuffer`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glDeleteFramebuffer.xml) | ||
* [`gl.framebufferRenderbuffer`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glFramebufferRenderbuffer.xml) | ||
* [`gl.framebufferTexture2D`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glFramebufferTexture2D.xml) | ||
* [`gl.bindFramebuffer`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glBindFramebuffer.xml) | ||
--------------------------------------- | ||
@@ -1321,15 +1400,18 @@ ## Other features | ||
| `extensions` | A list of all supported extensions | | ||
| `pointSizeRange` | `gl.ALIASED_POINT_SIZE_RANGE` | | ||
| `lineWidthRange` | `gl.ALIASED_LINE_WIDTH_RANGE` | | ||
| `viewport` | `gl.MAX_VIEWPORT_DIMS` | | ||
| `combinedTextureUnits` | `gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS` | | ||
| `cubeMapSize` | `gl.MAX_CUBE_MAP_TEXTURE_SIZE` | | ||
| `renderbufferSize` | `gl.MAX_RENDERBUFFER_SIZE` | | ||
| `texUnits` | `gl.MAX_TEXTURE_IMAGE_UNITS` | | ||
| `textureSize` | `gl.MAX_TEXTURE_SIZE` | | ||
| `attributes` | `gl.MAX_VERTEX_ATTRIBS` | | ||
| `vertexUniforms` | `gl.MAX_VERTEX_UNIFORM_VECTORS` | | ||
| `vertexTextureUnits` | `gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS` | | ||
| `varyingVectors` | `gl.MAX_VARYING_VECTORS` | | ||
| `fragmentUniforms` | `gl.MAX_FRAGMENT_UNIFORM_VECTORS` | | ||
| `maxAnisotropic` | Maximum number of anisotropic filtering samples | | ||
| `maxDrawbuffers` | Maximum number of draw buffers | | ||
| `maxColorAttachments` | Maximum number of color attachments | | ||
| `pointSizeDims` | `gl.ALIASED_POINT_SIZE_RANGE` | | ||
| `lineWidthDims` | `gl.ALIASED_LINE_WIDTH_RANGE` | | ||
| `maxViewportDims` | `gl.MAX_VIEWPORT_DIMS` | | ||
| `maxCombinedTextureUnits` | `gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS` | | ||
| `maxCubeMapSize` | `gl.MAX_CUBE_MAP_TEXTURE_SIZE` | | ||
| `maxRenderbufferSize` | `gl.MAX_RENDERBUFFER_SIZE` | | ||
| `maxTextureUnits` | `gl.MAX_TEXTURE_IMAGE_UNITS` | | ||
| `maxTextureSize` | `gl.MAX_TEXTURE_SIZE` | | ||
| `maxAttributes` | `gl.MAX_VERTEX_ATTRIBS` | | ||
| `maxVertexUniforms` | `gl.MAX_VERTEX_UNIFORM_VECTORS` | | ||
| `maxVertexTextureUnits` | `gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS` | | ||
| `maxVaryingVectors` | `gl.MAX_VARYING_VECTORS` | | ||
| `maxFragmentUniforms` | `gl.MAX_FRAGMENT_UNIFORM_VECTORS` | | ||
| `glsl` | `gl.SHADING_LANGUAGE_VERSION` | | ||
@@ -1336,0 +1418,0 @@ | `renderer` | `gl.RENDERER` | |
# Release notes | ||
## Next | ||
* added renderbuffers (via regl.renderbuffer) | ||
* added framebuffer objects (via regl.framebuffer) | ||
* regl.buffer and regl.elements can now take ndarray-like inputs | ||
* Switch to using Google closure compiler for minified builds | ||
## 0.2.0 | ||
@@ -4,0 +11,0 @@ |
@@ -61,8 +61,6 @@ var glTypes = require('./constants/dtypes.json') | ||
module.exports = function wrapAttributeState (gl, extensionState, bufferState) { | ||
var extensions = extensionState.extensions | ||
module.exports = function wrapAttributeState (gl, extensions, limits, bufferState) { | ||
var attributeState = {} | ||
var NUM_ATTRIBUTES = gl.getParameter(gl.MAX_VERTEX_ATTRIBS) | ||
var NUM_ATTRIBUTES = limits.maxAttributes | ||
var attributeBindings = new Array(NUM_ATTRIBUTES) | ||
@@ -69,0 +67,0 @@ for (var i = 0; i < NUM_ATTRIBUTES; ++i) { |
// Array and element buffer creation | ||
var check = require('./check') | ||
var isTypedArray = require('./is-typed-array') | ||
var isNDArrayLike = require('./is-ndarray') | ||
var arrayTypes = require('./constants/arraytypes.json') | ||
var bufferTypes = require('./constants/dtypes.json') | ||
var values = require('./values') | ||
var GL_STATIC_DRAW = 35044 | ||
var GL_BYTE = 5120 | ||
var GL_UNSIGNED_BYTE = 5121 | ||
var GL_STATIC_DRAW = 35044 | ||
var GL_SHORT = 5122 | ||
var GL_UNSIGNED_SHORT = 5123 | ||
var GL_INT = 5124 | ||
var GL_UNSIGNED_INT = 5125 | ||
var GL_FLOAT = 5126 | ||
@@ -16,4 +25,28 @@ | ||
function flatten (data, dimension) { | ||
var result = new Float32Array(data.length * dimension) | ||
function typedArrayCode (data) { | ||
return arrayTypes[Object.prototype.toString.call(data)] | 0 | ||
} | ||
function makeTypedArray (dtype, args) { | ||
switch (dtype) { | ||
case GL_UNSIGNED_BYTE: | ||
return new Uint8Array(args) | ||
case GL_UNSIGNED_SHORT: | ||
return new Uint16Array(args) | ||
case GL_UNSIGNED_INT: | ||
return new Uint32Array(args) | ||
case GL_BYTE: | ||
return new Int8Array(args) | ||
case GL_SHORT: | ||
return new Int16Array(args) | ||
case GL_INT: | ||
return new Int32Array(args) | ||
case GL_FLOAT: | ||
return new Float32Array(args) | ||
default: | ||
return null | ||
} | ||
} | ||
function flatten (result, data, dimension) { | ||
var ptr = 0 | ||
@@ -26,2 +59,11 @@ for (var i = 0; i < data.length; ++i) { | ||
} | ||
} | ||
function transpose (result, data, shapeX, shapeY, strideX, strideY, offset) { | ||
var ptr = 0 | ||
for (var i = 0; i < shapeX; ++i) { | ||
for (var j = 0; j < shapeY; ++j) { | ||
result[ptr++] = data[strideX * i + strideY * j + offset] | ||
} | ||
} | ||
return result | ||
@@ -45,9 +87,35 @@ } | ||
Object.assign(REGLBuffer.prototype, { | ||
bind: function () { | ||
gl.bindBuffer(this.type, this.buffer) | ||
}, | ||
REGLBuffer.prototype.bind = function () { | ||
gl.bindBuffer(this.type, this.buffer) | ||
} | ||
update: function (options) { | ||
if (Array.isArray(options) || isTypedArray(options)) { | ||
function refresh (buffer) { | ||
if (!gl.isBuffer(buffer.buffer)) { | ||
buffer.buffer = gl.createBuffer() | ||
} | ||
buffer.bind() | ||
gl.bufferData(buffer.type, buffer.data || buffer.byteLength, buffer.usage) | ||
} | ||
function destroy (buffer) { | ||
var handle = buffer.buffer | ||
check(handle, 'buffer must not be deleted already') | ||
if (gl.isBuffer(handle)) { | ||
gl.deleteBuffer(handle) | ||
} | ||
buffer.buffer = null | ||
delete bufferSet[buffer.id] | ||
} | ||
function createBuffer (options, type, deferInit) { | ||
var handle = gl.createBuffer() | ||
var buffer = new REGLBuffer(handle, type) | ||
bufferSet[buffer.id] = buffer | ||
function reglBuffer (input) { | ||
var options = input || {} | ||
if (Array.isArray(options) || | ||
isTypedArray(options) || | ||
isNDArrayLike(options)) { | ||
options = { | ||
@@ -70,68 +138,87 @@ data: options | ||
var usage = options.usage | ||
check.parameter(usage, usageTypes, 'buffer usage') | ||
this.usage = usageTypes[options.usage] | ||
check.parameter(usage, usageTypes, 'invalid buffer usage') | ||
buffer.usage = usageTypes[options.usage] | ||
} else { | ||
buffer.usage = GL_STATIC_DRAW | ||
} | ||
var dtype = 0 | ||
if ('type' in options) { | ||
check.parameter(options.type, bufferTypes, 'invalid buffer type') | ||
dtype = bufferTypes[options.type] | ||
} | ||
var dimension = (options.dimension | 0) || 1 | ||
var byteLength = 0 | ||
var data = null | ||
if ('data' in options) { | ||
var data = options.data | ||
data = options.data | ||
if (data === null) { | ||
this.byteLength = options.length | 0 | ||
this.dtype = GL_UNSIGNED_BYTE | ||
byteLength = options.length | 0 | ||
} else { | ||
if (Array.isArray(data)) { | ||
if (isNDArrayLike(data)) { | ||
var shape = data.shape | ||
var stride = data.stride | ||
var offset = data.offset | ||
var shapeX = 0 | ||
var shapeY = 0 | ||
var strideX = 0 | ||
var strideY = 0 | ||
if (shape.length === 1) { | ||
shapeX = shape[0] | ||
shapeY = 1 | ||
strideX = stride[0] | ||
strideY = 0 | ||
} else if (shape.length === 2) { | ||
shapeX = shape[0] | ||
shapeY = shape[1] | ||
strideX = stride[0] | ||
strideY = stride[1] | ||
} else { | ||
check.raise('invalid shape') | ||
} | ||
dtype = dtype || typedArrayCode(data) || GL_FLOAT | ||
dimension = shapeY | ||
data = transpose( | ||
makeTypedArray(dtype, shapeX * shapeY), | ||
data.data, | ||
shapeX, shapeY, | ||
strideX, strideY, | ||
offset) | ||
} else if (Array.isArray(data)) { | ||
if (data.length > 0 && Array.isArray(data[0])) { | ||
dimension = data[0].length | ||
data = flatten(data, dimension) | ||
this.dtype = GL_FLOAT | ||
dtype = dtype || GL_FLOAT | ||
var result = makeTypedArray(dtype, data.length * dimension) | ||
data = flatten(result, data, dimension) | ||
data = result | ||
} else { | ||
data = new Float32Array(data) | ||
this.dtype = GL_FLOAT | ||
dtype = dtype || GL_FLOAT | ||
data = makeTypedArray(dtype, data) | ||
} | ||
} else { | ||
check.isTypedArray(data, 'invalid data type buffer data') | ||
this.dtype = arrayTypes[Object.prototype.toString.call(data)] | ||
dtype = dtype || typedArrayCode(data) | ||
} | ||
this.dimension = dimension | ||
this.byteLength = data.byteLength | ||
byteLength = data.byteLength | ||
} | ||
this.data = data | ||
} else if ('length' in options) { | ||
var byteLength = options.length | ||
byteLength = options.length | 0 | ||
check.nni(byteLength, 'buffer length must be a nonnegative integer') | ||
this.data = null | ||
this.byteLength = options.length | 0 | ||
this.dtype = GL_UNSIGNED_BYTE | ||
} | ||
this.bind() | ||
gl.bufferData(this.type, this.data || this.byteLength, this.usage) | ||
}, | ||
buffer.data = data | ||
buffer.dtype = dtype || GL_UNSIGNED_BYTE | ||
buffer.byteLength = byteLength | ||
buffer.dimension = dimension | ||
refresh: function () { | ||
if (!gl.isBuffer(this.buffer)) { | ||
this.buffer = gl.createBuffer() | ||
} | ||
this.update({}) | ||
}, | ||
refresh(buffer) | ||
destroy: function () { | ||
check(this.buffer, 'buffer must not be deleted already') | ||
gl.deleteBuffer(this.buffer) | ||
this.buffer = null | ||
delete bufferSet[this.id] | ||
return reglBuffer | ||
} | ||
}) | ||
function createBuffer (options, type) { | ||
options = options || {} | ||
var handle = gl.createBuffer() | ||
var buffer = new REGLBuffer(handle, type) | ||
buffer.update(options) | ||
bufferSet[buffer.id] = buffer | ||
function reglBuffer (options) { | ||
buffer.update(options || {}) | ||
return reglBuffer | ||
if (!deferInit) { | ||
reglBuffer(options) | ||
} | ||
@@ -141,3 +228,3 @@ | ||
reglBuffer._buffer = buffer | ||
reglBuffer.destroy = function () { buffer.destroy() } | ||
reglBuffer.destroy = function () { destroy(buffer) } | ||
@@ -151,11 +238,7 @@ return reglBuffer | ||
clear: function () { | ||
Object.keys(bufferSet).forEach(function (bufferId) { | ||
bufferSet[bufferId].destroy() | ||
}) | ||
values(bufferSet).forEach(destroy) | ||
}, | ||
refresh: function () { | ||
Object.keys(bufferSet).forEach(function (bufferId) { | ||
bufferSet[bufferId].refresh() | ||
}) | ||
values(bufferSet).forEach(refresh) | ||
}, | ||
@@ -162,0 +245,0 @@ |
@@ -24,32 +24,36 @@ // Error checking and parameter validation | ||
function checkParameter (param, possibilities, message) { | ||
check(param in possibilities, | ||
'unknown parameter (' + param + ')' + encolon(message) + | ||
'. possible values: ' + Object.keys(possibilities).join()) | ||
if (!(param in possibilities)) { | ||
raise('unknown parameter (' + param + ')' + encolon(message) + | ||
'. possible values: ' + Object.keys(possibilities).join()) | ||
} | ||
} | ||
function checkIsTypedArray (data, message) { | ||
check( | ||
isTypedArray(data), | ||
'invalid parameter type' + encolon(message) + | ||
'. must be a typed array') | ||
if (!isTypedArray(data)) { | ||
raise( | ||
'invalid parameter type' + encolon(message) + | ||
'. must be a typed array') | ||
} | ||
} | ||
function checkTypeOf (value, type, message) { | ||
check(typeof value === type, | ||
'invalid parameter type' + encolon(message) + | ||
'. expected ' + type + ', got ' + (typeof value)) | ||
if (typeof value !== type) { | ||
raise( | ||
'invalid parameter type' + encolon(message) + | ||
'. expected ' + type + ', got ' + (typeof value)) | ||
} | ||
} | ||
function checkNonNegativeInt (value, message) { | ||
check( | ||
(value >= 0) && | ||
((value | 0) === value), | ||
'invalid parameter type, (' + value + ')' + encolon(message) + | ||
'. must be a nonnegative integer') | ||
if (!((value >= 0) && | ||
((value | 0) === value))) { | ||
raise('invalid parameter type, (' + value + ')' + encolon(message) + | ||
'. must be a nonnegative integer') | ||
} | ||
} | ||
function checkOneOf (value, list, message) { | ||
check( | ||
list.indexOf(value) >= 0, | ||
'invalid value' + encolon(message) + '. must be one of: ' + list) | ||
if (list.indexOf(value) < 0) { | ||
raise('invalid value' + encolon(message) + '. must be one of: ' + list) | ||
} | ||
} | ||
@@ -56,0 +60,0 @@ |
@@ -173,9 +173,23 @@ var check = require('./check') | ||
// Need to process framebuffer first in options list | ||
function optionPriority (a, b) { | ||
if (a === 'framebuffer') { | ||
return -1 | ||
} | ||
if (a < b) { | ||
return -1 | ||
} else if (a > b) { | ||
return 1 | ||
} | ||
return 0 | ||
} | ||
module.exports = function reglCompiler ( | ||
gl, | ||
extensionState, | ||
extensions, | ||
limits, | ||
bufferState, | ||
elementState, | ||
textureState, | ||
fboState, | ||
framebufferState, | ||
glState, | ||
@@ -186,4 +200,4 @@ uniformState, | ||
drawState, | ||
frameState) { | ||
var extensions = extensionState.extensions | ||
frameState, | ||
reglPoll) { | ||
var contextState = glState.contextState | ||
@@ -339,2 +353,3 @@ | ||
var FRAME_STATE = link(frameState) | ||
var FRAMEBUFFER_STATE = link(framebufferState) | ||
var DRAW_STATE = { | ||
@@ -346,2 +361,3 @@ count: link(drawState.count), | ||
} | ||
var CONTEXT_STATE = {} | ||
var ELEMENTS = link(elementState.elements) | ||
@@ -361,2 +377,11 @@ var CUR_COUNT = def(stackTop(DRAW_STATE.count)) | ||
function linkContext (x) { | ||
var result = CONTEXT_STATE[x] | ||
if (result) { | ||
return result | ||
} | ||
result = CONTEXT_STATE[x] = link(contextState[x]) | ||
return result | ||
} | ||
// ------------------------------- | ||
@@ -445,3 +470,3 @@ // batch/argument vars | ||
'if(', CUR_ELEMENTS, '){', | ||
GL, '.bindBuffer(', GL_ELEMENT_ARRAY_BUFFER, ',', CUR_ELEMENTS, '.buffer._buffer.buffer);', | ||
GL, '.bindBuffer(', GL_ELEMENT_ARRAY_BUFFER, ',', CUR_ELEMENTS, '.buffer.buffer);', | ||
'}else{', | ||
@@ -462,3 +487,3 @@ GL, '.bindBuffer(', GL_ELEMENT_ARRAY_BUFFER, ',null);', | ||
// ------------------------------- | ||
Object.keys(options).forEach(function (option) { | ||
Object.keys(options).sort(optionPriority).forEach(function (option) { | ||
var VALUE = dyn(options[option]) | ||
@@ -474,2 +499,14 @@ | ||
switch (option) { | ||
case 'framebuffer': | ||
var VIEWPORT_STATE = linkContext('viewport') | ||
var SCISSOR_STATE = linkContext('scissor.box') | ||
batch( | ||
'if(', FRAMEBUFFER_STATE, '.push(', | ||
VALUE, '&&', VALUE, '._framebuffer)){', | ||
FRAMEBUFFER_STATE, '.poll();', | ||
VIEWPORT_STATE, '.setDirty();', | ||
SCISSOR_STATE, '.setDirty();', | ||
'}') | ||
break | ||
// Caps | ||
@@ -608,19 +645,9 @@ case 'cull.enable': | ||
case 'scissor.box': | ||
var SCISSOR_X = batch.def(VALUE + '.x||0') | ||
var SCISSOR_Y = batch.def(VALUE + '.y||0') | ||
batch(GL, '.scissor(', | ||
SCISSOR_X, ',', | ||
SCISSOR_Y, ',', | ||
'"w" in ', VALUE, '?', VALUE, '.w:', GL, '.drawingBufferWidth-', SCISSOR_X, ',', | ||
'"h" in ', VALUE, '?', VALUE, '.h:', GL, '.drawingBufferHeight-', SCISSOR_Y, ');') | ||
break | ||
case 'viewport': | ||
var VIEWPORT_X = batch.def(VALUE + '.x||0') | ||
var VIEWPORT_Y = batch.def(VALUE + '.y||0') | ||
batch(GL, '.viewport(', | ||
VIEWPORT_X, ',', | ||
VIEWPORT_Y, ',', | ||
'"w" in ', VALUE, '?', VALUE, '.w:', GL, '.drawingBufferWidth-', VIEWPORT_X, ',', | ||
'"h" in ', VALUE, '?', VALUE, '.h:', GL, '.drawingBufferHeight-', VIEWPORT_Y, ');') | ||
var BOX_STATE = linkContext(option) | ||
batch(BOX_STATE, '.push(', | ||
VALUE, '.x||0,', | ||
VALUE, '.y||0,', | ||
VALUE, '.w||-1,', | ||
VALUE, '.h||-1);') | ||
break | ||
@@ -639,2 +666,13 @@ | ||
// update viewport/scissor box state and restore framebuffer | ||
if ('viewport' in options || 'framebuffer' in options) { | ||
batch(linkContext('viewport'), '.poll();') | ||
} | ||
if ('scissor.box' in options || 'framebuffer' in options) { | ||
batch(linkContext('scissor.box'), '.poll();') | ||
} | ||
if ('framebuffer' in options) { | ||
batch(FRAMEBUFFER_STATE, '.pop();') | ||
} | ||
// ------------------------------- | ||
@@ -725,3 +763,3 @@ // set dynamic uniforms | ||
GL_ELEMENT_ARRAY_BUFFER, ',', | ||
CUR_ELEMENTS, '.buffer._buffer.buffer);') | ||
CUR_ELEMENTS, '.buffer.buffer);') | ||
} | ||
@@ -804,6 +842,7 @@ if (instancing) { | ||
// ------------------------------- | ||
var GL_POLL = link(glState.poll) | ||
var GL_POLL = link(reglPoll) | ||
var FRAG_SHADER_STATE = link(shaderState.fragShaders) | ||
var VERT_SHADER_STATE = link(shaderState.vertShaders) | ||
var PROGRAM_STATE = link(shaderState.programs) | ||
var FRAMEBUFFER_STATE = link(framebufferState) | ||
var DRAW_STATE = { | ||
@@ -847,3 +886,3 @@ count: link(drawState.count), | ||
var hasShader = false | ||
Object.keys(staticOptions).forEach(function (param) { | ||
Object.keys(staticOptions).sort(optionPriority).forEach(function (param) { | ||
var value = staticOptions[param] | ||
@@ -863,2 +902,18 @@ switch (param) { | ||
case 'framebuffer': | ||
var fbo = framebufferState.getFramebuffer(value) | ||
check(value === null || fbo, 'invalid framebuffer object') | ||
var VIEWPORT_STATE = linkContext('viewport') | ||
var SCISSOR_STATE = linkContext('scissor.box') | ||
entry('if(', FRAMEBUFFER_STATE, '.push(', link( | ||
value && value._framebuffer), ')){', | ||
VIEWPORT_STATE, '.setDirty();', | ||
SCISSOR_STATE, '.setDirty();', | ||
'}') | ||
exit('if(', FRAMEBUFFER_STATE, '.pop()){', | ||
VIEWPORT_STATE, '.setDirty();', | ||
SCISSOR_STATE, '.setDirty();', | ||
'}') | ||
break | ||
// Update draw state | ||
@@ -1070,3 +1125,9 @@ case 'count': | ||
case 'lineWidth': | ||
check(value > 0 && typeof value === 'number', param) | ||
var lineWidthDims = limits.lineWidthDims | ||
check( | ||
typeof value === 'number' && | ||
value >= lineWidthDims[0] && | ||
value <= lineWidthDims[1], | ||
'invalid line width, must positive number between ' + | ||
lineWidthDims[0] + ' and ' + lineWidthDims[1]) | ||
handleStaticOption(param, value) | ||
@@ -1280,3 +1341,3 @@ break | ||
// ------------------------------- | ||
Object.keys(dynamicOptions).forEach(function (param) { | ||
Object.keys(dynamicOptions).sort(optionPriority).forEach(function (param) { | ||
// Link in dynamic variable | ||
@@ -1286,2 +1347,18 @@ var variable = dyn(dynamicOptions[param]) | ||
switch (param) { | ||
case 'framebuffer': | ||
var VIEWPORT_STATE = linkContext('viewport') | ||
var SCISSOR_STATE = linkContext('scissor.box') | ||
dynamicEntry('if(', | ||
FRAMEBUFFER_STATE, '.push(', | ||
variable, '&&', variable, '._framebuffer)){', | ||
VIEWPORT_STATE, '.setDirty();', | ||
SCISSOR_STATE, '.setDirty();', | ||
'}') | ||
dynamicExit('if(', | ||
FRAMEBUFFER_STATE, '.pop()){', | ||
VIEWPORT_STATE, '.setDirty();', | ||
SCISSOR_STATE, '.setDirty();', | ||
'}') | ||
break | ||
case 'cull.enable': | ||
@@ -1288,0 +1365,0 @@ case 'blend.enable': |
var check = require('./check') | ||
var isTypedArray = require('./is-typed-array') | ||
var isNDArrayLike = require('./is-ndarray') | ||
var primTypes = require('./constants/primitives.json') | ||
@@ -9,4 +10,7 @@ | ||
var GL_BYTE = 5120 | ||
var GL_UNSIGNED_BYTE = 5121 | ||
var GL_SHORT = 5122 | ||
var GL_UNSIGNED_SHORT = 5123 | ||
var GL_INT = 5124 | ||
var GL_UNSIGNED_INT = 5125 | ||
@@ -16,5 +20,3 @@ | ||
module.exports = function wrapElementsState (gl, extensionState, bufferState) { | ||
var extensions = extensionState.extensions | ||
module.exports = function wrapElementsState (gl, extensions, bufferState) { | ||
var elements = [ null ] | ||
@@ -29,123 +31,132 @@ | ||
function parseOptions (elements, options) { | ||
var result = { | ||
type: 'elements' | ||
} | ||
var ext32bit = extensions.oes_element_index_uint | ||
elements.primType = GL_TRIANGLES | ||
elements.vertCount = 0 | ||
elements.type = 0 | ||
REGLElementBuffer.prototype.bind = function () { | ||
this.buffer.bind() | ||
} | ||
var data = null | ||
function createElements (options) { | ||
var elements = new REGLElementBuffer() | ||
var buffer = bufferState.create(null, GL_ELEMENT_ARRAY_BUFFER, true) | ||
elements.buffer = buffer._buffer | ||
// Check option type | ||
if (!options) { | ||
return result | ||
} | ||
if (typeof options === 'number') { | ||
result.length = options | ||
} else { | ||
check.type(options, 'object', 'argument to element buffer must be object') | ||
data = options.data || options | ||
} | ||
function reglElements (input) { | ||
var options = input | ||
var ext32bit = extensions.oes_element_index_uint | ||
if (Array.isArray(data)) { | ||
if (options.length === 0) { | ||
data = null | ||
} else if (Array.isArray(data[0])) { | ||
var dim = data[0].length | ||
if (dim === 1) elements.primType = GL_POINTS | ||
if (dim === 2) elements.primType = GL_LINES | ||
if (dim === 3) elements.primType = GL_TRIANGLES | ||
var i | ||
var count = 0 | ||
for (i = 0; i < data.length; ++i) { | ||
count += data[i].length | ||
} | ||
var flattened = ext32bit | ||
? new Uint32Array(count) | ||
: new Uint16Array(count) | ||
var ptr = 0 | ||
for (i = 0; i < data.length; ++i) { | ||
var x = data[i] | ||
for (var j = 0; j < x.length; ++j) { | ||
flattened[ptr++] = x[j] | ||
// Upload data to vertex buffer | ||
if (!options) { | ||
buffer() | ||
} else if (typeof options === 'number') { | ||
buffer(options) | ||
} else { | ||
var data = null | ||
var usage = 'static' | ||
var byteLength = 0 | ||
if ( | ||
Array.isArray(options) || | ||
isTypedArray(options) || | ||
isNDArrayLike(options)) { | ||
data = options | ||
} else { | ||
check.type(options, 'object', 'invalid arguments for elements') | ||
if ('data' in options) { | ||
data = options.data | ||
} | ||
if ('usage' in options) { | ||
usage = options.usage | ||
} | ||
if ('length' in options) { | ||
byteLength = options.length | ||
} | ||
} | ||
data = flattened | ||
} else if (ext32bit) { | ||
data = new Uint32Array(data) | ||
} else { | ||
data = new Uint16Array(data) | ||
if (Array.isArray(data) || | ||
(isNDArrayLike(data) && data.dtype === 'array') || | ||
'type' in options) { | ||
buffer({ | ||
type: options.type || | ||
(ext32bit | ||
? 'uint32' | ||
: 'uint16'), | ||
usage: usage, | ||
data: data, | ||
length: byteLength | ||
}) | ||
} else { | ||
buffer({ | ||
usage: usage, | ||
data: data, | ||
length: byteLength | ||
}) | ||
} | ||
if (Array.isArray(data) || isTypedArray(data)) { | ||
buffer.dimension = 3 | ||
} | ||
} | ||
} | ||
if (isTypedArray(data)) { | ||
if ((data instanceof Uint8Array) || | ||
(data instanceof Uint8ClampedArray)) { | ||
elements.type = GL_UNSIGNED_BYTE | ||
} else if (data instanceof Uint16Array) { | ||
elements.type = GL_UNSIGNED_SHORT | ||
} else if (data instanceof Uint32Array) { | ||
check(ext32bit, '32-bit element buffers not supported') | ||
elements.type = GL_UNSIGNED_INT | ||
} else { | ||
check.raise('invalid typed array for element buffer') | ||
} | ||
elements.vertCount = data.length | ||
result.data = data | ||
} else { | ||
check(!data, 'invalid element buffer data type') | ||
} | ||
// try to guess default primitive type and arguments | ||
var vertCount = elements.buffer.byteLength | ||
var type = 0 | ||
switch (elements.buffer.dtype) { | ||
case GL_UNSIGNED_BYTE: | ||
case GL_BYTE: | ||
type = GL_UNSIGNED_BYTE | ||
break | ||
if (typeof options === 'object') { | ||
if ('primitive' in options) { | ||
var primitive = options.primitive | ||
check.param(primitive, primTypes) | ||
elements.primType = primTypes[primitive] | ||
} | ||
case GL_UNSIGNED_SHORT: | ||
case GL_SHORT: | ||
type = GL_UNSIGNED_SHORT | ||
vertCount >>= 1 | ||
break | ||
if ('usage' in options) { | ||
result.usage = options.usage | ||
} | ||
case GL_UNSIGNED_INT: | ||
case GL_INT: | ||
check(ext32bit, '32 bit element buffers not supported') | ||
type = GL_UNSIGNED_INT | ||
vertCount >>= 2 | ||
break | ||
if ('count' in options) { | ||
elements.vertCount = options.vertCount | 0 | ||
default: | ||
check.raise('invalid element buffer type') | ||
} | ||
} | ||
return result | ||
} | ||
// try to guess primitive type from cell dimension | ||
var primType = GL_TRIANGLES | ||
var dimension = elements.buffer.dimension | ||
if (dimension === 1) primType = GL_POINTS | ||
if (dimension === 2) primType = GL_LINES | ||
if (dimension === 3) primType = GL_TRIANGLES | ||
Object.assign(REGLElementBuffer.prototype, { | ||
bind: function () { | ||
gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, this.buffer._buffer.buffer) | ||
}, | ||
// if manual override present, use that | ||
if (typeof options === 'object') { | ||
if ('primitive' in options) { | ||
var primitive = options.primitive | ||
check.parameter(primitive, primTypes) | ||
primType = primTypes[primitive] | ||
} | ||
destroy: function () { | ||
if (this.buffer) { | ||
this.buffer.destroy() | ||
this.buffer = null | ||
if ('count' in options) { | ||
vertCount = options.vertCount | 0 | ||
} | ||
} | ||
} | ||
}) | ||
function createElements (options) { | ||
var elements = new REGLElementBuffer() | ||
// update properties for element buffer | ||
elements.primType = primType | ||
elements.vertCount = vertCount | ||
elements.type = type | ||
// Create buffer | ||
elements.buffer = bufferState.create( | ||
parseOptions(elements, options), | ||
GL_ELEMENT_ARRAY_BUFFER) | ||
function updateElements (options) { | ||
elements.buffer(parseOptions(elements, options)) | ||
return updateElements | ||
return reglElements | ||
} | ||
updateElements._reglType = 'elements' | ||
updateElements._elements = elements | ||
updateElements.destroy = function () { elements.destroy() } | ||
reglElements(options) | ||
return updateElements | ||
Object.assign(reglElements, { | ||
_reglType: 'elements', | ||
_elements: elements, | ||
destroy: function () { | ||
check(elements.buffer !== null, 'must not double destroy elements') | ||
buffer.destroy() | ||
elements.buffer = null | ||
} | ||
}) | ||
return reglElements | ||
} | ||
@@ -152,0 +163,0 @@ |
@@ -31,5 +31,6 @@ var GL_SUBPIXEL_BITS = 0x0D50 | ||
module.exports = function (gl, extensionState) { | ||
var extensions = extensionState.extensions | ||
var GL_MAX_COLOR_ATTACHMENTS_WEBGL = 0x8CDF | ||
var GL_MAX_DRAW_BUFFERS_WEBGL = 0x8824 | ||
module.exports = function (gl, extensions) { | ||
var maxAnisotropic = 1 | ||
@@ -40,2 +41,9 @@ if (extensions.ext_texture_filter_anisotropic) { | ||
var maxDrawbuffers = 1 | ||
var maxColorAttachments = 1 | ||
if (extensions.webgl_draw_buffers) { | ||
maxDrawbuffers = gl.getParameter(GL_MAX_DRAW_BUFFERS_WEBGL) | ||
maxColorAttachments = gl.getParameter(GL_MAX_COLOR_ATTACHMENTS_WEBGL) | ||
} | ||
return { | ||
@@ -61,16 +69,20 @@ // drawing buffer bit depth | ||
// max draw buffers | ||
maxDrawbuffers: maxDrawbuffers, | ||
maxColorAttachments: maxColorAttachments, | ||
// point and line size ranges | ||
pointSize: gl.getParameter(GL_ALIASED_POINT_SIZE_RANGE), | ||
lineWidth: gl.getParameter(GL_ALIASED_LINE_WIDTH_RANGE), | ||
viewport: gl.getParameter(GL_MAX_VIEWPORT_DIMS), | ||
combinedTextureUnits: gl.getParameter(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS), | ||
cubeMapSize: gl.getParameter(GL_MAX_CUBE_MAP_TEXTURE_SIZE), | ||
renderbufferSize: gl.getParameter(GL_MAX_RENDERBUFFER_SIZE), | ||
textureUnits: gl.getParameter(GL_MAX_TEXTURE_IMAGE_UNITS), | ||
textureSize: gl.getParameter(GL_MAX_TEXTURE_SIZE), | ||
attributes: gl.getParameter(GL_MAX_VERTEX_ATTRIBS), | ||
vertexUniforms: gl.getParameter(GL_MAX_VERTEX_UNIFORM_VECTORS), | ||
vertexTextureUnits: gl.getParameter(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS), | ||
varyingVectors: gl.getParameter(GL_MAX_VARYING_VECTORS), | ||
fragmentUniforms: gl.getParameter(GL_MAX_FRAGMENT_UNIFORM_VECTORS), | ||
pointSizeDims: gl.getParameter(GL_ALIASED_POINT_SIZE_RANGE), | ||
lineWidthDims: gl.getParameter(GL_ALIASED_LINE_WIDTH_RANGE), | ||
maxViewportDims: gl.getParameter(GL_MAX_VIEWPORT_DIMS), | ||
maxCombinedTextureUnits: gl.getParameter(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS), | ||
maxCubeMapSize: gl.getParameter(GL_MAX_CUBE_MAP_TEXTURE_SIZE), | ||
maxRenderbufferSize: gl.getParameter(GL_MAX_RENDERBUFFER_SIZE), | ||
maxTextureUnits: gl.getParameter(GL_MAX_TEXTURE_IMAGE_UNITS), | ||
maxTextureSize: gl.getParameter(GL_MAX_TEXTURE_SIZE), | ||
maxAttributes: gl.getParameter(GL_MAX_VERTEX_ATTRIBS), | ||
maxVertexUniforms: gl.getParameter(GL_MAX_VERTEX_UNIFORM_VECTORS), | ||
maxVertexTextureUnits: gl.getParameter(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS), | ||
maxVaryingVectors: gl.getParameter(GL_MAX_VARYING_VECTORS), | ||
maxFragmentUniforms: gl.getParameter(GL_MAX_FRAGMENT_UNIFORM_VECTORS), | ||
@@ -77,0 +89,0 @@ // vendor info |
@@ -8,3 +8,3 @@ var check = require('./check') | ||
module.exports = function wrapReadPixels (gl, glState) { | ||
module.exports = function wrapReadPixels (gl, reglPoll, viewportState) { | ||
function readPixels (input) { | ||
@@ -26,6 +26,5 @@ var options = input || {} | ||
// Update WebGL state | ||
glState.poll() | ||
reglPoll() | ||
// Read viewport state | ||
var viewportState = glState.viewport | ||
var x = options.x || 0 | ||
@@ -32,0 +31,0 @@ var y = options.y || 0 |
@@ -17,3 +17,2 @@ var check = require('./check') | ||
gl, | ||
extensions, | ||
attributeState, | ||
@@ -20,0 +19,0 @@ uniformState, |
@@ -6,2 +6,3 @@ // A stack for managing the state of a scalar/vector parameter | ||
var stack = init.slice() | ||
var current = init.slice() | ||
var dirty = true | ||
@@ -34,2 +35,5 @@ | ||
} | ||
for (var i = 0; i < n; ++i) { | ||
current[i] = stack[ptr + i] | ||
} | ||
dirty = false | ||
@@ -41,11 +45,16 @@ } | ||
push: function () { | ||
dirty = false | ||
for (var i = 0; i < n; ++i) { | ||
stack.push(arguments[i]) | ||
var x = arguments[i] | ||
dirty = dirty || (x !== current[i]) | ||
stack.push(x) | ||
} | ||
dirty = true | ||
}, | ||
pop: function () { | ||
dirty = false | ||
for (var i = 0; i < n; ++i) { | ||
dirty = dirty || (stack[stack.length - n + i] !== current[i]) | ||
} | ||
stack.length -= n | ||
dirty = true | ||
}, | ||
@@ -52,0 +61,0 @@ |
@@ -24,3 +24,3 @@ var createStack = require('./stack') | ||
module.exports = function wrapContextState (gl, shaderState) { | ||
module.exports = function wrapContextState (gl, framebufferState, viewportState) { | ||
function capStack (cap, dflt) { | ||
@@ -38,7 +38,2 @@ var result = createStack([!!dflt], function (flag) { | ||
var viewportState = { | ||
width: 0, | ||
height: 0 | ||
} | ||
// Caps, flags and other random WebGL context state | ||
@@ -134,8 +129,17 @@ var contextState = { | ||
var w_ = w | ||
var fbo = framebufferState.top() | ||
if (w < 0) { | ||
w_ = gl.drawingBufferWidth - x | ||
if (fbo) { | ||
w_ = fbo.width - x | ||
} else { | ||
w_ = gl.drawingBufferWidth - x | ||
} | ||
} | ||
var h_ = h | ||
if (h < 0) { | ||
h_ = gl.drawingBufferHeight - y | ||
if (fbo) { | ||
h_ = fbo.height - y | ||
} else { | ||
h_ = gl.drawingBufferHeight - y | ||
} | ||
} | ||
@@ -148,8 +152,17 @@ gl.scissor(x, y, w_, h_) | ||
var w_ = w | ||
var fbo = framebufferState.top() | ||
if (w < 0) { | ||
w_ = gl.drawingBufferWidth - x | ||
if (fbo) { | ||
w_ = fbo.width - x | ||
} else { | ||
w_ = gl.drawingBufferWidth - x | ||
} | ||
} | ||
var h_ = h | ||
if (h < 0) { | ||
h_ = gl.drawingBufferHeight - y | ||
if (fbo) { | ||
h_ = fbo.height - y | ||
} else { | ||
h_ = gl.drawingBufferHeight - y | ||
} | ||
} | ||
@@ -156,0 +169,0 @@ gl.viewport(x, y, w_, h_) |
var check = require('./check') | ||
var values = require('./values') | ||
var isTypedArray = require('./is-typed-array') | ||
var isNDArrayLike = require('./is-ndarray') | ||
var loadTexture = require('./load-texture') | ||
@@ -108,13 +110,2 @@ var convertToHalfFloat = require('./to-half-float') | ||
function isNDArrayLike (obj) { | ||
return ( | ||
typeof obj === 'object' && | ||
Array.isArray(obj.shape) && | ||
Array.isArray(obj.stride) && | ||
typeof obj.offset === 'number' && | ||
obj.shape.length === obj.stride.length && | ||
(Array.isArray(obj.data) || | ||
isTypedArray(obj.data))) | ||
} | ||
function isRectArray (arr) { | ||
@@ -191,5 +182,3 @@ if (!Array.isArray(arr)) { | ||
module.exports = function createTextureSet (gl, extensionState, limits, reglPoll, viewport) { | ||
var extensions = extensionState.extensions | ||
module.exports = function createTextureSet (gl, extensions, limits, reglPoll, viewportState) { | ||
var mipmapHint = { | ||
@@ -515,4 +504,2 @@ "don't care": GL_DONT_CARE, | ||
data = loadTexture(data, this.crossOrigin) | ||
} else { | ||
check(!data || isPixelData(data), 'invalid pixel data') | ||
} | ||
@@ -629,4 +616,4 @@ | ||
this.y = this.y | 0 | ||
this.width = (this.width || viewport.width) | 0 | ||
this.height = (this.height || viewport.height) | 0 | ||
this.width = (this.width || viewportState.width) | 0 | ||
this.height = (this.height || viewportState.height) | 0 | ||
this.setDefaultFormat() | ||
@@ -862,6 +849,2 @@ } | ||
Object.assign(TexParams.prototype, { | ||
clear: function () { | ||
TexParams.call(this, this.target) | ||
}, | ||
parse: function (options) { | ||
@@ -1098,3 +1081,3 @@ if (typeof options !== 'object' || !options) { | ||
var pollSet = [] | ||
var numTexUnits = limits.textureUnits | ||
var numTexUnits = limits.maxTextureUnits | ||
var textureUnits = Array(numTexUnits).map(function () { | ||
@@ -1104,6 +1087,8 @@ return null | ||
function REGLTexture (target, texture) { | ||
function REGLTexture (target) { | ||
this.id = textureCount++ | ||
this.refCount = 1 | ||
this.target = target | ||
this.texture = texture | ||
this.texture = null | ||
@@ -1123,250 +1108,269 @@ this.pollId = -1 | ||
Object.assign(REGLTexture.prototype, { | ||
function update (texture, options) { | ||
var i | ||
clearListeners(texture) | ||
bind: function () { | ||
this.bindCount += 1 | ||
var unit = this.unit | ||
if (unit < 0) { | ||
// FIXME: should we use an LRU to allocate textures here? | ||
for (var i = 0; i < numTexUnits; ++i) { | ||
var other = textureUnits[i] | ||
if (!other || other.bindCount <= 0) { | ||
if (other) { | ||
other.unit = -1 | ||
} | ||
textureUnits[i] = this | ||
unit = i | ||
break | ||
} | ||
} | ||
if (unit >= numTexUnits) { | ||
check.raise('insufficient number of texture units') | ||
} | ||
this.unit = unit | ||
gl.activeTexture(GL_TEXTURE0 + unit) | ||
gl.bindTexture(this.target, this.texture) | ||
activeTexture = unit | ||
} | ||
return unit | ||
}, | ||
// Clear parameters and pixel data | ||
var params = texture.params | ||
TexParams.call(params, texture.target) | ||
var pixels = texture.pixels | ||
pixels.length = 0 | ||
unbind: function () { | ||
this.bindCount -= 1 | ||
}, | ||
// parse parameters | ||
params.parse(options) | ||
refresh: function () { | ||
// Lazy bind | ||
var target = this.target | ||
var unit = this.unit | ||
if (unit >= 0) { | ||
gl.activeTexture(GL_TEXTURE0 + unit) | ||
activeTexture = unit | ||
// parse pixel data | ||
function parseMip (target, data) { | ||
var mipmap = data.mipmap | ||
var pixmap | ||
if (Array.isArray(mipmap)) { | ||
for (var i = 0; i < mipmap.length; ++i) { | ||
pixmap = new PixelInfo(target) | ||
pixmap.parseFlags(options) | ||
pixmap.parseFlags(data) | ||
pixmap.parse(mipmap[i], i) | ||
pixels.push(pixmap) | ||
} | ||
} else { | ||
gl.bindTexture(target, this.texture) | ||
pixmap = new PixelInfo(target) | ||
pixmap.parseFlags(options) | ||
pixmap.parse(data, 0) | ||
pixels.push(pixmap) | ||
} | ||
// Upload | ||
var pixels = this.pixels | ||
var params = this.params | ||
for (var i = 0; i < pixels.length; ++i) { | ||
pixels[i].upload(params) | ||
} | ||
params.upload() | ||
// Lazy unbind | ||
if (unit < 0) { | ||
var active = textureUnits[activeTexture] | ||
if (active) { | ||
// restore binding state | ||
gl.bindTexture(active.target, active.texture) | ||
} else { | ||
// otherwise become new active | ||
this.unit = activeTexture | ||
textureUnits[activeTexture] = this | ||
} | ||
if (texture.target === GL_TEXTURE_2D) { | ||
parseMip(GL_TEXTURE_2D, options) | ||
} else { | ||
var faces = options.faces || options | ||
if (Array.isArray(faces)) { | ||
check(faces.length === 6, | ||
'invalid number of faces in cube map') | ||
for (i = 0; i < 6; ++i) { | ||
parseMip(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, faces[i]) | ||
} | ||
} else if (typeof faces === 'string') { | ||
// TODO Read dds | ||
} else { | ||
// Initialize to all empty textures | ||
for (i = 0; i < 6; ++i) { | ||
parseMip(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, {}) | ||
} | ||
} | ||
}, | ||
} | ||
update: function (options) { | ||
var i | ||
// do a second pass to reconcile defaults | ||
checkTextureComplete(params, pixels) | ||
this.clearListeners() | ||
if (params.needsListeners) { | ||
hookListeners(texture) | ||
} | ||
// Clear parameters and pixel data | ||
var params = this.params | ||
var pixels = this.pixels | ||
params.clear() | ||
pixels.length = 0 | ||
if (params.needsPoll) { | ||
texture.pollId = pollSet.length | ||
pollSet.push(texture) | ||
} | ||
// parse parameters | ||
params.parse(options) | ||
refresh(texture) | ||
} | ||
// parse pixel data | ||
function parseMip (target, data) { | ||
var mipmap = data.mipmap | ||
var pixmap | ||
if (Array.isArray(mipmap)) { | ||
for (var i = 0; i < mipmap.length; ++i) { | ||
pixmap = new PixelInfo(target) | ||
pixmap.parseFlags(options) | ||
pixmap.parseFlags(data) | ||
pixmap.parse(mipmap[i], i) | ||
pixels.push(pixmap) | ||
} | ||
} else { | ||
pixmap = new PixelInfo(target) | ||
pixmap.parseFlags(options) | ||
pixmap.parse(data, 0) | ||
pixels.push(pixmap) | ||
} | ||
} | ||
if (this.target === GL_TEXTURE_2D) { | ||
parseMip(GL_TEXTURE_2D, options) | ||
} else { | ||
var faces = options.faces || options | ||
if (Array.isArray(faces)) { | ||
check(faces.length === 6, | ||
'invalid number of faces in cube map') | ||
for (i = 0; i < 6; ++i) { | ||
parseMip(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, faces[i]) | ||
} | ||
} else if (typeof faces === 'string') { | ||
// TODO Read dds | ||
} else { | ||
// Initialize to all empty textures | ||
for (i = 0; i < 6; ++i) { | ||
parseMip(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, {}) | ||
} | ||
} | ||
} | ||
function refresh (texture) { | ||
if (!gl.isTexture(texture.texture)) { | ||
texture.texture = gl.createTexture() | ||
} | ||
// do a second pass to reconcile defaults | ||
checkTextureComplete(params, pixels) | ||
// Lazy bind | ||
var target = texture.target | ||
var unit = texture.unit | ||
if (unit >= 0) { | ||
gl.activeTexture(GL_TEXTURE0 + unit) | ||
activeTexture = unit | ||
} else { | ||
gl.bindTexture(target, texture.texture) | ||
} | ||
if (params.needsListeners) { | ||
this.hookListeners() | ||
} | ||
// Upload | ||
var pixels = texture.pixels | ||
var params = texture.params | ||
for (var i = 0; i < pixels.length; ++i) { | ||
pixels[i].upload(params) | ||
} | ||
params.upload() | ||
if (params.needsPoll) { | ||
this.pollId = pollSet.length | ||
pollSet.push(this) | ||
// Lazy unbind | ||
if (unit < 0) { | ||
var active = textureUnits[activeTexture] | ||
if (active) { | ||
// restore binding state | ||
gl.bindTexture(active.target, active.texture) | ||
} else { | ||
// otherwise become new active | ||
texture.unit = activeTexture | ||
textureUnits[activeTexture] = texture | ||
} | ||
} | ||
} | ||
this.refresh() | ||
}, | ||
function hookListeners (texture) { | ||
var params = texture.params | ||
var pixels = texture.pixels | ||
hookListeners: function () { | ||
var params = this.params | ||
var pixels = this.pixels | ||
var texture = this | ||
// Appends all the texture data from the buffer to the current | ||
function appendDDS (target, miplevel, buffer) { | ||
var dds = parseDDS(buffer) | ||
// Appends all the texture data from the buffer to the current | ||
function appendDDS (target, miplevel, buffer) { | ||
var dds = parseDDS(buffer) | ||
check(dds.format in colorFormats, 'unsupported dds texture format') | ||
check(dds.format in colorFormats, 'unsupported dds texture format') | ||
if (dds.cube) { | ||
check(texture.target === GL_TEXTURE_CUBE_MAP) | ||
if (dds.cube) { | ||
check(texture.target === GL_TEXTURE_CUBE_MAP) | ||
// TODO handle cube map DDS | ||
check.raise('cube map DDS not yet implemented') | ||
} else { | ||
check(texture.target === GL_TEXTURE_2D) | ||
} | ||
// TODO handle cube map | ||
} else { | ||
check(texture.target === GL_TEXTURE_2D) | ||
} | ||
if (miplevel) { | ||
check(dds.pixels.length === 1, 'number of mip levels inconsistent') | ||
} | ||
if (miplevel) { | ||
check(dds.pixels.length === 1, 'number of mip levels inconsistent') | ||
} | ||
dds.pixels.forEach(function (pixmap) { | ||
var info = new PixelInfo(dds.cube ? pixmap.target : target) | ||
dds.pixels.forEach(function (pixmap) { | ||
var info = new PixelInfo(dds.cube ? pixmap.target : target) | ||
info.channels = dds.channels | ||
info.compressed = dds.compressed | ||
info.type = dds.type | ||
info.internalformat = dds.format | ||
info.format = colorFormats[dds.format] | ||
info.channels = dds.channels | ||
info.compressed = dds.compressed | ||
info.type = dds.type | ||
info.internalformat = dds.format | ||
info.format = colorFormats[dds.format] | ||
info.width = pixmap.width | ||
info.height = pixmap.height | ||
info.miplevel = pixmap.miplevel || miplevel | ||
info.data = pixmap.data | ||
info.width = pixmap.width | ||
info.height = pixmap.height | ||
info.miplevel = pixmap.miplevel || miplevel | ||
info.data = pixmap.data | ||
pixels.push(info) | ||
}) | ||
} | ||
pixels.push(info) | ||
}) | ||
function onData () { | ||
// Update size of any newly loaded pixels | ||
for (var i = 0; i < pixels.length; ++i) { | ||
var pixelData = pixels[i] | ||
var image = pixelData.image | ||
var video = pixelData.video | ||
var xhr = pixelData.xhr | ||
if (image && image.complete) { | ||
pixelData.width = image.naturalWidth | ||
pixelData.height = image.naturalHeight | ||
} else if (video && video.readyState > 2) { | ||
pixelData.width = video.width | ||
pixelData.height = video.height | ||
} else if (xhr && xhr.readyState === 4) { | ||
pixels[i] = pixels[pixels.length - 1] | ||
pixels.pop() | ||
xhr.removeEventListener('readystatechange', refresh) | ||
appendDDS(pixelData.target, pixelData.miplevel, xhr.response) | ||
} | ||
} | ||
checkTextureComplete(params, pixels) | ||
refresh(texture) | ||
} | ||
function refresh () { | ||
// Update size of any newly loaded pixels | ||
for (var i = 0; i < pixels.length; ++i) { | ||
var pixelData = pixels[i] | ||
var image = pixelData.image | ||
var video = pixelData.video | ||
var xhr = pixelData.xhr | ||
if (image && image.complete) { | ||
pixelData.width = image.naturalWidth | ||
pixelData.height = image.naturalHeight | ||
} else if (video && video.readyState > 2) { | ||
pixelData.width = video.width | ||
pixelData.height = video.height | ||
} else if (xhr && xhr.readyState === 4) { | ||
pixels[i] = pixels[pixels.length - 1] | ||
pixels.pop() | ||
xhr.removeEventListener('readystatechange', refresh) | ||
appendDDS(pixelData.target, pixelData.miplevel, xhr.response) | ||
} | ||
} | ||
checkTextureComplete(params, pixels) | ||
texture.refresh() | ||
pixels.forEach(function (pixelData) { | ||
if (pixelData.image && !pixelData.image.complete) { | ||
pixelData.image.addEventListener('load', onData) | ||
} else if (pixelData.video && pixelData.readyState < 1) { | ||
pixelData.video.addEventListener('progress', onData) | ||
} else if (pixelData.xhr) { | ||
pixelData.xhr.addEventListener('readystatechange', onData) | ||
} | ||
}) | ||
texture.cancelPending = function detachListeners () { | ||
pixels.forEach(function (pixelData) { | ||
if (pixelData.image && !pixelData.image.complete) { | ||
pixelData.image.addEventListener('load', refresh) | ||
} else if (pixelData.video && pixelData.readyState < 1) { | ||
pixelData.video.addEventListener('progress', refresh) | ||
if (pixelData.image) { | ||
pixelData.image.removeEventListener('load', onData) | ||
} else if (pixelData.video) { | ||
pixelData.video.removeEventListener('progress', onData) | ||
} else if (pixelData.xhr) { | ||
pixelData.xhr.addEventListener('readystatechange', refresh) | ||
pixelData.xhr.removeEventListener('readystatechange', onData) | ||
pixelData.xhr.abort() | ||
} | ||
}) | ||
} | ||
} | ||
this.cancelPending = function detachListeners () { | ||
pixels.forEach(function (pixelData) { | ||
if (pixelData.image) { | ||
pixelData.image.removeEventListener('load', refresh) | ||
} else if (pixelData.video) { | ||
pixelData.video.removeEventListener('progress', refresh) | ||
function clearListeners (texture) { | ||
var cancelPending = texture.cancelPending | ||
if (cancelPending) { | ||
cancelPending() | ||
texture.cancelPending = null | ||
} | ||
var id = texture.pollId | ||
if (id >= 0) { | ||
var other = pollSet[id] = pollSet[pollSet.length - 1] | ||
other.id = id | ||
pollSet.pop() | ||
texture.pollId = -1 | ||
} | ||
} | ||
function destroy (texture) { | ||
var handle = texture.texture | ||
check(handle, 'must not double destroy texture') | ||
var unit = texture.unit | ||
var target = texture.target | ||
if (unit >= 0) { | ||
gl.activeTexture(GL_TEXTURE0 + unit) | ||
activeTexture = unit | ||
gl.bindTexture(target, null) | ||
textureUnits[unit] = null | ||
} | ||
clearListeners(texture) | ||
if (gl.isTexture(handle)) { | ||
gl.deleteTexture(handle) | ||
} | ||
texture.texture = null | ||
texture.params = null | ||
texture.pixels = null | ||
texture.refCount = 0 | ||
delete textureSet[texture.id] | ||
} | ||
Object.assign(REGLTexture.prototype, { | ||
bind: function () { | ||
var texture = this | ||
texture.bindCount += 1 | ||
var unit = texture.unit | ||
if (unit < 0) { | ||
for (var i = 0; i < numTexUnits; ++i) { | ||
var other = textureUnits[i] | ||
if (other) { | ||
if (other.bindCount > 0) { | ||
continue | ||
} | ||
other.unit = -1 | ||
} | ||
}) | ||
textureUnits[i] = texture | ||
unit = i | ||
break | ||
} | ||
if (unit >= numTexUnits) { | ||
check.raise('insufficient number of texture units') | ||
} | ||
texture.unit = unit | ||
gl.activeTexture(GL_TEXTURE0 + unit) | ||
gl.bindTexture(texture.target, texture.texture) | ||
activeTexture = unit | ||
} | ||
return unit | ||
}, | ||
clearListeners: function () { | ||
if (this.cancelPending) { | ||
this.cancelPending() | ||
this.cancelPending = null | ||
} | ||
var id = this.pollId | ||
if (id >= 0) { | ||
var other = pollSet[id] = pollSet[pollSet.length - 1] | ||
other.id = id | ||
pollSet.pop() | ||
this.pollId = -1 | ||
} | ||
unbind: function () { | ||
this.bindCount -= 1 | ||
}, | ||
destroy: function () { | ||
check(this.texture, 'must not double free texture') | ||
if (this.unit >= 0) { | ||
gl.activeTexture(GL_TEXTURE0 + this.unit) | ||
activeTexture = this.unit | ||
gl.bindTexture(this.target, null) | ||
textureUnits[this.unit] = null | ||
decRef: function () { | ||
if (--this.refCount === 0) { | ||
destroy(this) | ||
} | ||
this.clearListeners() | ||
gl.deleteTexture(this.texture) | ||
this.texture = null | ||
this.params = null | ||
this.pixels = null | ||
delete textureSet[this.id] | ||
} | ||
@@ -1376,3 +1380,3 @@ }) | ||
function createTexture (options, target) { | ||
var texture = new REGLTexture(target, gl.createTexture()) | ||
var texture = new REGLTexture(target) | ||
textureSet[texture.id] = texture | ||
@@ -1385,3 +1389,6 @@ | ||
} | ||
texture.update(options) | ||
update(texture, options) | ||
reglTexture.width = texture.params.width | ||
reglTexture.height = texture.params.height | ||
return reglTexture | ||
} | ||
@@ -1395,3 +1402,3 @@ | ||
destroy: function () { | ||
texture.destroy() | ||
texture.decRef() | ||
} | ||
@@ -1405,5 +1412,3 @@ }) | ||
function refreshTextures () { | ||
Object.keys(textureSet).forEach(function (texId) { | ||
textureSet[texId].refresh() | ||
}) | ||
values(textureSet).forEach(refresh) | ||
for (var i = 0; i < numTexUnits; ++i) { | ||
@@ -1425,5 +1430,3 @@ textureUnits[i] = null | ||
activeTexture = 0 | ||
Object.keys(textureSet).forEach(function (texId) { | ||
textureSet[texId].destroy() | ||
}) | ||
values(textureSet).forEach(destroy) | ||
} | ||
@@ -1433,5 +1436,3 @@ | ||
function pollTextures () { | ||
for (var i = 0; i < pollSet.length; ++i) { | ||
pollSet[i].refresh() | ||
} | ||
pollSet.forEach(refresh) | ||
} | ||
@@ -1438,0 +1439,0 @@ |
{ | ||
"name": "regl", | ||
"version": "0.2.0", | ||
"version": "0.3.0", | ||
"description": "WebGL", | ||
@@ -17,7 +17,8 @@ "main": "regl.js", | ||
"canvas-fit": "^1.5.0", | ||
"canvas-orbit-camera": "^1.0.2", | ||
"coverify": "^1.4.1", | ||
"derequire": "^2.0.3", | ||
"faucet": "0.0.1", | ||
"gl": "^3.0.3", | ||
"gl-mat4": "^1.1.4", | ||
"google-closure-compiler": "^20160315.2.0", | ||
"hsv2rgb": "^1.1.0", | ||
@@ -41,4 +42,4 @@ "indexhtmlify": "^1.2.1", | ||
"build": "npm run build-script && npm run build-min && npm run build-bench && npm run build-gallery", | ||
"build-script": "browserify regl.js -s regl | derequire > dist/regl.js", | ||
"build-min": "uglifyjs < dist/regl.js > dist/regl.min.js", | ||
"build-script": "browserify regl.js --standalone reglInit > dist/regl.js", | ||
"build-min": "node bin/build-min.js", | ||
"build-bench": "browserify bench/index.js | indexhtmlify > www/bench.html", | ||
@@ -45,0 +46,0 @@ "build-gallery": "node bin/build-gallery.js" |
101
regl.js
@@ -8,3 +8,4 @@ var check = require('./lib/check') | ||
var wrapTextures = require('./lib/texture') | ||
var wrapFBOs = require('./lib/fbo') | ||
var wrapRenderbuffers = require('./lib/renderbuffer') | ||
var wrapFramebuffers = require('./lib/framebuffer') | ||
var wrapUniforms = require('./lib/uniform') | ||
@@ -38,10 +39,30 @@ var wrapAttributes = require('./lib/attribute') | ||
var extensionState = wrapExtensions(gl) | ||
var limits = wrapLimits(gl, extensionState) | ||
var extensions = extensionState.extensions | ||
var viewportState = { | ||
width: gl.drawingBufferWidth, | ||
height: gl.drawingBufferHeight | ||
} | ||
var limits = wrapLimits( | ||
gl, | ||
extensions) | ||
var bufferState = wrapBuffers(gl) | ||
var elementState = wrapElements(gl, extensionState, bufferState) | ||
var elementState = wrapElements( | ||
gl, | ||
extensions, | ||
bufferState) | ||
var uniformState = wrapUniforms() | ||
var attributeState = wrapAttributes(gl, extensionState, bufferState) | ||
var attributeState = wrapAttributes( | ||
gl, | ||
extensions, | ||
limits, | ||
bufferState) | ||
var shaderState = wrapShaders( | ||
gl, | ||
extensionState, | ||
attributeState, | ||
@@ -52,11 +73,27 @@ uniformState, | ||
}) | ||
var drawState = wrapDraw(gl, extensionState, bufferState) | ||
var glState = wrapContext(gl, shaderState) | ||
var drawState = wrapDraw( | ||
gl, | ||
extensions, | ||
bufferState) | ||
var textureState = wrapTextures( | ||
gl, | ||
extensionState, | ||
extensions, | ||
limits, | ||
poll, | ||
glState.viewport) | ||
var fboState = wrapFBOs(gl, extensionState, textureState) | ||
viewportState) | ||
var renderbufferState = wrapRenderbuffers( | ||
gl, | ||
extensions, | ||
limits) | ||
var framebufferState = wrapFramebuffers( | ||
gl, | ||
extensions, | ||
limits, | ||
textureState, | ||
renderbufferState) | ||
var frameState = { | ||
@@ -72,11 +109,18 @@ count: 0, | ||
} | ||
var readPixels = wrapRead(gl, glState) | ||
var glState = wrapContext( | ||
gl, | ||
framebufferState, | ||
viewportState) | ||
var readPixels = wrapRead(gl, poll, viewportState) | ||
var compiler = createCompiler( | ||
gl, | ||
extensionState, | ||
extensions, | ||
limits, | ||
bufferState, | ||
elementState, | ||
textureState, | ||
fboState, | ||
framebufferState, | ||
glState, | ||
@@ -87,3 +131,4 @@ uniformState, | ||
drawState, | ||
frameState) | ||
frameState, | ||
poll) | ||
@@ -145,3 +190,4 @@ var canvas = gl.canvas | ||
textureState.refresh() | ||
fboState.refresh() | ||
renderbufferState.refresh() | ||
framebufferState.refresh() | ||
shaderState.refresh() | ||
@@ -170,3 +216,4 @@ glState.refresh() | ||
shaderState.clear() | ||
fboState.clear() | ||
framebufferState.clear() | ||
renderbufferState.clear() | ||
textureState.clear() | ||
@@ -269,2 +316,3 @@ bufferState.clear() | ||
function poll () { | ||
framebufferState.poll() | ||
glState.poll() | ||
@@ -278,3 +326,3 @@ } | ||
// Update context state | ||
glState.poll() | ||
poll() | ||
@@ -286,3 +334,2 @@ var c = options.color | ||
} | ||
if ('depth' in options) { | ||
@@ -292,3 +339,2 @@ gl.clearDepth(+options.depth) | ||
} | ||
if ('stencil' in options) { | ||
@@ -331,6 +377,9 @@ gl.clearStencil(options.stencil | 0) | ||
// Dynamic variable binding | ||
// Short cut for prop binding | ||
prop: dynamic.define, | ||
// Object constructors | ||
// executes an empty draw command | ||
draw: compileProcedure({}), | ||
// Resources | ||
elements: function (options) { | ||
@@ -354,3 +403,11 @@ return elementState.create(options) | ||
}, | ||
// fbo: create(fboState), | ||
renderbuffer: function (options) { | ||
return renderbufferState.create(options) | ||
}, | ||
framebuffer: function (options) { | ||
return framebufferState.create(options) | ||
}, | ||
framebufferCube: function (options) { | ||
check.raise('framebuffer cube not yet implemented') | ||
}, | ||
@@ -357,0 +414,0 @@ // Frame rendering |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Uses eval
Supply chain riskPackage uses eval() which is a dangerous function. This prevents the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
Uses eval
Supply chain riskPackage uses eval() which is a dangerous function. This prevents the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
490990
44
11660
23