Comparing version 0.1.0 to 0.2.0
355
API.md
@@ -8,2 +8,3 @@ # REGL API | ||
* [From a WebGL context](#from-a-webgl-context) | ||
+ [Initialization options](#initialization-options) | ||
* [Commands](#commands) | ||
@@ -20,3 +21,3 @@ + [Dynamic properties](#dynamic-properties) | ||
- [Drawing](#drawing) | ||
- [Framebuffer](#framebuffer) | ||
- [Render target](#render-target) | ||
- [Depth buffer](#depth-buffer) | ||
@@ -39,8 +40,8 @@ - [Blending](#blending) | ||
+ [Types](#types) | ||
- [`regl.buffer(options)`](#-reglbuffer-options--) | ||
- [`regl.elements(options)`](#-reglelements-options--) | ||
- [`regl.texture(options)`](#-regltexture-options--) | ||
- [`regl.rbo(options)`](#-reglrbo-options--) | ||
- [`regl.fbo(options)`](#-reglfbo-options--) | ||
* [Other properties](#other-properties) | ||
- [Buffers](#buffers) | ||
- [Elements](#elements) | ||
- [Textures](#textures) | ||
- [Render buffers](#render-buffers) | ||
- [Frame buffers](#frame-buffers) | ||
* [Other features](#other-features) | ||
+ [Clear the draw buffer](#clear-the-draw-buffer) | ||
@@ -50,3 +51,3 @@ + [Reading pixels](#reading-pixels) | ||
+ [Frame stats](#frame-stats) | ||
+ [WebGL capabilities](#webgl-capabilities) | ||
+ [Limits](#limits) | ||
+ [Clean up](#clean-up) | ||
@@ -93,2 +94,6 @@ + [Context loss](#context-loss) | ||
### Initialization options | ||
**TODO** | ||
--------------------------------------- | ||
@@ -432,3 +437,3 @@ ## Commands | ||
--------------------------------------- | ||
#### Framebuffer | ||
#### Render target | ||
@@ -526,3 +531,6 @@ TODO | ||
| `'reverse subtract'` | `gl.FUNC_REVERSE_SUBTRACT` | | ||
| `'min'` | `gl.MIN_EXT` | | ||
| `'max'` | `gl.MAX_EXT` | | ||
* `'min'` and `'max'` are only available if the `EXT_blend_minmax` extension is supported | ||
* `func` can be an object with the fields `{src, dst}` or `{srcRGB, srcAlpha, dstRGB, dstAlpha}`, with the former corresponding to `gl.blendFunc` and the latter to `gl.blendFuncSeparate` | ||
@@ -922,12 +930,60 @@ * The fields of `func` can take on the following values | ||
#### Buffers | ||
Examples, | ||
```javascript | ||
// Creates an empty length 100 buffer | ||
var zeroBuffer = regl.buffer(100) | ||
// A buffer with float data | ||
var floatBuffer = regl.buffer(new Float32Array([1, 2, 3, 4])) | ||
// A streaming buffer of bytes | ||
var streamBuffer = regl.buffer({ | ||
usage: 'stream', | ||
data: new Uint8Array([2, 4, 6, 8, 10]) | ||
}) | ||
// An unpacked buffer of position data | ||
var positionBuffer = regl.buffer([ | ||
[1, 2, 3], | ||
[4, 5, 6], | ||
[2, 1, -2] | ||
]) | ||
``` | ||
| Property | Description | Default | | ||
|----------|-------------|---------| | ||
| `data` | | `null` | | ||
| `length` | | `0` | | ||
| `usage` | | `'static'` | | ||
| `data` | The data for the vertex buffer (see below) | `null` | | ||
| `length` | If `data` is `null` or not present reserves space for the buffer | `0` | | ||
| `usage` | Sets array buffer usage hint | `'static'` | | ||
| Usage Hint | Description | | ||
|------------|-------------| | ||
| `'static'` | `gl.DRAW_STATIC` | | ||
| `'dynamic'` | `gl.DYNAMIC_DRAW` | | ||
| `'stream'` | `gl.STREAM_DRAW` | | ||
**Relevant WebGL APIs** | ||
* [`gl.createBuffer`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glCreateBuffer.xml) | ||
* [`gl.deleteBuffer`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glDeleteBuffer.xml) | ||
* [`gl.bufferData`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glBufferData.xml) | ||
--------------------------------------- | ||
#### Elements | ||
Examples, | ||
```javascript | ||
var triElements = regl.elements([ | ||
[1, 2, 3], | ||
[0, 2, 5] | ||
]) | ||
var starElements = regl.elements({ | ||
primitive: 'line loop', | ||
count: 5, | ||
data: new Uint8Array([0, 2, 4, 1, 3]) | ||
}) | ||
``` | ||
| Property | Description | Default | | ||
@@ -941,8 +997,231 @@ |----------|-------------|---------| | ||
* `usage` must take on one of the following values | ||
| Usage Hint | Description | | ||
|------------|-------------| | ||
| `'static'` | `gl.DRAW_STATIC` | | ||
| `'dynamic'` | `gl.DYNAMIC_DRAW` | | ||
| `'stream'` | `gl.STREAM_DRAW` | | ||
* `primitive` can be one of the following primitive types | ||
| Primitive type | Description | | ||
|-------|-------------| | ||
| `'points'` | `gl.POINTS` | | ||
| `'lines'` | gl.LINES` | | ||
| `'line strip'` | `gl.LINE_STRIP` | | ||
| `'line loop` | `gl.LINE_LOOP` | | ||
| `'triangles` | `gl.TRIANGLES` | | ||
| `'triangle strip'` | `gl.TRIANGLE_STRIP` | | ||
| `'triangle fan'` | `gl.TRIANGLE_FAN` | | ||
**Relevant WebGL APIs** | ||
* [`gl.createBuffer`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glCreateBuffer.xml) | ||
* [`gl.deleteBuffer`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glDeleteBuffer.xml) | ||
* [`gl.bufferData`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glBufferData.xml) | ||
* [`gl.drawElements`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glDrawElements.xml) | ||
--------------------------------------- | ||
#### Textures | ||
There are many ways to upload data to a texture in WebGL. As with drawing commands, regl consolidates all of these crazy configuration parameters into one function. Here are some examples of how to create a texture, | ||
**NOT YET IMPLEMENTED** | ||
```javascript | ||
// From size parameters | ||
var emptyTexture = regl.texture({ | ||
shape: [16, 16] | ||
}) | ||
// From a flat array | ||
var typedArrayTexture = regl.texture({ | ||
width: 2, | ||
height: 2, | ||
data: [ | ||
255, 255, 255, 255, 0, 0, 0, 0, | ||
255, 0, 255, 255, 0, 0, 255, 255 | ||
] | ||
}) | ||
// From a square array | ||
var nestedArrayTexture = regl.texture([ | ||
[ [0, 255, 0], [255, 0, 0] ], | ||
[ [0, 0, 255], [255, 255, 255] ] | ||
]) | ||
// From an ndarray-like object | ||
var ndarrayTexture = regl.texture(require('baboon-image')) | ||
// Manual mipmap specification | ||
var mipmapTexture = regl.texture({ | ||
minFilter: 'mipmap' | ||
}) | ||
// From an image element | ||
var image = new Image() | ||
image.src = 'http://mydomain.com/myimage.png' | ||
var imageTexture = regl.texture(image) | ||
// From a canvas | ||
var canvas = document.createElement(canvas) | ||
var context2D = canvas.getContext('2d') | ||
var canvasTexture = regl.texture(canvas) | ||
var otherCanvasTexture = regl.texture(context2D) | ||
// From a video element | ||
var video = document.querySelector('video') | ||
var videoTexture = regl.texture(video) | ||
// From the pixels in the current frame buffer | ||
var copyPixels = regl.texture({ | ||
x: 5, | ||
y: 1, | ||
width: 10, | ||
height: 10, | ||
copy: true | ||
}) | ||
``` | ||
A data source from an image can be one of the following types: | ||
| Data type | Description | | ||
|-----------|-------------| | ||
| Rectangular array of arrays | Interpreted as 2D array of arrays | | ||
| Typed array | A binary array of pixel values | | ||
| Array | Interpreted as array of pixel values with type based on the input type | | ||
| `ndarray` | Any object with a `shape, stride, offset, data` | | ||
| Image | An HTML image element | | ||
| Video | An HTML video element | | ||
| Canvas | A canvas element | | ||
| Context 2D | A canvas 2D context | | ||
| String | A URL to an image or video to load | | ||
| Property | Description | Default | | ||
|----------|-------------|---------| | ||
| `width` | Width of texture | `0` | | ||
| `height` | Height of texture | `0` | ||
| `mag` | Sets magnification filter (see table) | `'nearest'` | | ||
| `min` | Sets minification filter (see table) | `'nearest'` | | ||
| `wrapS` | Sets wrap mode on S axis (see table) | `'repeat'` | | ||
| `wrapT` | Sets wrap mode on T axis (see table) | `'repeat'` | | ||
| `aniso` | Sets number of anisotropic samples, requires [EXT_texture_filter_anisotropic](https://www.khronos.org/registry/webgl/extensions/EXT_texture_filter_anisotropic/) | `0` | | ||
| `format` | Texture format (see table) | `'rgba'` | | ||
| `type` | Texture type (see table) | `'uint8'` | | ||
| `data` | Input data (see below) | | | ||
| `mipmap` | If set, regenerate mipmaps | `false` | | ||
| `flipY` | Flips textures vertically when uploading | `false` | | ||
| `alignment` | Sets unpack alignment per pixel | `1` | | ||
| `premultiplyAlpha` | Premultiply alpha when unpacking | `false` | | ||
| `colorSpace` | Sets colorspace conversion | `'browser'` | | ||
| `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 | | ||
* `shape` can be used as an array shortcut for `[width, height, channels]` of image | ||
* `radius` can be specified for square images and sets both `width` and `height` | ||
* `data` can take one of the following values, | ||
* If an image element is specified and not yet loaded, then regl will upload a temporary image and hook a callback on the image | ||
* If a video element is specified, then regl will reupload a frame of the video element each tick unless `poll` is set to false | ||
* `mag` sets `gl.MAG_FILTER` for the texture and can have one of the following values | ||
| Mag filter | Description | | ||
|------------|-------------| | ||
| `'nearest'` | `gl.NEAREST` | | ||
| `'linear'` | `gl.LINEAR` | | ||
* `min` sets `gl.MIN_FILTER` for the texture, and can take on one of the following values, | ||
| Min filter | Description | | ||
|------------|-------------| | ||
| `'nearest'` | `gl.NEAREST` | | ||
| `'linear'` | `gl.LINEAR` | | ||
| `'mipmap', 'linear mipmap linear'` | `gl.LINEAR_MIPMAP_LINEAR` | | ||
| `'nearest mipmap linear'` | `gl.NEAREST_MIPMAP_LINEAR` | | ||
| `'linear mipmap nearest'` | `gl.LINEAR_MIPMAP_NEAREST` | | ||
| `'nearest mipmap nearest'` | `gl.NEAREST_MIPMAP_NEAREST` | | ||
* `wrap` can be used as an array shortcut for `[wrapS, wrapT]` | ||
* `wrapS` and `wrapT` can have any of the following values, | ||
| Wrap mode | Description | | ||
|-----------|-------------| | ||
| `'repeat'` | `gl.REPEAT` | | ||
| `'clamp'` | `gl.CLAMP_TO_EDGE` | | ||
| `'mirror'` | `gl.MIRRORED_REPEAT` | | ||
* `format` determines the format of the texture and possibly the type. Possible values for `format` include, | ||
| Format | Description | Channels | Types | Compressed? | Extension? | | ||
|--------|-------------|----------|-------|------|------------| | ||
| `'alpha'` | `gl.ALPHA` | 1 | `'uint8','half float','float'` | ✖ | | | ||
| `'luminance'` | `gl.LUMINANCE` | 1 | `'uint8','half float','float'` | ✖ | | | ||
| `'luminance alpha'` | `gl.LUMINANCE_ALPHA` | 2 | `'uint8','half float','float'` | ✖ | | | ||
| `'rgb'` | `gl.RGB` | 3 | `'uint8','half float','float'` | ✖ | | | ||
| `'rgba'` | `gl.RGBA` | 4 | `'uint8','half float','float'`| ✖ | | | ||
| `'rgba4'` | `gl.RGBA4` | 4 | `'rgba4'` | ✖ | | | ||
| `'rgb5 a1'` | `gl.RGB5_A1` | 4 | `'rgb5 a1'` | ✖ | | | ||
| `'rgb5'` | `gl.RGB5` | 3 | `'rgb5'` | ✖ | | | ||
| `'srgb'` | `ext.SRGB` | 3 | `'uint8','half float','float'` | ✖ | [EXT_sRGB](https://www.khronos.org/registry/webgl/extensions/EXT_sRGB/) | | ||
| `'srgba'` | `ext.RGBA` | 4 | `'uint8','half float','float'`| ✖ | [EXT_sRGB](https://www.khronos.org/registry/webgl/extensions/EXT_sRGB/) | | ||
| `'depth'` | `gl.DEPTH_COMPONENT` | 1 | `'uint16','uint32'` | ✖ | [WEBGL_depth_texture](https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) | | ||
| `'depth stencil'` | `gl.DEPTH_STENCIL` | 2 | `'depth stencil'` | ✖ | [WEBGL_depth_texture](https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) | | ||
| `'rgb s3tc dxt1'` | `ext.COMPRESSED_RGB_S3TC_DXT1_EXT` | 3 | `'uint8'` | ✓ | [WEBGL_compressed_texture_s3tc](https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_s3tc/) | | ||
| `'rgba s3tc dxt1'` | `ext.COMPRESSED_RGBA_S3TC_DXT1_EXT` | 4 | `'uint8'` | ✓ | [WEBGL_compressed_texture_s3tc](https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_s3tc/) | | ||
| `'rgba s3tc dxt3'` | `ext.COMPRESSED_RGBA_S3TC_DXT3_EXT` | 4 | `'uint8'` | ✓ | [WEBGL_compressed_texture_s3tc](https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_s3tc/) | | ||
| `'rgba s3tc dxt5'` | `ext.COMPRESSED_RGBA_S3TC_DXT5_EXT` | 4 | `'uint8'` | ✓ | [WEBGL_compressed_texture_s3tc](https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_s3tc/) | | ||
| `'rgb arc'` | `ext.COMPRESSED_RGB_ATC_WEBGL` | 3 | `'uint8'` | ✓ | [WEBGL_compressed_texture_atc](https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_atc/) | | ||
| `'rgba arc explicit alpha'` | `ext.COMPRESSED_RGBA_ATC_EXPLICIT_ALPHA_WEBGL` | 4 | `'uint8'` | ✓ | [WEBGL_compressed_texture_atc](https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_atc/) | | ||
| `'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 etc1'` | `ext.COMPRESSED_RGB_ETC1_WEBGL` | 3 | `'uint8'` | ✓ | [WEBGL_compressed_texture_etc1](https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_etc1/) | | ||
* In many cases `type` can be inferred from the format and other information in the texture. However, in some situations it may still be necessary to set it manually. In such an event, the following values are possible, | ||
| Type | Description | | ||
|------|-------------| | ||
| `'uint8'` | `gl.UNSIGNED_BYTE` | | ||
| `'uint16'` | `gl.UNSIGNED_SHORT` | | ||
| `'uint32'` | `gl.UNSIGNED_INT` | | ||
| `'float'` | `gl.FLOAT` | | ||
| `'half float'` | `ext.HALF_FLOAT_OES` | | ||
* `colorSpace` sets the WebGL color space flag for pixel unpacking | ||
| Color space | Description | | ||
|------------|-------------| | ||
| `'none'` | `gl.NONE` | | ||
| `'browser'` | `gl.BROWSER_DEFAULT_WEBGL` | | ||
* `unpackAlignment` sets the pixel unpack alignment and must be one of `[1, 2, 4, 8]` | ||
**Relevant WebGL APIs** | ||
* [`gl.createTexture`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glCreateTexture.xml) | ||
* [`gl.deleteTexture`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glDeleteTexture.xml) | ||
* [`gl.texParameter`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glTexParameter.xml) | ||
* [`gl.pixelStorei`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml) | ||
* [`gl.texImage2D`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glTexImage2D.xml) | ||
* [`gl.texImage2D`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glTexImage2D.xml) | ||
* [`gl.compressedTexImage2D`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glCompressedTexImage2D.xml) | ||
* [`gl.copyTexImage2D`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glCopyTexImage2D.xml) | ||
* [`gl.generateMipmap`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glGenerateMipmap.xml) | ||
--------------------------------------- | ||
#### Cube maps | ||
Cube maps follow similar syntax to textures. They are created using `regl.cube()` | ||
```javascript | ||
var cubeMap = regl.cube( | ||
'posx.jpg', | ||
'negx.jpg', | ||
'posy.jpg', | ||
'negy.jpg', | ||
'posz.jpg', | ||
'negz.jpg') | ||
``` | ||
--------------------------------------- | ||
#### Render buffers | ||
@@ -969,3 +1248,3 @@ | ||
depth: 1, | ||
stencil: 0xff | ||
stencil: 0 | ||
}) | ||
@@ -977,7 +1256,14 @@ ``` | ||
| `color` | Sets the clear color | | ||
| `depth` | | | ||
| `stencil` | | | ||
| `depth` | Sets the clear depth value | | ||
| `stencil` | Sets the clear stencil value | | ||
If an option is not present, then the corresponding buffer is not cleared | ||
**Relevant WebGL APIs** | ||
* [`gl.clearColor`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glClearColor.xml) | ||
* [`gl.clearDepth`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glClearDepth.xml) | ||
* [`gl.clearStencil`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glClearStencil.xml) | ||
* [`gl.clear`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glClear.xml) | ||
--------------------------------------- | ||
@@ -998,2 +1284,7 @@ ### Reading pixels | ||
**Relevant WebGL APIs** | ||
* [`gl.pixelStorei`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml) | ||
* [`gl.readPixels`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glReadPixels.xml) | ||
--------------------------------------- | ||
@@ -1028,4 +1319,34 @@ ### Per-frame callbacks | ||
--------------------------------------- | ||
### WebGL capabilities | ||
### Limits | ||
regl exposes info about the WebGL context limits and capabilities via the `regl.limits` object. The following properties are supported, | ||
| Property | Description | | ||
|----------|-------------| | ||
| `colorBits` | An array of bits depths for the red, green, blue and alpha channels | | ||
| `depthBits` | Bit depth of drawing buffer | | ||
| `stencilBits` | Bit depth of stencil buffer | | ||
| `subpixelBits` | `gl.SUBPIXEL_BITS` | | ||
| `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` | | ||
| `glsl` | `gl.SHADING_LANGUAGE_VERSION` | | ||
| `renderer` | `gl.RENDERER` | | ||
| `vendor` | `gl.VENDOR` | | ||
| `version` | `gl.VERSION` | | ||
**Relevant WebGL APIs** | ||
* [`gl.getParameter`](https://www.khronos.org/opengles/sdk/docs/man/xhtml/glGetParameter.xml) | ||
--------------------------------------- | ||
@@ -1032,0 +1353,0 @@ ### Clean up |
# Release notes | ||
## 0.2.0 | ||
* Texture support implemented, but not well tested | ||
* Fix attribute binding bug | ||
## 0.1.0 | ||
@@ -4,0 +9,0 @@ |
@@ -24,4 +24,4 @@ var glTypes = require('./constants/dtypes.json') | ||
equals: function (other, size) { | ||
if (this.pointer) { | ||
return other.pointer && | ||
if (!this.pointer) { | ||
return !other.pointer && | ||
this.x === other.x && | ||
@@ -32,3 +32,3 @@ this.y === other.y && | ||
} else { | ||
return !other.pointer && | ||
return other.pointer && | ||
this.buffer === other.buffer && | ||
@@ -35,0 +35,0 @@ this.size === size && |
// Array and element buffer creation | ||
var check = require('./check') | ||
var isTypedArray = require('./is-typed-array') | ||
var usageTypes = require('./constants/usage.json') | ||
var arrayTypes = require('./constants/arraytypes.json') | ||
@@ -11,2 +10,8 @@ | ||
var usageTypes = { | ||
'static': 35044, | ||
'dynamic': 35048, | ||
'stream': 35040 | ||
} | ||
function flatten (data, dimension) { | ||
@@ -126,12 +131,12 @@ var result = new Float32Array(data.length * dimension) | ||
function updateBuffer (options) { | ||
function reglBuffer (options) { | ||
buffer.update(options || {}) | ||
return updateBuffer | ||
return reglBuffer | ||
} | ||
updateBuffer._reglType = 'buffer' | ||
updateBuffer._buffer = buffer | ||
updateBuffer.destroy = function () { buffer.destroy() } | ||
reglBuffer._reglType = 'buffer' | ||
reglBuffer._buffer = buffer | ||
reglBuffer.destroy = function () { buffer.destroy() } | ||
return updateBuffer | ||
return reglBuffer | ||
} | ||
@@ -138,0 +143,0 @@ |
@@ -5,4 +5,5 @@ // Error checking and parameter validation | ||
function raise (message) { | ||
console.error(message) | ||
throw new Error(message) | ||
var error = new Error('(regl) ' + message) | ||
console.error(error) | ||
throw error | ||
} | ||
@@ -9,0 +10,0 @@ |
@@ -6,6 +6,2 @@ var check = require('./check') | ||
var glTypes = require('./constants/dtypes.json') | ||
var compareFuncs = require('./constants/comparefuncs.json') | ||
var blendFuncs = require('./constants/blendFuncs.json') | ||
var blendEquations = require('./constants/blendEquations.json') | ||
var stencilOps = require('./constants/stencil-ops.json') | ||
@@ -29,2 +25,4 @@ var GL_ELEMENT_ARRAY_BUFFER = 34963 | ||
var GL_FLOAT_MAT4 = 35676 | ||
var GL_SAMPLER_2D = 35678 | ||
var GL_SAMPLER_CUBE = 35680 | ||
@@ -49,2 +47,57 @@ var GL_TRIANGLES = 4 | ||
var GL_MIN_EXT = 0x8007 | ||
var GL_MAX_EXT = 0x8008 | ||
var blendFuncs = { | ||
'0': 0, | ||
'1': 1, | ||
'zero': 0, | ||
'one': 1, | ||
'src color': 768, | ||
'one minus src color': 769, | ||
'src alpha': 770, | ||
'one minus src alpha': 771, | ||
'dst color': 774, | ||
'one minus dst color': 775, | ||
'dst alpha': 772, | ||
'one minus dst alpha': 773, | ||
'constant color': 32769, | ||
'one minus constant color': 32770, | ||
'constant alpha': 32771, | ||
'one minus constant alpha': 32772, | ||
'src alpha saturate': 776 | ||
} | ||
var compareFuncs = { | ||
'never': 512, | ||
'less': 513, | ||
'<': 513, | ||
'equal': 514, | ||
'=': 514, | ||
'==': 514, | ||
'===': 514, | ||
'lequal': 515, | ||
'<=': 515, | ||
'greater': 516, | ||
'>': 516, | ||
'notequal': 517, | ||
'!=': 517, | ||
'!==': 517, | ||
'gequal': 518, | ||
'>=': 518, | ||
'always': 519 | ||
} | ||
var stencilOps = { | ||
'0': 0, | ||
'zero': 0, | ||
'keep': 7680, | ||
'replace': 7681, | ||
'increment': 7682, | ||
'decrement': 7683, | ||
'increment wrap': 34055, | ||
'decrement wrap': 34056, | ||
'invert': 5386 | ||
} | ||
function typeLength (x) { | ||
@@ -139,2 +192,12 @@ switch (x) { | ||
var blendEquations = { | ||
'add': 32774, | ||
'subtract': 32778, | ||
'reverse subtract': 32779 | ||
} | ||
if (extensions.ext_blend_minmax) { | ||
blendEquations.min = GL_MIN_EXT | ||
blendEquations.max = GL_MAX_EXT | ||
} | ||
var drawCallCounter = 0 | ||
@@ -163,2 +226,3 @@ | ||
var ELEMENT_STATE = link(elementState.elements) | ||
var TEXTURE_UNIFORMS = [] | ||
@@ -183,5 +247,17 @@ // bind the program | ||
var TOP = STACK + '[' + STACK + '.length-1]' | ||
draw(setUniformString(GL, uniform.info.type, LOCATION, TOP)) | ||
if (uniform.info.type === GL_SAMPLER_2D || | ||
uniform.info.type === GL_SAMPLER_CUBE) { | ||
var TEX_VALUE = def(TOP + '._texture') | ||
TEXTURE_UNIFORMS.push(TEX_VALUE) | ||
draw(setUniformString(GL, GL_INT, LOCATION, TEX_VALUE + '.bind()')) | ||
} else { | ||
draw(setUniformString(GL, uniform.info.type, LOCATION, TOP)) | ||
} | ||
}) | ||
// unbind textures immediately | ||
TEXTURE_UNIFORMS.forEach(function (TEX_VALUE) { | ||
draw(TEX_VALUE, '.unbind();') | ||
}) | ||
// Execute draw command | ||
@@ -339,3 +415,10 @@ var CUR_PRIMITIVE = def(stackTop(DRAW_STATE.primitive)) | ||
var TOP = STACK + '[' + STACK + '.length-1]' | ||
batch(setUniformString(GL, uniform.info.type, LOCATION, TOP)) | ||
if (uniform.info.type === GL_SAMPLER_2D || | ||
uniform.info.type === GL_SAMPLER_CUBE) { | ||
var TEX_VALUE = def(TOP + '._texture') | ||
batch(setUniformString(GL, GL_INT, LOCATION, TEX_VALUE + '.bind()')) | ||
exit(TEX_VALUE, '.unbind();') | ||
} else { | ||
batch(setUniformString(GL, uniform.info.type, LOCATION, TOP)) | ||
} | ||
}) | ||
@@ -558,2 +641,3 @@ | ||
var programUniforms = program.uniforms | ||
var DYNAMIC_TEXTURES = [] | ||
Object.keys(uniforms).forEach(function (uniform) { | ||
@@ -567,4 +651,14 @@ var data = findInfo(programUniforms, uniform) | ||
var VALUE = dyn(uniforms[uniform]) | ||
batch(setUniformString(GL, TYPE, LOCATION, VALUE)) | ||
if (data.info.type === GL_SAMPLER_2D || | ||
data.info.type === GL_SAMPLER_CUBE) { | ||
var TEX_VALUE = def(VALUE + '._texture') | ||
DYNAMIC_TEXTURES.push(TEX_VALUE) | ||
batch(setUniformString(GL, GL_INT, LOCATION, TEX_VALUE + '.bind()')) | ||
} else { | ||
batch(setUniformString(GL, TYPE, LOCATION, VALUE)) | ||
} | ||
}) | ||
DYNAMIC_TEXTURES.forEach(function (VALUE) { | ||
batch(VALUE, '.unbind();') | ||
}) | ||
@@ -1057,3 +1151,3 @@ // ------------------------------- | ||
PROGRAM_STATE, '.push(', | ||
LINK_PROG, '(', FRAG_SRC, ',', VERT_SRC, '));') | ||
LINK_PROG, '(', VERT_SRC, ',', FRAG_SRC, '));') | ||
} | ||
@@ -1071,3 +1165,5 @@ exit(PROGRAM_STATE, '.pop();') | ||
var value = staticUniforms[uniform] | ||
if (Array.isArray(value)) { | ||
if (typeof value === 'function' && value._reglType) { | ||
VALUE = link(value) | ||
} else if (Array.isArray(value)) { | ||
VALUE = link(value.slice()) | ||
@@ -1243,9 +1339,9 @@ } else { | ||
'if(typeof ', variable, '==="string"){', | ||
BLEND_EQUATION_STACK, '.push(', | ||
BLEND_EQUATIONS, '[', variable, '],', | ||
BLEND_EQUATIONS, '[', variable, ']);', | ||
BLEND_EQUATION_STACK, '.push(', | ||
BLEND_EQUATIONS, '[', variable, '],', | ||
BLEND_EQUATIONS, '[', variable, ']);', | ||
'}else{', | ||
BLEND_EQUATION_STACK, '.push(', | ||
BLEND_EQUATIONS, '[', variable, '.rgb],', | ||
BLEND_EQUATIONS, '[', variable, '.alpha]);', | ||
BLEND_EQUATION_STACK, '.push(', | ||
BLEND_EQUATIONS, '[', variable, '.rgb],', | ||
BLEND_EQUATIONS, '[', variable, '.alpha]);', | ||
'}') | ||
@@ -1343,9 +1439,9 @@ dynamicExit(BLEND_EQUATION_STACK, '.pop();') | ||
var hasPrimitive = | ||
!('primitive' in dynamicOptions) && | ||
!('primitive' in dynamicOptions) && | ||
!('primitive' in staticOptions) | ||
var hasCount = | ||
!('count' in dynamicOptions) && | ||
!('count' in dynamicOptions) && | ||
!('count' in staticOptions) | ||
var hasOffset = | ||
!('offset' in dynamicOptions) && | ||
!('offset' in dynamicOptions) && | ||
!('offset' in staticOptions) | ||
@@ -1352,0 +1448,0 @@ var ELEMENTS = dynamicEntry.def() |
@@ -27,3 +27,3 @@ // Context and canvas creation helper functions | ||
var scale = +window.devicePixelRatio | ||
var scale = +args.options.pixelRatio | ||
function resize () { | ||
@@ -62,3 +62,3 @@ var w = window.innerWidth | ||
function getContext (canvas, options) { | ||
var glOptions = options.glOptions | ||
var glOptions = options.glOptions || {} | ||
@@ -81,3 +81,5 @@ function get (name) { | ||
gl: gl, | ||
options: options | ||
options: Object.assign({ | ||
pixelRatio: window.devicePixelRatio | ||
}, options) | ||
} | ||
@@ -106,3 +108,5 @@ } | ||
gl: args[0], | ||
options: options | ||
options: Object.assign({ | ||
pixelRatio: 1 | ||
}, options) | ||
} | ||
@@ -109,0 +113,0 @@ } else { |
@@ -1,6 +0,2 @@ | ||
// Extension wrangling | ||
var check = require('./check') | ||
module.exports = function createExtensionCache (gl, required) { | ||
module.exports = function createExtensionCache (gl) { | ||
var extensions = {} | ||
@@ -29,3 +25,8 @@ | ||
'angle_instanced_arrays' | ||
'angle_instanced_arrays', | ||
'webgl_compressed_texture_s3tc', | ||
'webgl_compressed_texture_atc', | ||
'webgl_compressed_texture_pvrtc', | ||
'webgl_compressed_texture_etc1' | ||
].forEach(function (ext) { | ||
@@ -36,7 +37,2 @@ try { | ||
}) | ||
required.forEach(function (ext) { | ||
check(extensions[ext.toLowerCase()], | ||
'required extension "' + ext + '" is unsupported') | ||
}) | ||
} | ||
@@ -43,0 +39,0 @@ |
@@ -156,3 +156,3 @@ var check = require('./check') | ||
if (!cache) { | ||
cache = programCache[vertSource] = {} | ||
cache = programCache[fragSource] = {} | ||
} | ||
@@ -159,0 +159,0 @@ var program = cache[vertSource] |
1398
lib/texture.js
var check = require('./check') | ||
var isTypedArray = require('./is-typed-array') | ||
var loadTexture = require('./load-texture') | ||
var convertToHalfFloat = require('./to-half-float') | ||
var parseDDS = require('./parse-dds') | ||
var GL_COMPRESSED_TEXTURE_FORMATS = 0x86A3 | ||
var GL_TEXTURE_2D = 0x0DE1 | ||
var GL_TEXTURE_CUBE_MAP = 0x8513 | ||
var GL_TEXTURE_CUBE_MAP_POSITIVE_X = 0x8515 | ||
var GL_DEPTH_COMPONENT = 0x1902 | ||
var GL_RGBA = 0x1908 | ||
var GL_ALPHA = 0x1906 | ||
var GL_RGB = 0x1907 | ||
var GL_RGBA = 0x1908 | ||
var GL_LUMINANCE = 0x1909 | ||
var GL_LUMINANCE_ALPHA = 0x190A | ||
var GL_RGBA4 = 0x8056 | ||
var GL_RGB5_A1 = 0x8057 | ||
var GL_RGB565 = 0x8D62 | ||
var GL_UNSIGNED_SHORT_4_4_4_4 = 0x8033 | ||
var GL_UNSIGNED_SHORT_5_5_5_1 = 0x8034 | ||
var GL_UNSIGNED_SHORT_5_6_5 = 0x8363 | ||
var GL_UNSIGNED_INT_24_8_WEBGL = 0x84FA | ||
var GL_DEPTH_COMPONENT = 0x1902 | ||
var GL_DEPTH_STENCIL = 0x84F9 | ||
var GL_SRGB_EXT = 0x8C40 | ||
var GL_SRGB_ALPHA_EXT = 0x8C42 | ||
var GL_HALF_FLOAT_OES = 0x8D61 | ||
var GL_COMPRESSED_RGB_S3TC_DXT1_EXT = 0x83F0 | ||
var GL_COMPRESSED_RGBA_S3TC_DXT1_EXT = 0x83F1 | ||
var GL_COMPRESSED_RGBA_S3TC_DXT3_EXT = 0x83F2 | ||
var GL_COMPRESSED_RGBA_S3TC_DXT5_EXT = 0x83F3 | ||
var GL_COMPRESSED_RGB_ATC_WEBGL = 0x8C92 | ||
var GL_COMPRESSED_RGBA_ATC_EXPLICIT_ALPHA_WEBGL = 0x8C93 | ||
var GL_COMPRESSED_RGBA_ATC_INTERPOLATED_ALPHA_WEBGL = 0x87EE | ||
var GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG = 0x8C00 | ||
var GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG = 0x8C01 | ||
var GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG = 0x8C02 | ||
var GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG = 0x8C03 | ||
var GL_COMPRESSED_RGB_ETC1_WEBGL = 0x8D64 | ||
var GL_UNSIGNED_BYTE = 0x1401 | ||
var GL_UNSIGNED_SHORT = 0x1403 | ||
var GL_UNSIGNED_INT = 0x1405 | ||
var GL_FLOAT = 0x1406 | ||
@@ -33,120 +74,1291 @@ | ||
var GL_GENERATE_MIPMAP_HINT = 0x8192 | ||
var GL_DONT_CARE = 0x1100 | ||
var GL_FASTEST = 0x1101 | ||
var GL_NICEST = 0x1102 | ||
var GL_TEXTURE_MAX_ANISOTROPY_EXT = 0x84FE | ||
var GL_UNPACK_ALIGNMENT = 0x0CF5 | ||
var GL_UNPACK_FLIP_Y_WEBGL = 0x9240 | ||
var GL_UNPACK_PREMULTIPLY_ALPHA_WEBGL = 0x9241 | ||
var GL_UNPACK_COLORSPACE_CONVERSION_WEBGL = 0x9243 | ||
var GL_BROWSER_DEFAULT_WEBGL = 0x9244 | ||
var wrapModes = { | ||
'repeat': GL_REPEAT, | ||
'clamp': GL_CLAMP_TO_EDGE, | ||
'mirror': GL_MIRRORED_REPEAT | ||
var GL_TEXTURE0 = 0x84C0 | ||
var MIPMAP_FILTERS = [ | ||
GL_NEAREST_MIPMAP_NEAREST, | ||
GL_NEAREST_MIPMAP_LINEAR, | ||
GL_LINEAR_MIPMAP_NEAREST, | ||
GL_LINEAR_MIPMAP_LINEAR | ||
] | ||
function isPow2 (v) { | ||
return !(v & (v - 1)) && (!!v) | ||
} | ||
var magFilters = { | ||
'nearest': GL_NEAREST, | ||
'linear': GL_LINEAR | ||
function isNumericArray (arr) { | ||
return ( | ||
Array.isArray(arr) && | ||
(arr.length === 0 || | ||
typeof arr[0] === 'number')) | ||
} | ||
var minFilters = Object.assign({ | ||
'nearest mipmap nearest': GL_NEAREST_MIPMAP_NEAREST, | ||
'linear mipmap nearest': GL_LINEAR_MIPMAP_NEAREST, | ||
'nearest mipmap linear': GL_NEAREST_MIPMAP_LINEAR, | ||
'linear mipmap linear': GL_LINEAR_MIPMAP_LINEAR, | ||
'mipmap': GL_LINEAR_MIPMAP_LINEAR | ||
}, magFilters) | ||
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))) | ||
} | ||
module.exports = function createTextureSet (gl, extensionState) { | ||
function isRectArray (arr) { | ||
if (!Array.isArray(arr)) { | ||
return false | ||
} | ||
var width = arr.length | ||
if (width === 0 || !Array.isArray(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 | ||
} | ||
function classString (x) { | ||
return Object.prototype.toString.call(x) | ||
} | ||
function isCanvasElement (object) { | ||
return classString(object) === '[object HTMLCanvasElement]' | ||
} | ||
function isContext2D (object) { | ||
return classString(object) === '[object CanvasRenderingContext2D]' | ||
} | ||
function isImageElement (object) { | ||
return classString(object) === '[object HTMLImageElement]' | ||
} | ||
function isVideoElement (object) { | ||
return classString(object) === '[object HTMLVideoElement]' | ||
} | ||
function isPendingXHR (object) { | ||
return classString(object) === '[object XMLHttpRequest]' | ||
} | ||
function isPixelData (object) { | ||
return ( | ||
typeof object === 'string' || | ||
(!!object && ( | ||
isTypedArray(object) || | ||
isNumericArray(object) || | ||
isNDArrayLike(object) || | ||
isCanvasElement(object) || | ||
isContext2D(object) || | ||
isImageElement(object) || | ||
isVideoElement(object) || | ||
isRectArray(object)))) | ||
} | ||
// Transpose an array of pixels | ||
function transposePixels (data, nx, ny, nc, sx, sy, sc, off) { | ||
var result = new data.constructor(nx * ny * nc) | ||
var ptr = 0 | ||
for (var i = 0; i < ny; ++i) { | ||
for (var j = 0; j < nx; ++j) { | ||
for (var k = 0; k < nc; ++k) { | ||
result[ptr++] = data[sy * i + sx * j + sc * k + off] | ||
} | ||
} | ||
} | ||
return result | ||
} | ||
module.exports = function createTextureSet (gl, extensionState, limits, reglPoll, viewport) { | ||
var extensions = extensionState.extensions | ||
var textureCount = 0 | ||
var textureSet = {} | ||
var mipmapHint = { | ||
"don't care": GL_DONT_CARE, | ||
'dont care': GL_DONT_CARE, | ||
'nice': GL_NICEST, | ||
'fast': GL_FASTEST | ||
} | ||
function REGLTexture () { | ||
this.id = textureCount++ | ||
var wrapModes = { | ||
'repeat': GL_REPEAT, | ||
'clamp': GL_CLAMP_TO_EDGE, | ||
'mirror': GL_MIRRORED_REPEAT | ||
} | ||
// Texture target | ||
this.target = GL_TEXTURE_2D | ||
var magFilters = { | ||
'nearest': GL_NEAREST, | ||
'linear': GL_LINEAR | ||
} | ||
// Texture handle | ||
this.texture = null | ||
var minFilters = Object.assign({ | ||
'nearest mipmap nearest': GL_NEAREST_MIPMAP_NEAREST, | ||
'linear mipmap nearest': GL_LINEAR_MIPMAP_NEAREST, | ||
'nearest mipmap linear': GL_NEAREST_MIPMAP_LINEAR, | ||
'linear mipmap linear': GL_LINEAR_MIPMAP_LINEAR, | ||
'mipmap': GL_LINEAR_MIPMAP_LINEAR | ||
}, magFilters) | ||
// Texture format | ||
this.format = GL_RGBA | ||
this.type = GL_UNSIGNED_BYTE | ||
var colorSpace = { | ||
'none': 0, | ||
'browser': GL_BROWSER_DEFAULT_WEBGL | ||
} | ||
// Data | ||
this.mipLevels = [] | ||
var textureTypes = { | ||
'uint8': GL_UNSIGNED_BYTE, | ||
'rgba4': GL_UNSIGNED_SHORT_4_4_4_4, | ||
'rgb565': GL_UNSIGNED_SHORT_5_6_5, | ||
'rgb5 a1': GL_UNSIGNED_SHORT_5_5_5_1 | ||
} | ||
// Shape | ||
this.width = 0 | ||
this.height = 0 | ||
var textureFormats = { | ||
'alpha': GL_ALPHA, | ||
'luminance': GL_LUMINANCE, | ||
'luminance alpha': GL_LUMINANCE_ALPHA, | ||
'rgb': GL_RGB, | ||
'rgba': GL_RGBA, | ||
'rgba4': GL_RGBA4, | ||
'rgb5 a1': GL_RGB5_A1, | ||
'rgb565': GL_RGB565 | ||
} | ||
// Parameters | ||
this.minFilter = GL_NEAREST | ||
this.magFilter = GL_NEAREST | ||
this.wrapS = GL_REPEAT | ||
this.wrapT = GL_REPEAT | ||
this.mipSamples = 0 | ||
var compressedTextureFormats = {} | ||
// Storage flags | ||
if (extensions.ext_srgb) { | ||
textureFormats.srgb = GL_SRGB_EXT | ||
textureFormats.srgba = GL_SRGB_ALPHA_EXT | ||
} | ||
if (extensions.oes_texture_float) { | ||
textureTypes.float = GL_FLOAT | ||
} | ||
if (extensions.oes_texture_half_float) { | ||
textureTypes['half float'] = GL_HALF_FLOAT_OES | ||
} | ||
if (extensions.webgl_depth_texture) { | ||
Object.assign(textureFormats, { | ||
'depth': GL_DEPTH_COMPONENT, | ||
'depth stencil': GL_DEPTH_STENCIL | ||
}) | ||
Object.assign(textureTypes, { | ||
'uint16': GL_UNSIGNED_SHORT, | ||
'uint32': GL_UNSIGNED_INT, | ||
'depth stencil': GL_UNSIGNED_INT_24_8_WEBGL | ||
}) | ||
} | ||
if (extensions.webgl_compressed_texture_s3tc) { | ||
Object.assign(compressedTextureFormats, { | ||
'rgb s3tc dxt1': GL_COMPRESSED_RGB_S3TC_DXT1_EXT, | ||
'rgba s3tc dxt1': GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, | ||
'rgba s3tc dxt3': GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, | ||
'rgba s3tc dxt5': GL_COMPRESSED_RGBA_S3TC_DXT5_EXT | ||
}) | ||
} | ||
if (extensions.webgl_compressed_texture_atc) { | ||
Object.assign(compressedTextureFormats, { | ||
'rgb arc': GL_COMPRESSED_RGB_ATC_WEBGL, | ||
'rgba atc explicit alpha': GL_COMPRESSED_RGBA_ATC_EXPLICIT_ALPHA_WEBGL, | ||
'rgba atc interpolated alpha': GL_COMPRESSED_RGBA_ATC_INTERPOLATED_ALPHA_WEBGL | ||
}) | ||
} | ||
if (extensions.webgl_compressed_texture_pvrtc) { | ||
Object.assign(compressedTextureFormats, { | ||
'rgb pvrtc 4bppv1': GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG, | ||
'rgb pvrtc 2bppv1': GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG, | ||
'rgba pvrtc 4bppv1': GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG, | ||
'rgba pvrtc 2bppv1': GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG | ||
}) | ||
} | ||
if (extensions.webgl_compressed_texture_etc1) { | ||
compressedTextureFormats['rgb etc1'] = GL_COMPRESSED_RGB_ETC1_WEBGL | ||
} | ||
// Copy over all texture formats | ||
var supportedCompressedFormats = Array.prototype.slice.call( | ||
gl.getParameter(GL_COMPRESSED_TEXTURE_FORMATS)) | ||
Object.keys(compressedTextureFormats).forEach(function (name) { | ||
var format = compressedTextureFormats[name] | ||
if (supportedCompressedFormats.indexOf(format) >= 0) { | ||
textureFormats[name] = format | ||
} | ||
}) | ||
var supportedFormats = Object.keys(textureFormats) | ||
limits.textureFormats = supportedFormats | ||
var colorFormats = supportedFormats.reduce(function (color, key) { | ||
var glenum = textureFormats[key] | ||
if (glenum === GL_LUMINANCE || | ||
glenum === GL_ALPHA || | ||
glenum === GL_LUMINANCE || | ||
glenum === GL_LUMINANCE_ALPHA || | ||
glenum === GL_DEPTH_COMPONENT || | ||
glenum === GL_DEPTH_STENCIL) { | ||
color[glenum] = glenum | ||
} else if (glenum === GL_RGB5_A1 || key.indexOf('rgba') >= 0) { | ||
color[glenum] = GL_RGBA | ||
} else { | ||
color[glenum] = GL_RGB | ||
} | ||
return color | ||
}, {}) | ||
// Pixel storage parsing | ||
function PixelInfo (target) { | ||
// tex target | ||
this.target = target | ||
// pixelStorei info | ||
this.flipY = false | ||
this.premultiplyAlpha = false | ||
this.unpackAlignment = 1 | ||
this.colorSpace = GL_BROWSER_DEFAULT_WEBGL | ||
// shape | ||
this.width = 0 | ||
this.height = 0 | ||
this.channels = 0 | ||
// format and type | ||
this.format = 0 | ||
this.internalformat = 0 | ||
this.type = 0 | ||
this.compressed = false | ||
// mip level | ||
this.miplevel = 0 | ||
// ndarray-like parameters | ||
this.strideX = 0 | ||
this.strideY = 0 | ||
this.strideC = 0 | ||
this.offset = 0 | ||
// copy pixels info | ||
this.x = 0 | ||
this.y = 0 | ||
this.copy = false | ||
// data sources | ||
this.data = null | ||
this.image = null | ||
this.video = null | ||
this.canvas = null | ||
this.xhr = null | ||
// CORS | ||
this.crossOrigin = null | ||
// horrible state flags | ||
this.needsPoll = false | ||
this.needsListeners = false | ||
} | ||
Object.assign(REGLTexture.prototype, { | ||
bind: function () { | ||
Object.assign(PixelInfo.prototype, { | ||
parseFlags: function (options) { | ||
if (typeof options !== 'object' || !options) { | ||
return | ||
} | ||
if ('premultiplyAlpha' in options) { | ||
check.type(options.premultiplyAlpha, 'boolean', | ||
'invalid premultiplyAlpha') | ||
this.premultiplyAlpha = options.premultiplyAlpha | ||
} | ||
if ('flipY' in options) { | ||
check.type(options.flipY, 'boolean', | ||
'invalid texture flip') | ||
this.flipY = options.flipY | ||
} | ||
if ('alignment' in options) { | ||
check.oneOf(options.alignment, [1, 2, 4, 8], | ||
'invalid texture unpack alignment') | ||
this.unpackAlignment = options.alignment | ||
} | ||
if ('colorSpace' in options) { | ||
check.parameter(options.colorSpace, colorSpace, | ||
'invalid colorSpace') | ||
this.colorSpace = colorSpace[options.colorSpace] | ||
} | ||
if ('format' in options) { | ||
var format = options.format | ||
check.parameter(format, textureFormats, | ||
'invalid texture format') | ||
this.internalformat = textureFormats[format] | ||
if (format in textureTypes) { | ||
this.type = textureTypes[format] | ||
} | ||
if (format in compressedTextureFormats) { | ||
this.compressed = true | ||
} | ||
} | ||
if ('type' in options) { | ||
var type = options.type | ||
check.parameter(type, textureTypes, | ||
'invalid texture type') | ||
this.type = textureTypes[type] | ||
} | ||
var w = this.width | ||
var h = this.height | ||
var c = this.channels | ||
if ('shape' in options) { | ||
check(Array.isArray(options.shape) && options.shape.length >= 2, | ||
'shape must be an array') | ||
w = options.shape[0] | ||
h = options.shape[1] | ||
if (options.shape.length === 3) { | ||
c = options.shape[2] | ||
} | ||
} else { | ||
if ('radius' in options) { | ||
w = h = options.radius | ||
} | ||
if ('width' in options) { | ||
w = options.width | ||
} | ||
if ('height' in options) { | ||
h = options.height | ||
} | ||
if ('channels' in options) { | ||
c = options.channels | ||
} | ||
} | ||
this.width = w | 0 | ||
this.height = h | 0 | ||
this.channels = c | 0 | ||
if ('stride' in options) { | ||
var stride = options.stride | ||
check(Array.isArray(stride) && stride.length >= 2, | ||
'invalid stride vector') | ||
this.strideX = stride[0] | ||
this.strideY = stride[1] | ||
if (stride.length === 3) { | ||
this.strideC = stride[2] | ||
} else { | ||
this.strideC = 1 | ||
} | ||
this.needsTranspose = true | ||
} else { | ||
this.strideC = 1 | ||
this.strideX = this.strideC * c | ||
this.strideY = this.strideX * w | ||
} | ||
if ('offset' in options) { | ||
this.offset = options.offset | 0 | ||
this.needsTranspose = true | ||
} | ||
if ('crossOrigin' in options) { | ||
this.crossOrigin = options.crossOrigin | ||
} | ||
}, | ||
parse: function (options, miplevel) { | ||
this.miplevel = miplevel | ||
this.width = this.width >> miplevel | ||
this.height = this.height >> miplevel | ||
update: function (args) { | ||
var options = args || {} | ||
var data = options | ||
switch (typeof options) { | ||
case 'string': | ||
break | ||
case 'object': | ||
if (!options) { | ||
return | ||
} | ||
this.parseFlags(options) | ||
if (isPixelData(options.data)) { | ||
data = options.data | ||
} | ||
break | ||
case 'undefined': | ||
return | ||
default: | ||
check.raise('invalid pixel data type') | ||
} | ||
// Possible initialization pathways: | ||
if (Array.isArray(args) || | ||
isTypedArray(args) || | ||
isHTMLElement(args)) { | ||
options = { | ||
data: args | ||
if (typeof data === 'string') { | ||
data = loadTexture(data, this.crossOrigin) | ||
} else { | ||
check(!data || isPixelData(data), 'invalid pixel data') | ||
} | ||
var array = null | ||
var needsConvert = false | ||
if (this.compressed) { | ||
check(data instanceof Uint8Array || isPendingXHR(data), | ||
'compressed texture data must be stored in a uint8array') | ||
} | ||
if (data === null) { | ||
// TODO | ||
} else if (isTypedArray(data)) { | ||
this.data = data | ||
} else if (isNumericArray(data)) { | ||
array = data | ||
needsConvert = true | ||
} else if (isNDArrayLike(data)) { | ||
if (Array.isArray(data.data)) { | ||
array = data.data | ||
needsConvert = true | ||
} else { | ||
this.data = data.data | ||
} | ||
var shape = data.shape | ||
this.width = shape[0] | ||
this.height = shape[1] | ||
if (shape.length === 3) { | ||
this.channels = shape[2] | ||
} else { | ||
this.channels = 1 | ||
} | ||
var stride = data.stride | ||
this.strideX = data.stride[0] | ||
this.strideY = data.stride[1] | ||
if (stride.length === 3) { | ||
this.strideC = data.stride[2] | ||
} else { | ||
this.strideC = 1 | ||
} | ||
this.offset = data.offset | ||
this.needsTranspose = true | ||
} else if (isCanvasElement(data) || isContext2D(data)) { | ||
if (isCanvasElement(data)) { | ||
this.canvas = data | ||
} else { | ||
this.canvas = data.canvas | ||
} | ||
this.width = this.canvas.width | ||
this.height = this.canvas.height | ||
this.setDefaultFormat() | ||
} else if (isImageElement(data)) { | ||
this.image = data | ||
if (!data.complete) { | ||
this.width = this.width || data.naturalWidth | ||
this.height = this.height || data.naturalHeight | ||
this.needsListeners = true | ||
} else { | ||
this.width = data.naturalWidth | ||
this.height = data.naturalHeight | ||
} | ||
this.setDefaultFormat() | ||
} else if (isVideoElement(data)) { | ||
this.video = data | ||
if (data.readyState > 1) { | ||
this.width = data.width | ||
this.height = data.height | ||
} else { | ||
this.width = this.width || data.width | ||
this.height = this.height || data.height | ||
this.needsListeners = true | ||
} | ||
this.needsPoll = true | ||
this.setDefaultFormat() | ||
} else if (isPendingXHR(data)) { | ||
this.xhr = data | ||
this.needsListeners = true | ||
} else if (isRectArray(data)) { | ||
var w = data.length | ||
var h = data[0].length | ||
var c = 1 | ||
var i, j, k, p | ||
if (Array.isArray(data[0][0])) { | ||
c = data[0][0].length | ||
check(c >= 0 && c <= 4, 'invalid number of channels for image data') | ||
array = Array(w * h * c) | ||
p = 0 | ||
for (i = 0; i < w; ++i) { | ||
for (j = 0; j < h; ++j) { | ||
for (k = 0; k < c; ++k) { | ||
array[p++] = data[i][j][k] | ||
} | ||
} | ||
} | ||
} else { | ||
array = Array(w * h) | ||
p = 0 | ||
for (i = 0; i < w; ++i) { | ||
for (j = 0; j < h; ++j) { | ||
array[p++] = data[i][j] | ||
} | ||
} | ||
} | ||
this.width = w | ||
this.height = h | ||
this.channels = c | ||
needsConvert = true | ||
} else if (options.copy) { | ||
this.copy = true | ||
this.x = this.x | 0 | ||
this.y = this.y | 0 | ||
this.width = (this.width || viewport.width) | 0 | ||
this.height = (this.height || viewport.height) | 0 | ||
this.setDefaultFormat() | ||
} | ||
var data = options.data || null | ||
var width = options.width || 0 | ||
var height = options.height || 0 | ||
var format = options.format || 'rgba' | ||
// Fix up missing type info for typed arrays | ||
if (!this.type && this.data) { | ||
if (this.format === GL_DEPTH_COMPONENT) { | ||
if (this.data instanceof Uint16Array) { | ||
this.type = GL_UNSIGNED_SHORT | ||
} else if (this.data instanceof Uint32Array) { | ||
this.type = GL_UNSIGNED_INT | ||
} | ||
} else if (this.data instanceof Float32Array) { | ||
this.type = GL_FLOAT | ||
} | ||
} | ||
this.minFilter = GL_NEAREST | ||
// Infer default format | ||
if (!this.internalformat) { | ||
var channels = this.channels = this.channels || 4 | ||
this.internalformat = [ | ||
GL_LUMINANCE, | ||
GL_LUMINANCE_ALPHA, | ||
GL_RGB, | ||
GL_RGBA][channels - 1] | ||
check(this.internalformat, 'invalid number of channels') | ||
} | ||
var format = this.internalformat | ||
if (format === GL_DEPTH_COMPONENT || format === GL_DEPTH_STENCIL) { | ||
check(extensions.webgl_depth_texture, | ||
'depth/stencil texture not supported') | ||
if (format === GL_DEPTH_COMPONENT) { | ||
check(this.type === GL_UNSIGNED_SHORT || GL_UNSIGNED_INT, | ||
'depth texture type must be uint16 or uint32') | ||
} | ||
if (format === GL_DEPTH_STENCIL) { | ||
check(this.type === GL_UNSIGNED_INT_24_8_WEBGL, | ||
'depth stencil texture format must match type') | ||
} | ||
check( | ||
!this.data && !array && !this.image && !this.video && !this.canvas, | ||
'depth/stencil textures are for rendering only') | ||
} | ||
// Compute color format and number of channels | ||
var colorFormat = this.format = colorFormats[format] | ||
if (!this.channels) { | ||
switch (colorFormat) { | ||
case GL_LUMINANCE: | ||
case GL_ALPHA: | ||
case GL_DEPTH_COMPONENT: | ||
this.channels = 1 | ||
break | ||
case GL_DEPTH_STENCIL: | ||
case GL_LUMINANCE_ALPHA: | ||
this.channels = 2 | ||
break | ||
case GL_RGB: | ||
this.channels = 3 | ||
break | ||
default: | ||
this.channels = 4 | ||
} | ||
} | ||
// Check that texture type is supported | ||
var type = this.type | ||
if (type === GL_FLOAT) { | ||
check(extensions.oes_texture_float, | ||
'float texture not supported') | ||
} else if (type === GL_HALF_FLOAT_OES) { | ||
check(extensions.oes_texture_half_float, | ||
'half float texture not supported') | ||
} else if (!type) { | ||
if (format === GL_DEPTH_COMPONENT) { | ||
type = GL_UNSIGNED_INT | ||
} else { | ||
type = GL_UNSIGNED_BYTE | ||
} | ||
} | ||
this.type = type | ||
// apply conversion | ||
if (needsConvert) { | ||
switch (type) { | ||
case GL_UNSIGNED_BYTE: | ||
this.data = new Uint8Array(array) | ||
break | ||
case GL_UNSIGNED_SHORT: | ||
this.data = new Uint16Array(array) | ||
break | ||
case GL_UNSIGNED_INT: | ||
this.data = new Uint32Array(array) | ||
break | ||
case GL_FLOAT: | ||
this.data = new Float32Array(array) | ||
break | ||
case GL_HALF_FLOAT_OES: | ||
this.data = convertToHalfFloat(array) | ||
break | ||
case GL_UNSIGNED_SHORT_5_6_5: | ||
case GL_UNSIGNED_SHORT_5_5_5_1: | ||
case GL_UNSIGNED_SHORT_4_4_4_4: | ||
case GL_UNSIGNED_INT_24_8_WEBGL: | ||
check.raise('unsupported format for automatic conversion') | ||
break | ||
default: | ||
check.raise('unsupported type conversion') | ||
} | ||
} | ||
if (this.data) { | ||
// apply transpose | ||
if (this.needsTranspose) { | ||
this.data = transposePixels( | ||
this.data, | ||
this.width, | ||
this.height, | ||
this.channels, | ||
this.strideX, | ||
this.strideY, | ||
this.strideC, | ||
this.offset) | ||
} | ||
// check data type | ||
switch (type) { | ||
case GL_UNSIGNED_BYTE: | ||
check(this.data instanceof Uint8Array || | ||
this.data instanceof Uint8ClampedArray, | ||
'incompatible pixel type') | ||
break | ||
case GL_UNSIGNED_SHORT_5_6_5: | ||
case GL_UNSIGNED_SHORT_5_5_5_1: | ||
case GL_UNSIGNED_SHORT_4_4_4_4: | ||
case GL_UNSIGNED_SHORT: | ||
case GL_HALF_FLOAT_OES: | ||
check(this.data instanceof Uint16Array, | ||
'incompatible pixel type') | ||
break | ||
case GL_UNSIGNED_INT: | ||
check(this.data instanceof Uint32Array, | ||
'incompatible pixel type') | ||
break | ||
case GL_FLOAT: | ||
check(this.data instanceof Float32Array, | ||
'incompatible pixel type') | ||
break | ||
default: | ||
check.raise('bad or missing pixel type') | ||
} | ||
} | ||
this.needsTranspose = false | ||
}, | ||
setDefaultFormat: function () { | ||
this.format = this.internalformat = GL_RGBA | ||
this.type = GL_UNSIGNED_BYTE | ||
this.channels = 4 | ||
this.compressed = false | ||
}, | ||
upload: function (params) { | ||
gl.pixelStorei(GL_UNPACK_FLIP_Y_WEBGL, this.flipY) | ||
gl.pixelStorei(GL_UNPACK_PREMULTIPLY_ALPHA_WEBGL, this.premultiplyAlpha) | ||
gl.pixelStorei(GL_UNPACK_COLORSPACE_CONVERSION_WEBGL, this.colorSpace) | ||
gl.pixelStorei(GL_UNPACK_ALIGNMENT, this.unpackAlignment) | ||
var target = this.target | ||
var miplevel = this.miplevel | ||
var image = this.image | ||
var canvas = this.canvas | ||
var video = this.video | ||
var data = this.data | ||
var internalformat = this.internalformat | ||
var format = this.format | ||
var type = this.type | ||
var width = this.width || Math.max(1, params.width >> miplevel) | ||
var height = this.height || Math.max(1, params.height >> miplevel) | ||
if (video && video.readyState > 2) { | ||
gl.texImage2D(target, miplevel, format, format, type, video) | ||
} else if (image && image.complete) { | ||
gl.texImage2D(target, miplevel, format, format, type, image) | ||
} else if (canvas) { | ||
gl.texImage2D(target, miplevel, format, format, type, canvas) | ||
} else if (this.compressed) { | ||
gl.compressedTexImage2D(target, miplevel, internalformat, width, height, 0, data) | ||
} else if (this.copy) { | ||
reglPoll() | ||
gl.copyTexImage2D(target, miplevel, format, this.x, this.y, width, height, 0) | ||
} else if (data) { | ||
gl.texImage2D(target, miplevel, format, width, height, 0, format, type, data) | ||
} else { | ||
gl.texImage2D(target, miplevel, format, width || 1, height || 1, 0, format, type, null) | ||
} | ||
} | ||
}) | ||
function TexParams (target) { | ||
this.target = target | ||
// Default image shape info | ||
this.width = 0 | ||
this.height = 0 | ||
this.format = 0 | ||
this.internalformat = 0 | ||
this.type = 0 | ||
// wrap mode | ||
this.wrapS = GL_CLAMP_TO_EDGE | ||
this.wrapT = GL_CLAMP_TO_EDGE | ||
// filtering | ||
this.minFilter = 0 | ||
this.magFilter = GL_NEAREST | ||
this.anisotropic = 1 | ||
// mipmaps | ||
this.genMipmaps = false | ||
this.mipmapHint = GL_DONT_CARE | ||
} | ||
Object.assign(TexParams.prototype, { | ||
clear: function () { | ||
TexParams.call(this, this.target) | ||
}, | ||
parse: function (options) { | ||
if (typeof options !== 'object' || !options) { | ||
return | ||
} | ||
if ('min' in options) { | ||
check.param(options.min, minFilters) | ||
this.minFilter = minFilters[options.min] | ||
var minFilter = options.min | ||
check.parameter(minFilter, minFilters) | ||
this.minFilter = minFilters[minFilter] | ||
} | ||
this.magFilter = GL_NEAREST | ||
if ('mag' in options) { | ||
check.param(options.mag, magFilters) | ||
this.magFilter = magFilters(options.mag) | ||
var magFilter = options.mag | ||
check.parameter(magFilter, magFilters) | ||
this.magFilter = magFilters[magFilter] | ||
} | ||
if (Array.isArray(data)) { | ||
var wrapS = this.wrapS | ||
var wrapT = this.wrapT | ||
if ('wrap' in options) { | ||
var wrap = options.wrap | ||
if (typeof wrap === 'string') { | ||
check.parameter(wrap, wrapModes) | ||
wrapS = wrapT = wrapModes[wrap] | ||
} else if (Array.isArray(wrap)) { | ||
check.parameter(wrap[0], wrapModes) | ||
check.parameter(wrap[1], wrapModes) | ||
wrapS = wrapModes[wrap[0]] | ||
wrapT = wrapModes[wrap[1]] | ||
} | ||
} else { | ||
if ('wrapS' in options) { | ||
var optWrapS = options.wrapS | ||
check.parameter(optWrapS, wrapModes) | ||
wrapS = wrapModes[optWrapS] | ||
} | ||
if ('wrapT' in options) { | ||
var optWrapT = options.wrapT | ||
check.parameter(optWrapT, wrapModes) | ||
wrapT = wrapModes[optWrapT] | ||
} | ||
} | ||
this.wrapS = wrapS | ||
this.wrapT = wrapT | ||
} else if (isTypedArray(data)) { | ||
if ('anisotropic' in options) { | ||
var anisotropic = options.anisotropic | ||
check(typeof anisotropic === 'number' && | ||
anisotropic >= 1 && anisotropic <= limits.maxAnisotropic, | ||
'aniso samples must be between 1 and ') | ||
this.anisotropic = options.anisotropic | ||
} | ||
} else if (isHTMLElement(data)) { | ||
if ('mipmap' in options) { | ||
var mipmap = options.mipmap | ||
switch (typeof mipmap) { | ||
case 'string': | ||
check.parameter(mipmap, mipmapHint, | ||
'invalid mipmap hint') | ||
this.mipmapHint = mipmapHint[mipmap] | ||
this.genMipmaps = true | ||
break | ||
case 'boolean': | ||
this.genMipmaps = !!mipmap | ||
break | ||
case 'object': | ||
break | ||
default: | ||
check.raise('invalid mipmap type') | ||
} | ||
} | ||
}, | ||
// Set tex image | ||
upload: function () { | ||
var target = this.target | ||
gl.texParameteri(target, GL_TEXTURE_MIN_FILTER, this.minFilter) | ||
gl.texParameteri(target, GL_TEXTURE_MAG_FILTER, this.magFilter) | ||
gl.texParameteri(target, GL_TEXTURE_WRAP_S, this.wrapS) | ||
gl.texParameteri(target, GL_TEXTURE_WRAP_T, this.wrapT) | ||
if (extensions.ext_texture_filter_anisotropic) { | ||
gl.texParameteri(target, GL_TEXTURE_MAX_ANISOTROPY_EXT, this.anisotropic) | ||
} | ||
if (this.genMipmaps) { | ||
gl.hint(GL_GENERATE_MIPMAP_HINT, this.mipmapHint) | ||
gl.generateMipmap(target) | ||
} | ||
} | ||
}) | ||
// Final pass to merge params and pixel data | ||
function checkTextureComplete (params, pixels) { | ||
var i, pixmap | ||
var type = 0 | ||
var format = 0 | ||
var internalformat = 0 | ||
var width = 0 | ||
var height = 0 | ||
var channels = 0 | ||
var compressed = false | ||
var needsPoll = false | ||
var needsListeners = false | ||
var mipMask2D = 0 | ||
var mipMaskCube = [0, 0, 0, 0, 0, 0] | ||
var cubeMask = 0 | ||
var hasMip = false | ||
for (i = 0; i < pixels.length; ++i) { | ||
pixmap = pixels[i] | ||
width = width || (pixmap.width << pixmap.miplevel) | ||
height = height || (pixmap.height << pixmap.miplevel) | ||
type = type || pixmap.type | ||
format = format || pixmap.format | ||
internalformat = internalformat || pixmap.internalformat | ||
channels = channels || pixmap.channels | ||
needsPoll = needsPoll || pixmap.needsPoll | ||
needsListeners = needsListeners || pixmap.needsListeners | ||
compressed = compressed || pixmap.compressed | ||
var miplevel = pixmap.miplevel | ||
var target = pixmap.target | ||
hasMip = hasMip || (miplevel > 0) | ||
if (target === GL_TEXTURE_2D) { | ||
mipMask2D |= (1 << miplevel) | ||
} else { | ||
var face = target - GL_TEXTURE_CUBE_MAP_POSITIVE_X | ||
mipMaskCube[face] |= (1 << miplevel) | ||
cubeMask |= (1 << face) | ||
} | ||
} | ||
params.needsPoll = needsPoll | ||
params.needsListeners = needsListeners | ||
params.width = width | ||
params.height = height | ||
params.format = format | ||
params.internalformat = internalformat | ||
params.type = type | ||
var mipMask = hasMip ? (width << 1) - 1 : 1 | ||
if (params.target === GL_TEXTURE_2D) { | ||
check(cubeMask === 0, | ||
'pixmap type must not contain cubemap faces') | ||
check(mipMask2D === mipMask, 'missing mip map data') | ||
} else { | ||
check(cubeMask === ((1 << 6) - 1), 'missing cubemap faces') | ||
for (i = 0; i < 6; ++i) { | ||
check(mipMaskCube[i] === mipMask, 'missing mip map data') | ||
} | ||
} | ||
var mipFilter = (MIPMAP_FILTERS.indexOf(params.minFilter) >= 0) | ||
params.genMipmaps = !hasMip && (params.genMipmaps || mipFilter) | ||
var useMipmaps = hasMip || params.genMipmaps | ||
if (!params.minFilter) { | ||
params.minFilter = useMipmaps | ||
? GL_LINEAR_MIPMAP_LINEAR | ||
: GL_NEAREST | ||
} else { | ||
check(useMipmaps === mipFilter, | ||
'min filter inconsistent with mipmap data') | ||
} | ||
if (useMipmaps) { | ||
check(width === height && isPow2(width), | ||
'must be a square power of 2 to support mipmaps') | ||
} | ||
if (params.genMipmaps) { | ||
check(!compressed, 'mipmap generation not supported for compressed textures') | ||
} | ||
params.wrapS = params.wrapS || GL_CLAMP_TO_EDGE | ||
params.wrapT = params.wrapT || GL_CLAMP_TO_EDGE | ||
if (params.wrapS !== GL_CLAMP_TO_EDGE || | ||
params.wrapT !== GL_CLAMP_TO_EDGE) { | ||
check(isPow2(width) && isPow2(height) && !cubeMask, | ||
'incompatible size for wrap mode, image must be a power of 2') | ||
} | ||
if ((type === GL_FLOAT && !extensions.oes_texture_float_linear) || | ||
(type === GL_HALF_FLOAT_OES && | ||
!extensions.oes_texture_half_float_linear)) { | ||
check(this.magFilter === GL_NEAREST && this.minFilter === GL_NEAREST, | ||
'unsupported filter mode for float texture') | ||
} | ||
for (i = 0; i < pixels.length; ++i) { | ||
pixmap = pixels[i] | ||
var level = pixmap.miplevel | ||
if (pixmap.width) { | ||
check(pixmap.width << level === width, 'inconsistent width') | ||
} | ||
if (pixmap.height) { | ||
check(pixmap.height << level === height, 'inconsistent width') | ||
} | ||
if (pixmap.channels) { | ||
check(pixmap.channels === channels, 'inconsistent channels') | ||
} else { | ||
pixmap.channels = channels | ||
} | ||
if (pixmap.format) { | ||
check(pixmap.format === format, 'inconsistent format') | ||
} else { | ||
pixmap.format = format | ||
} | ||
if (pixmap.internalformat) { | ||
check(pixmap.internalformat === internalformat, 'inconsistent internalformat') | ||
} else { | ||
pixmap.internalformat = internalformat | ||
} | ||
if (pixmap.type) { | ||
check(pixmap.type === type, 'inconsistent type') | ||
} else { | ||
pixmap.type = type | ||
} | ||
if (pixmap.copy) { | ||
check(pixmap.type === GL_UNSIGNED_BYTE && | ||
pixmap.internalformat === GL_RGBA, | ||
'incompatible format/type for copyTexImage2D') | ||
} | ||
} | ||
} | ||
var activeTexture = 0 | ||
var textureCount = 0 | ||
var textureSet = {} | ||
var pollSet = [] | ||
var numTexUnits = limits.textureUnits | ||
var textureUnits = Array(numTexUnits).map(function () { | ||
return null | ||
}) | ||
function REGLTexture (target, texture) { | ||
this.id = textureCount++ | ||
this.target = target | ||
this.texture = texture | ||
this.pollId = -1 | ||
this.unit = -1 | ||
this.bindCount = 0 | ||
// cancels all pending callbacks | ||
this.cancelPending = null | ||
// parsed user inputs | ||
this.params = new TexParams(target) | ||
this.pixels = [] | ||
} | ||
Object.assign(REGLTexture.prototype, { | ||
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 | ||
}, | ||
unbind: function () { | ||
this.bindCount -= 1 | ||
}, | ||
refresh: function () { | ||
gl.textureParameteri(GL_TEXTURE_MIN_FILTER, this.minFilter) | ||
gl.textureParameteri(GL_TEXTURE_MAG_FILTER, this.magFilter) | ||
gl.textureParameteri(GL_TEXTURE_WRAP_T, this.wrapT) | ||
gl.textureParameteri(GL_TEXTURE_WRAP_S, this.wrapS) | ||
// Lazy bind | ||
var target = this.target | ||
var unit = this.unit | ||
if (unit >= 0) { | ||
gl.activeTexture(GL_TEXTURE0 + unit) | ||
activeTexture = unit | ||
} else { | ||
gl.bindTexture(target, this.texture) | ||
} | ||
// 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 | ||
} | ||
} | ||
}, | ||
update: function (options) { | ||
var i | ||
this.clearListeners() | ||
// Clear parameters and pixel data | ||
var params = this.params | ||
var pixels = this.pixels | ||
params.clear() | ||
pixels.length = 0 | ||
// parse parameters | ||
params.parse(options) | ||
// 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, {}) | ||
} | ||
} | ||
} | ||
// do a second pass to reconcile defaults | ||
checkTextureComplete(params, pixels) | ||
if (params.needsListeners) { | ||
this.hookListeners() | ||
} | ||
if (params.needsPoll) { | ||
this.pollId = pollSet.length | ||
pollSet.push(this) | ||
} | ||
this.refresh() | ||
}, | ||
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) | ||
check(dds.format in colorFormats, 'unsupported dds texture format') | ||
if (dds.cube) { | ||
check(texture.target === GL_TEXTURE_CUBE_MAP) | ||
// TODO handle cube map | ||
} else { | ||
check(texture.target === GL_TEXTURE_2D) | ||
} | ||
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) | ||
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 | ||
pixels.push(info) | ||
}) | ||
} | ||
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', refresh) | ||
} else if (pixelData.video && pixelData.readyState < 1) { | ||
pixelData.video.addEventListener('progress', refresh) | ||
} else if (pixelData.xhr) { | ||
pixelData.xhr.addEventListener('readystatechange', refresh) | ||
} | ||
}) | ||
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) | ||
} | ||
}) | ||
} | ||
}, | ||
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 | ||
} | ||
}, | ||
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 | ||
} | ||
this.clearListeners() | ||
gl.deleteTexture(this.texture) | ||
this.texture = null | ||
this.params = null | ||
this.pixels = null | ||
delete textureSet[this.id] | ||
@@ -156,21 +1368,28 @@ } | ||
function createTexture (options) { | ||
var texture = new REGLTexture() | ||
texture.texture = gl.createTexture() | ||
texture.update(options) | ||
function createTexture (options, target) { | ||
var texture = new REGLTexture(target, gl.createTexture()) | ||
textureSet[texture.id] = texture | ||
function updateTexture (options) { | ||
function reglTexture (a0, a1, a2, a3, a4, a5) { | ||
var options = a0 || {} | ||
if (target === GL_TEXTURE_CUBE_MAP && arguments.length === 6) { | ||
options = [a0, a1, a2, a3, a4, a5] | ||
} | ||
texture.update(options) | ||
return updateTexture | ||
} | ||
updateTexture._texture = texture | ||
updateTexture.destroy = function () { | ||
texture.destroy() | ||
} | ||
reglTexture(options) | ||
return updateTexture | ||
Object.assign(reglTexture, { | ||
_reglType: 'texture', | ||
_texture: texture, | ||
destroy: function () { | ||
texture.destroy() | ||
} | ||
}) | ||
return reglTexture | ||
} | ||
// Called after context restore | ||
function refreshTextures () { | ||
@@ -180,5 +1399,18 @@ Object.keys(textureSet).forEach(function (texId) { | ||
}) | ||
for (var i = 0; i < numTexUnits; ++i) { | ||
textureUnits[i] = null | ||
} | ||
activeTexture = 0 | ||
gl.activeTexture(GL_TEXTURE0) | ||
} | ||
// Called when regl is destroyed | ||
function destroyTextures () { | ||
for (var i = 0; i < numTexUnits; ++i) { | ||
gl.activeTexture(GL_TEXTURE0 + i) | ||
gl.bindTexture(GL_TEXTURE_2D, null) | ||
textureUnits[i] = null | ||
} | ||
gl.activeTexture(GL_TEXTURE0) | ||
activeTexture = 0 | ||
Object.keys(textureSet).forEach(function (texId) { | ||
@@ -189,2 +1421,9 @@ textureSet[texId].destroy() | ||
// Called once per raf, updates video textures | ||
function pollTextures () { | ||
for (var i = 0; i < pollSet.length; ++i) { | ||
pollSet[i].refresh() | ||
} | ||
} | ||
return { | ||
@@ -194,2 +1433,3 @@ create: createTexture, | ||
clear: destroyTextures, | ||
poll: pollTextures, | ||
getTexture: function (wrapper) { | ||
@@ -196,0 +1436,0 @@ return null |
{ | ||
"name": "regl", | ||
"version": "0.1.0", | ||
"version": "0.2.0", | ||
"description": "WebGL", | ||
@@ -11,4 +11,8 @@ "main": "regl.js", | ||
"devDependencies": { | ||
"angle-normals": "^1.0.0", | ||
"baboon-image": "^2.0.0", | ||
"browserify": "^13.0.0", | ||
"budo": "^8.1.0", | ||
"bunny": "^1.0.1", | ||
"canvas-fit": "^1.5.0", | ||
"coverify": "^1.4.1", | ||
@@ -18,3 +22,7 @@ "derequire": "^2.0.3", | ||
"gl": "^3.0.3", | ||
"gl-mat4": "^1.1.4", | ||
"hsv2rgb": "^1.1.0", | ||
"indexhtmlify": "^1.2.1", | ||
"mouse-change": "^1.2.1", | ||
"ncp": "^2.0.0", | ||
"runscript": "^1.1.0", | ||
@@ -21,0 +29,0 @@ "smokestack": "^3.4.1", |
# regl | ||
[![Circle CI](https://circleci.com/gh/mikolalysenko/regl.svg?style=svg)](https://circleci.com/gh/mikolalysenko/regl) [![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg)](http://standardjs.com/) | ||
[![Join the chat at https://gitter.im/ark-lang/ark](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/mikolalysenko/regl) [![Circle CI](https://circleci.com/gh/mikolalysenko/regl.svg?style=svg)](https://circleci.com/gh/mikolalysenko/regl) [![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg)](http://standardjs.com/) | ||
@@ -10,7 +10,8 @@ This repo is an attempt at building new functional abstractions for working with WebGL. It is still **experimental**, so expect things to change a lot in the near future! If you want to know more about why I am writing this thing and why it looks the way it does, take a look at the [rationale](RATIONALE.md). | ||
* **Just one function** | ||
* **Less state** Draw commands in regl are self contained, so you don't have to worry about some other weird subroutine breaking your rendering code | ||
* **No `bind()`** In regl, shaders, buffers, textures and fbos are specified declaratively, so there is no need to ever `bind()` them or unbind them. | ||
* **Fewer silent failure** If you pass incorrect parameters to some WebGL method, the default behavior is to set an error code and continue on. Because `regl` commands have more structure, we can do more validation up front without the run time performance cost. | ||
* **Fewer silent failures** If you pass incorrect parameters to some WebGL method, the default behavior is to set an error code and continue on. Because `regl` commands have more structure, we can do more validation up front without the run time performance cost. | ||
* **Sane defaults** Many WebGL APIs have redundant or outright broken parameters (for example `border` in `gl.texImage2D` or `transpose` in `gl.uniformMatrix4fv`). `regl` wraps these APIs in such a way that you will never have to see this mess. | ||
* **Low overhead** regl uses caching and code generation to minimize overhead from | ||
* **Still just WebGL** regl exposes the full power of the WebGL API, no features are removed or hidden. | ||
@@ -134,2 +135,3 @@ ## Simple example | ||
## [API](API.md) | ||
* [Initialization](API.md#initialization) | ||
@@ -140,2 +142,3 @@ * [As a fullscreen canvas](API.md#as-a-fullscreen-canvas) | ||
* [From a WebGL context](API.md#from-a-webgl-context) | ||
+ [Initialization options](API.md#initialization-options) | ||
* [Commands](API.md#commands) | ||
@@ -152,3 +155,3 @@ + [Dynamic properties](API.md#dynamic-properties) | ||
- [Drawing](API.md#drawing) | ||
- [Framebuffer](API.md#framebuffer) | ||
- [Render target](API.md#render-target) | ||
- [Depth buffer](API.md#depth-buffer) | ||
@@ -171,5 +174,8 @@ - [Blending](API.md#blending) | ||
+ [Types](API.md#types) | ||
- [`regl.buffer(options)`](API.md#-reglbuffer-options--) | ||
- [`regl.elements(options)`](API.md#-reglelements-options--) | ||
* [Other features](API.md#other-properties) | ||
- [Buffers](API.md#buffers) | ||
- [Elements](API.md#elements) | ||
- [Textures](API.md#textures) | ||
- [Render buffers](API.md#render-buffers) | ||
- [Frame buffers](API.md#frame-buffers) | ||
* [Other features](API.md#other-features) | ||
+ [Clear the draw buffer](API.md#clear-the-draw-buffer) | ||
@@ -179,3 +185,3 @@ + [Reading pixels](API.md#reading-pixels) | ||
+ [Frame stats](API.md#frame-stats) | ||
+ [WebGL capabilities](API.md#webgl-capabilities) | ||
+ [Limits](API.md#limits) | ||
+ [Clean up](API.md#clean-up) | ||
@@ -188,5 +194,13 @@ + [Context loss](API.md#context-loss) | ||
## License | ||
(c) 2016 MIT License | ||
## Credits | ||
All code (c) 2016 MIT License | ||
Supported by the [Freeman Lab](https://www.janelia.org/lab/freeman-lab) and the Howard Hughes Medical Institute | ||
Development supported by the [Freeman Lab](https://www.janelia.org/lab/freeman-lab) and the Howard Hughes Medical Institute (@freeman-lab on GitHub) | ||
Test video (doggie-chromakey.ogv) by [L0ckergn0me](https://archive.org/details/L0ckergn0me-PixieGreenScreen446), used under creative commons license | ||
Cube maps (posx.jpeg, negx.jpeg, posy.jpeg, negy.jpeg, posz.jpeg, negz.jpeg) by [Humus](http://www.humus.name/index.php?page=Textures), used under creative commons 3 license | ||
Environment map of Oregon (ogd-oregon-360.jpg) due to Max Ogden (@ogd on GitHub) | ||
DDS test images (alpine_cliff_a, alpine_cliff_a_norm, alpine_cliff_a_spec) taken from the CC0 license [0-AD texture pack by Wildfire games](http://opengameart.org/content/0-ad-textures) |
53
regl.js
var check = require('./lib/check') | ||
var getContext = require('./lib/context') | ||
var wrapExtensions = require('./lib/extension') | ||
var wrapLimits = require('./lib/limits') | ||
var wrapBuffers = require('./lib/buffer') | ||
@@ -24,2 +25,4 @@ var wrapElements = require('./lib/elements') | ||
var GL_ARRAY_BUFFER = 34962 | ||
var GL_TEXTURE_2D = 0x0DE1 | ||
var GL_TEXTURE_CUBE_MAP = 0x8513 | ||
@@ -34,7 +37,6 @@ var CONTEXT_LOST_EVENT = 'webglcontextlost' | ||
var extensionState = wrapExtensions(gl, options.requiredExtensions || []) | ||
var extensionState = wrapExtensions(gl) | ||
var limits = wrapLimits(gl, extensionState) | ||
var bufferState = wrapBuffers(gl) | ||
var elementState = wrapElements(gl, extensionState, bufferState) | ||
var textureState = wrapTextures(gl, extensionState) | ||
var fboState = wrapFBOs(gl, extensionState, textureState) | ||
var uniformState = wrapUniforms() | ||
@@ -52,2 +54,9 @@ var attributeState = wrapAttributes(gl, extensionState, bufferState) | ||
var glState = wrapContext(gl, shaderState) | ||
var textureState = wrapTextures( | ||
gl, | ||
extensionState, | ||
limits, | ||
poll, | ||
glState.viewport) | ||
var fboState = wrapFBOs(gl, extensionState, textureState) | ||
var frameState = { | ||
@@ -60,3 +69,4 @@ count: 0, | ||
width: gl.drawingBufferWidth, | ||
height: gl.drawingBufferHeight | ||
height: gl.drawingBufferHeight, | ||
pixelRatio: options.pixelRatio | ||
} | ||
@@ -98,2 +108,5 @@ var readPixels = wrapRead(gl, glState) | ||
frameState.t = now | ||
textureState.poll() | ||
for (var i = 0; i < rafCallbacks.length; ++i) { | ||
@@ -165,8 +178,2 @@ var cb = rafCallbacks[i] | ||
function create (cache) { | ||
return function (options) { | ||
return cache.create(options) | ||
} | ||
} | ||
// Compiles a set of procedures for an object | ||
@@ -260,2 +267,6 @@ function compileProcedure (options) { | ||
function poll () { | ||
glState.poll() | ||
} | ||
// Clears the currently bound frame buffer | ||
@@ -320,8 +331,21 @@ function clear (options) { | ||
// Object constructors | ||
elements: create(elementState), | ||
elements: function (options) { | ||
return elementState.create(options) | ||
}, | ||
buffer: function (options) { | ||
return bufferState.create(options, GL_ARRAY_BUFFER) | ||
}, | ||
texture: create(textureState), | ||
fbo: create(fboState), | ||
texture: function (options) { | ||
return textureState.create(options, GL_TEXTURE_2D) | ||
}, | ||
cube: function (options) { | ||
if (arguments.length === 6) { | ||
return textureState.create( | ||
Array.prototype.slice.call(arguments), | ||
GL_TEXTURE_CUBE_MAP) | ||
} else { | ||
return textureState.create(options, GL_TEXTURE_CUBE_MAP) | ||
} | ||
}, | ||
// fbo: create(fboState), | ||
@@ -332,2 +356,5 @@ // Frame rendering | ||
// System limits | ||
limits: limits, | ||
// Read pixels | ||
@@ -334,0 +361,0 @@ read: readPixels, |
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
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
9749
200
4
2
460835
22
41