From e0798bad4c0a031990e6ea0e99f71477e044322d Mon Sep 17 00:00:00 2001 From: Tarek Sherif Date: Mon, 9 Sep 2019 11:52:48 -0400 Subject: [PATCH] Module builds, v0.14.1 --- BANNER | 24 - CHANGELOG.md | 8 + LICENSE | 2 +- build/module/app.js | 1224 ++++++++++++++++++++++++++++ build/module/constants.js | 814 ++++++++++++++++++ build/module/cubemap.js | 279 +++++++ build/module/draw-call.js | 269 ++++++ build/module/framebuffer.js | 330 ++++++++ build/module/picogl.js | 91 +++ build/module/program.js | 335 ++++++++ build/module/query.js | 125 +++ build/module/renderbuffer.js | 91 +++ build/module/shader.js | 102 +++ build/module/texture.js | 349 ++++++++ build/module/timer.js | 161 ++++ build/module/transform-feedback.js | 117 +++ build/module/uniform-buffer.js | 286 +++++++ build/module/uniforms.js | 226 +++++ build/module/vertex-array.js | 229 ++++++ build/module/vertex-buffer.js | 187 +++++ build/picogl.min.js | 10 +- examples/125kcubes.html | 2 +- examples/3Dtexture.html | 2 +- examples/64kcubes.html | 2 +- examples/bloom.html | 2 +- examples/cloth.html | 2 +- examples/compressed-cubemap.html | 2 +- examples/compressed-textures.html | 2 +- examples/context-loss.html | 2 +- examples/cube.html | 2 +- examples/cubemap.html | 2 +- examples/deferred.html | 2 +- examples/dof.html | 2 +- examples/instanced.html | 2 +- examples/interleaved-triangle.html | 2 +- examples/mesh-compression.html | 2 +- examples/motion-blur.html | 2 +- examples/multi-draw.html | 2 +- examples/occlusion.html | 2 +- examples/oit.html | 2 +- examples/omni-shadows.html | 2 +- examples/outline.html | 2 +- examples/particles.html | 2 +- examples/picking.html | 2 +- examples/render-to-3Dtexture.html | 2 +- examples/render-to-cubemap.html | 2 +- examples/rtt.html | 2 +- examples/rtt_msaa.html | 2 +- examples/shadow.html | 2 +- examples/ssao.html | 2 +- examples/texturearray.html | 2 +- examples/transformfeedback.html | 2 +- examples/triangle.html | 2 +- examples/ubo.html | 2 +- examples/wandering-triangles.html | 2 +- package.json | 24 +- webpack.config.js | 32 +- 57 files changed, 5305 insertions(+), 78 deletions(-) delete mode 100644 BANNER create mode 100644 build/module/app.js create mode 100644 build/module/constants.js create mode 100644 build/module/cubemap.js create mode 100644 build/module/draw-call.js create mode 100644 build/module/framebuffer.js create mode 100644 build/module/picogl.js create mode 100644 build/module/program.js create mode 100644 build/module/query.js create mode 100644 build/module/renderbuffer.js create mode 100644 build/module/shader.js create mode 100644 build/module/texture.js create mode 100644 build/module/timer.js create mode 100644 build/module/transform-feedback.js create mode 100644 build/module/uniform-buffer.js create mode 100644 build/module/uniforms.js create mode 100644 build/module/vertex-array.js create mode 100644 build/module/vertex-buffer.js diff --git a/BANNER b/BANNER deleted file mode 100644 index 5362b278..00000000 --- a/BANNER +++ /dev/null @@ -1,24 +0,0 @@ -/* -PicoGL.js v%%VERSION%% - -The MIT License (MIT) - -Copyright (c) 2017 Tarek Sherif - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a9e266b..368392aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## v0.14.1 + +- fix uniform buffer typo bug +- dont throw when setting non-existent uniform + + ## v0.14.0 - `DrawCall` @@ -11,6 +17,7 @@ - Renderer information now stored in `PicoGL.WEBGL_INFO.VENDOR` and `PicoGL.WEBGL_INFO.RENDERER`. - Use recommended polling pattern for shader compilation and program linking. + ## v0.13.0 - Support for parallel shader compilation (KHR_parallel_shader_compile) @@ -18,6 +25,7 @@ - Auto-enable extensions - Remove methods `App.floatRenderTargets`, `App.linearFloatTextures`, `App.s3tcTextures`, `App.etcTextures`, `App.astcTextures`, `App.pvrtcTextures`, as they are no longer needed. + ## v0.12.0 - Support for compressed cubemaps diff --git a/LICENSE b/LICENSE index a37df523..f2fcaf22 100644 --- a/LICENSE +++ b/LICENSE @@ -17,4 +17,4 @@ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/build/module/app.js b/build/module/app.js new file mode 100644 index 00000000..ec459e91 --- /dev/null +++ b/build/module/app.js @@ -0,0 +1,1224 @@ +/////////////////////////////////////////////////////////////////////////////////// +// The MIT License (MIT) +// +// Copyright (c) 2017 Tarek Sherif +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +/////////////////////////////////////////////////////////////////////////////////// + +import { GL, WEBGL_INFO, DUMMY_OBJECT } from "./constants.js"; +import { Cubemap } from "./cubemap.js"; +import { DrawCall } from "./draw-call.js"; +import { Framebuffer } from "./framebuffer.js"; +import { Renderbuffer } from "./renderbuffer.js"; +import { Program } from "./program.js"; +import { Shader } from "./shader.js"; +import { Texture } from "./texture.js"; +import { Timer } from "./timer.js"; +import { TransformFeedback } from "./transform-feedback.js"; +import { UniformBuffer } from "./uniform-buffer.js"; +import { VertexArray } from "./vertex-array.js"; +import { VertexBuffer } from "./vertex-buffer.js"; +import { Query } from "./query.js"; + +/** + Primary entry point to PicoGL. An app will store all parts of the WebGL + state. + + @class + @prop {DOMElement} canvas The canvas on which this app drawing. + @prop {WebGLRenderingContext} gl The WebGL context. + @prop {number} width The width of the drawing surface. + @prop {number} height The height of the drawing surface. + @prop {Object} state Tracked GL state. + @prop {GLEnum} clearBits Current clear mask to use with clear(). +*/ +export class App { + + constructor(gl, canvas) { + this.canvas = canvas; + this.gl = gl; + this.width = this.gl.drawingBufferWidth; + this.height = this.gl.drawingBufferHeight; + this.viewportX = 0; + this.viewportY = 0; + this.viewportWidth = 0; + this.viewportHeight = 0; + this.currentDrawCalls = null; + this.emptyFragmentShader = null; + + this.state = { + program: null, + vertexArray: null, + transformFeedback: null, + activeTexture: -1, + textures: new Array(WEBGL_INFO.MAX_TEXTURE_UNITS), + uniformBuffers: new Array(WEBGL_INFO.MAX_UNIFORM_BUFFERS), + freeUniformBufferBases: [], + drawFramebuffer: null, + readFramebuffer: null, + extensions: {} + }; + + this.clearBits = this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT| this.gl.STENCIL_BUFFER_BIT; + + this.cpuTime = 0; + this.gpuTime = 0; + + this.viewport(0, 0, this.width, this.height); + + this.contextLostExt = null; + this.contextRestoredHandler = null; + + this.initExtensions(); + + this.canvas.addEventListener("webglcontextlost", (e) => { + e.preventDefault(); + }); + + this.canvas.addEventListener("webglcontextrestored", () => { + this.initExtensions(); + + if (this.contextRestoredHandler) { + this.contextRestoredHandler(); + } + }); + } + + /** + Simulate context loss. + + @method + @return {App} The App object. + */ + loseContext() { + if (this.contextLostExt) { + this.contextLostExt.loseContext(); + } + + return this; + } + + /** + Simulate context restoration. + + @method + @return {App} The App object. + */ + restoreContext() { + if (this.contextLostExt) { + this.contextLostExt.restoreContext(); + } + + return this; + } + + /** + Set function to handle context restoration after loss. + + @method + @param {function} fn Context restored handler. + @return {App} The App object. + */ + onContextRestored(fn) { + this.contextRestoredHandler = fn; + + return this; + } + + /** + Set the color mask to selectively enable or disable particular + color channels while rendering. + + @method + @param {boolean} r Red channel. + @param {boolean} g Green channel. + @param {boolean} b Blue channel. + @param {boolean} a Alpha channel. + @return {App} The App object. + */ + colorMask(r, g, b, a) { + this.gl.colorMask(r, g, b, a); + + return this; + } + + /** + Set the clear color. + + @method + @param {number} r Red channel. + @param {number} g Green channel. + @param {number} b Blue channel. + @param {number} a Alpha channel. + @return {App} The App object. + */ + clearColor(r, g, b, a) { + this.gl.clearColor(r, g, b, a); + + return this; + } + + /** + Set the clear mask bits to use when calling clear(). + E.g. app.clearMask(PicoGL.COLOR_BUFFER_BIT). + + @method + @param {GLEnum} mask Bit mask of buffers to clear. + @return {App} The App object. + */ + clearMask(mask) { + this.clearBits = mask; + + return this; + } + + /** + Clear the canvas + + @method + @return {App} The App object. + */ + clear() { + this.gl.clear(this.clearBits); + + return this; + } + + /** + Bind a draw framebuffer to the WebGL context. + + @method + @param {Framebuffer} framebuffer The Framebuffer object to bind. + @see Framebuffer + @return {App} The App object. + */ + drawFramebuffer(framebuffer) { + framebuffer.bindForDraw(); + + return this; + } + + /** + Bind a read framebuffer to the WebGL context. + + @method + @param {Framebuffer} framebuffer The Framebuffer object to bind. + @see Framebuffer + @return {App} The App object. + */ + readFramebuffer(framebuffer) { + framebuffer.bindForRead(); + + return this; + } + + /** + Switch back to the default framebuffer for drawing (i.e. draw to the screen). + Note that this method resets the viewport to match the default framebuffer. + + @method + @return {App} The App object. + */ + defaultDrawFramebuffer() { + if (this.state.drawFramebuffer !== null) { + this.gl.bindFramebuffer(this.gl.DRAW_FRAMEBUFFER, null); + this.state.drawFramebuffer = null; + } + + return this; + } + + /** + Switch back to the default framebuffer for reading (i.e. read from the screen). + + @method + @return {App} The App object. + */ + defaultReadFramebuffer() { + if (this.state.readFramebuffer !== null) { + this.gl.bindFramebuffer(this.gl.READ_FRAMEBUFFER, null); + this.state.readFramebuffer = null; + } + + return this; + } + + /** + Copy data from framebuffer attached to READ_FRAMEBUFFER to framebuffer attached to DRAW_FRAMEBUFFER. + + @method + @param {GLEnum} mask Write mask (e.g. PicoGL.COLOR_BUFFER_BIT). + @param {Object} [options] Blit options. + @param {number} [options.srcStartX=0] Source start x coordinate. + @param {number} [options.srcStartY=0] Source start y coordinate. + @param {number} [options.srcEndX=Width of the read framebuffer] Source end x coordinate. + @param {number} [options.srcEndY=Height of the read framebuffer] Source end y coordinate. + @param {number} [options.dstStartX=0] Destination start x coordinate. + @param {number} [options.dstStartY=0] Destination start y coordinate. + @param {number} [options.dstEndX=Width of the draw framebuffer] Destination end x coordinate. + @param {number} [options.dstEndY=Height of the draw framebuffer] Destination end y coordinate. + @param {number} [options.filter=NEAREST] Sampling filter. + @return {App} The App object. + */ + blitFramebuffer(mask, options = DUMMY_OBJECT) { + let readFramebuffer = this.state.readFramebuffer; + let drawFramebuffer = this.state.drawFramebuffer; + let defaultReadWidth = readFramebuffer ? readFramebuffer.width : this.width; + let defaultReadHeight = readFramebuffer ? readFramebuffer.height : this.height; + let defaultDrawWidth = drawFramebuffer ? drawFramebuffer.width : this.width; + let defaultDrawHeight = drawFramebuffer ? drawFramebuffer.height : this.height; + + let { + srcStartX = 0, + srcStartY = 0, + srcEndX = defaultReadWidth, + srcEndY = defaultReadHeight, + dstStartX = 0, + dstStartY = 0, + dstEndX = defaultDrawWidth, + dstEndY = defaultDrawHeight, + filter = GL.NEAREST + } = options; + + this.gl.blitFramebuffer(srcStartX, srcStartY, srcEndX, srcEndY, dstStartX, dstStartY, dstEndX, dstEndY, mask, filter); + + return this; + } + + /** + Set the depth range. + + @method + @param {number} near Minimum depth value. + @param {number} far Maximum depth value. + @return {App} The App object. + */ + depthRange(near, far) { + this.gl.depthRange(near, far); + + return this; + } + + /** + Enable depth testing. + + @method + @return {App} The App object. + */ + depthTest() { + this.gl.enable(this.gl.DEPTH_TEST); + + return this; + } + + /** + Disable depth testing. + + @method + @return {App} The App object. + */ + noDepthTest() { + this.gl.disable(this.gl.DEPTH_TEST); + + return this; + } + + /** + Enable or disable writing to the depth buffer. + + @method + @param {Boolean} mask The depth mask. + @return {App} The App object. + */ + depthMask(mask) { + this.gl.depthMask(mask); + + return this; + } + + /** + Set the depth test function. E.g. app.depthFunc(PicoGL.LEQUAL). + + @method + @param {GLEnum} func The depth testing function to use. + @return {App} The App object. + */ + depthFunc(func) { + this.gl.depthFunc(func); + + return this; + } + + /** + Enable blending. + + @method + @return {App} The App object. + */ + blend() { + this.gl.enable(this.gl.BLEND); + + return this; + } + + /** + Disable blending + + @method + @return {App} The App object. + */ + noBlend() { + this.gl.disable(this.gl.BLEND); + + return this; + } + + /** + Set the blend function. E.g. app.blendFunc(PicoGL.ONE, PicoGL.ONE_MINUS_SRC_ALPHA). + + @method + @param {GLEnum} src The source blending weight. + @param {GLEnum} dest The destination blending weight. + @return {App} The App object. + */ + blendFunc(src, dest) { + this.gl.blendFunc(src, dest); + + return this; + } + + /** + Set the blend function, with separate weighting for color and alpha channels. + E.g. app.blendFuncSeparate(PicoGL.ONE, PicoGL.ONE_MINUS_SRC_ALPHA, PicoGL.ONE, PicoGL.ONE). + + @method + @param {GLEnum} csrc The source blending weight for the RGB channels. + @param {GLEnum} cdest The destination blending weight for the RGB channels. + @param {GLEnum} asrc The source blending weight for the alpha channel. + @param {GLEnum} adest The destination blending weight for the alpha channel. + @return {App} The App object. + */ + blendFuncSeparate(csrc, cdest, asrc, adest) { + this.gl.blendFuncSeparate(csrc, cdest, asrc, adest); + + return this; + } + + /** + Enable stencil testing. + NOTE: Only works if { stencil: true } passed as a + context attribute when creating the App! + + @method + @return {App} The App object. + */ + stencilTest() { + this.gl.enable(this.gl.STENCIL_TEST); + + return this; + } + + /** + Disable stencil testing. + + @method + @return {App} The App object. + */ + noStencilTest() { + this.gl.disable(this.gl.STENCIL_TEST); + + return this; + } + + + /** + Enable scissor testing. + + @method + @return {App} The App object. + */ + scissorTest() { + this.gl.enable(this.gl.SCISSOR_TEST); + + return this; + } + + /** + Disable scissor testing. + + @method + @return {App} The App object. + */ + noScissorTest() { + this.gl.disable(this.gl.SCISSOR_TEST); + + return this; + } + + /** + Define the scissor box. + + @method + @return {App} The App object. + */ + scissor(x, y, width, height) { + this.gl.scissor(x, y, width, height); + + return this; + } + + /** + Set the bitmask to use for tested stencil values. + E.g. app.stencilMask(0xFF). + NOTE: Only works if { stencil: true } passed as a + context attribute when creating the App! + + @method + @param {number} mask The mask value. + @return {App} The App object. + + */ + stencilMask(mask) { + this.gl.stencilMask(mask); + + return this; + } + + /** + Set the bitmask to use for tested stencil values for a particular face orientation. + E.g. app.stencilMaskSeparate(PicoGL.FRONT, 0xFF). + NOTE: Only works if { stencil: true } passed as a + context attribute when creating the App! + + @method + @param {GLEnum} face The face orientation to apply the mask to. + @param {number} mask The mask value. + @return {App} The App object. + */ + stencilMaskSeparate(face, mask) { + this.gl.stencilMaskSeparate(face, mask); + + return this; + } + + /** + Set the stencil function and reference value. + E.g. app.stencilFunc(PicoGL.EQUAL, 1, 0xFF). + NOTE: Only works if { stencil: true } passed as a + context attribute when creating the App! + + @method + @param {GLEnum} func The testing function. + @param {number} ref The reference value. + @param {GLEnum} mask The bitmask to use against tested values before applying + the stencil function. + @return {App} The App object. + */ + stencilFunc(func, ref, mask) { + this.gl.stencilFunc(func, ref, mask); + + return this; + } + + /** + Set the stencil function and reference value for a particular face orientation. + E.g. app.stencilFuncSeparate(PicoGL.FRONT, PicoGL.EQUAL, 1, 0xFF). + NOTE: Only works if { stencil: true } passed as a + context attribute when creating the App! + + @method + @param {GLEnum} face The face orientation to apply the function to. + @param {GLEnum} func The testing function. + @param {number} ref The reference value. + @param {GLEnum} mask The bitmask to use against tested values before applying + the stencil function. + @return {App} The App object. + */ + stencilFuncSeparate(face, func, ref, mask) { + this.gl.stencilFuncSeparate(face, func, ref, mask); + + return this; + } + + /** + Set the operations for updating stencil buffer values. + E.g. app.stencilOp(PicoGL.KEEP, PicoGL.KEEP, PicoGL.REPLACE). + NOTE: Only works if { stencil: true } passed as a + context attribute when creating the App! + + @method + @param {GLEnum} stencilFail Operation to apply if the stencil test fails. + @param {GLEnum} depthFail Operation to apply if the depth test fails. + @param {GLEnum} pass Operation to apply if the both the depth and stencil tests pass. + @return {App} The App object. + */ + stencilOp(stencilFail, depthFail, pass) { + this.gl.stencilOp(stencilFail, depthFail, pass); + + return this; + } + + /** + Set the operations for updating stencil buffer values for a particular face orientation. + E.g. app.stencilOpSeparate(PicoGL.FRONT, PicoGL.KEEP, PicoGL.KEEP, PicoGL.REPLACE). + NOTE: Only works if { stencil: true } passed as a + context attribute when creating the App! + + @method + @param {GLEnum} face The face orientation to apply the operations to. + @param {GLEnum} stencilFail Operation to apply if the stencil test fails. + @param {GLEnum} depthFail Operation to apply if the depth test fails. + @param {GLEnum} pass Operation to apply if the both the depth and stencil tests pass. + @return {App} The App object. + */ + stencilOpSeparate(face, stencilFail, depthFail, pass) { + this.gl.stencilOpSeparate(face, stencilFail, depthFail, pass); + + return this; + } + + /** + Enable rasterization step. + + @method + @return {App} The App object. + */ + rasterize() { + this.gl.disable(this.gl.RASTERIZER_DISCARD); + + return this; + } + + /** + Disable rasterization step. + + @method + @return {App} The App object. + */ + noRasterize() { + this.gl.enable(this.gl.RASTERIZER_DISCARD); + + return this; + } + + /** + Enable backface culling. + + @method + @return {App} The App object. + */ + cullBackfaces() { + this.gl.enable(this.gl.CULL_FACE); + + return this; + } + + /** + Disable backface culling. + + @method + @return {App} The App object. + */ + drawBackfaces() { + this.gl.disable(this.gl.CULL_FACE); + + return this; + } + + /** + Read a pixel's color value from the currently-bound framebuffer. + + @method + @param {number} x The x coordinate of the pixel. + @param {number} y The y coordinate of the pixel. + @param {ArrayBufferView} outColor Typed array to store the pixel's color. + @param {object} [options] Options. + @param {GLEnum} [options.type=UNSIGNED_BYTE] Type of data stored in the read framebuffer. + @param {GLEnum} [options.format=RGBA] Read framebuffer data format. + @return {App} The App object. + */ + readPixel(x, y, outColor, options = DUMMY_OBJECT) { + let { + format = GL.RGBA, + type = GL.UNSIGNED_BYTE + } = options; + + this.gl.readPixels(x, y, 1, 1, format, type, outColor); + + return this; + } + + /** + Set the viewport. + + @method + @param {number} x Left bound of the viewport rectangle. + @param {number} y Lower bound of the viewport rectangle. + @param {number} width Width of the viewport rectangle. + @param {number} height Height of the viewport rectangle. + @return {App} The App object. + */ + viewport(x, y, width, height) { + + if (this.viewportWidth !== width || this.viewportHeight !== height || + this.viewportX !== x || this.viewportY !== y) { + this.viewportX = x; + this.viewportY = y; + this.viewportWidth = width; + this.viewportHeight = height; + this.gl.viewport(x, y, this.viewportWidth, this.viewportHeight); + } + + return this; + } + + /** + Set the viewport to the full canvas. + + @method + @return {App} The App object. + */ + defaultViewport() { + this.viewport(0, 0, this.width, this.height); + + return this; + } + + /** + Resize the drawing surface. + + @method + @param {number} width The new canvas width. + @param {number} height The new canvas height. + @return {App} The App object. + */ + resize(width, height) { + this.canvas.width = width; + this.canvas.height = height; + + this.width = this.gl.drawingBufferWidth; + this.height = this.gl.drawingBufferHeight; + this.viewport(0, 0, this.width, this.height); + + return this; + } + + /** + Create a program synchronously. It is highly recommended to use createPrograms instead as + that method will compile shaders in parallel where possible. + @method + @param {Shader|string} vertexShader Vertex shader object or source code. + @param {Shader|string} fragmentShader Fragment shader object or source code. + @param {Array} [xformFeedbackVars] Transform feedback varyings. + @return {Program} New Program object. + */ + createProgram(vsSource, fsSource, xformFeedbackVars) { + return new Program(this.gl, this.state, vsSource, fsSource, xformFeedbackVars) + .link() + .checkLinkage(); + } + + /** + Create several programs. Preferred method for program creation as it will compile shaders + in parallel where possible. + + @method + @param {...Array} sources Variable number of 2 or 3 element arrays, each containing: + + @return {Promise} Promise that will resolve to an array of Programs when compilation and + linking are complete for all programs. + */ + createPrograms(...sources) { + return new Promise((resolve, reject) => { + let numPrograms = sources.length; + let programs = new Array(numPrograms); + let pendingPrograms = new Array(numPrograms); + let numPending = numPrograms; + + for (let i = 0; i < numPrograms; ++i) { + let source = sources[i]; + let vsSource = source[0]; + let fsSource = source[1]; + let xformFeedbackVars = source[2]; + programs[i] = new Program(this.gl, this.state, vsSource, fsSource, xformFeedbackVars); + pendingPrograms[i] = programs[i]; + } + + for (let i = 0; i < numPrograms; ++i) { + programs[i].link(); + } + + let poll = () => { + let linked = 0; + for (let i = 0; i < numPending; ++i) { + if (pendingPrograms[i].checkCompletion()) { + pendingPrograms[i].checkLinkage(); + if (pendingPrograms[i].linked) { + ++linked; + } else { + reject(new Error("Program linkage failed")); + return; + } + } else { + pendingPrograms[i - linked] = pendingPrograms[i]; + } + } + + numPending -= linked; + + if (numPending === 0) { + resolve(programs); + } else { + requestAnimationFrame(poll); + } + }; + + poll(); + }); + } + + /** + Restore several programs after a context loss. Will do so in parallel where available. + + @method + @param {...Program} sources Variable number of programs to restore. + + @return {Promise} Promise that will resolve once all programs have been restored. + */ + restorePrograms(...programs) { + return new Promise((resolve, reject) => { + let numPrograms = programs.length; + let pendingPrograms = programs.slice(); + let numPending = numPrograms; + + for (let i = 0; i < numPrograms; ++i) { + programs[i].initialize(); + } + + for (let i = 0; i < numPrograms; ++i) { + programs[i].link(); + } + + for (let i = 0; i < numPrograms; ++i) { + programs[i].checkCompletion(); + } + + let poll = () => { + let linked = 0; + for (let i = 0; i < numPending; ++i) { + if (pendingPrograms[i].checkCompletion()) { + pendingPrograms[i].checkLinkage(); + if (pendingPrograms[i].linked) { + ++linked; + } else { + reject(new Error("Program linkage failed")); + return; + } + } else { + pendingPrograms[i - linked] = pendingPrograms[i]; + } + } + + numPending -= linked; + + if (numPending === 0) { + resolve(); + } else { + requestAnimationFrame(poll); + } + }; + + poll(); + }); + } + + /** + Create a shader. Creating a shader separately from a program allows for + shader reuse. + + @method + @param {GLEnum} type Shader type. + @param {string} source Shader source. + @return {Shader} New Shader object. + */ + createShader(type, source) { + return new Shader(this.gl, this.state, type, source); + } + + /** + Create a vertex array. + + @method + @return {VertexArray} New VertexArray object. + */ + createVertexArray() { + return new VertexArray(this.gl, this.state); + } + + /** + Create a transform feedback object. + + @method + @return {TransformFeedback} New TransformFeedback object. + */ + createTransformFeedback() { + return new TransformFeedback(this.gl, this.state); + } + + /** + Create a vertex buffer. + + @method + @param {GLEnum} type The data type stored in the vertex buffer. + @param {number} itemSize Number of elements per vertex. + @param {ArrayBufferView|number} data Buffer data itself or the total + number of elements to be allocated. + @param {GLEnum} [usage=STATIC_DRAW] Buffer usage. + @return {VertexBuffer} New VertexBuffer object. + */ + createVertexBuffer(type, itemSize, data, usage) { + return new VertexBuffer(this.gl, this.state, type, itemSize, data, usage); + } + + /** + Create a per-vertex matrix buffer. Matrix buffers ensure that columns + are correctly split across attribute locations. + + @method + @param {GLEnum} type The data type stored in the matrix buffer. Valid types + are FLOAT_MAT4, FLOAT_MAT4x2, FLOAT_MAT4x3, FLOAT_MAT3, FLOAT_MAT3x2, + FLOAT_MAT3x4, FLOAT_MAT2, FLOAT_MAT2x3, FLOAT_MAT2x4. + @param {ArrayBufferView} data Matrix buffer data. + @param {GLEnum} [usage=STATIC_DRAW] Buffer usage. + @return {VertexBuffer} New VertexBuffer object. + */ + createMatrixBuffer(type, data, usage) { + return new VertexBuffer(this.gl, this.state, type, 0, data, usage); + } + + /** + Create an buffer without any structure information. Structure + must be fully specified when binding to a VertexArray. + + @method + @param {number} bytesPerVertex Number of bytes per vertex. + @param {ArrayBufferView|number} data Buffer data itself or the total + number of bytes to be allocated. + @param {GLEnum} [usage=STATIC_DRAW] Buffer usage. + @return {VertexBuffer} New VertexBuffer object. + */ + createInterleavedBuffer(bytesPerVertex, data, usage) { + return new VertexBuffer(this.gl, this.state, null, bytesPerVertex, data, usage); + } + + /** + Create an index buffer. + + @method + @param {GLEnum} type The data type stored in the index buffer. + @param {number} itemSize Number of elements per primitive. + @param {ArrayBufferView} data Index buffer data. + @param {GLEnum} [usage=STATIC_DRAW] Buffer usage. + @return {VertexBuffer} New VertexBuffer object. + */ + createIndexBuffer(type, itemSize, data, usage) { + return new VertexBuffer(this.gl, this.state, type, itemSize, data, usage, true); + } + + /** + Create a uniform buffer in std140 layout. NOTE: FLOAT_MAT2, FLOAT_MAT3x2, FLOAT_MAT4x2, + FLOAT_MAT3, FLOAT_MAT2x3, FLOAT_MAT4x3 are supported, but must be manually padded to + 4-float column alignment by the application! + + @method + @param {Array} layout Array indicating the order and types of items to + be stored in the buffer. + @param {GLEnum} [usage=DYNAMIC_DRAW] Buffer usage. + @return {UniformBuffer} New UniformBuffer object. + */ + createUniformBuffer(layout, usage) { + return new UniformBuffer(this.gl, this.state, layout, usage); + } + + /** + Create a 2D texture. Can be used in several ways depending on the type of texture data: + + + @method + @param {DOMElement|ArrayBufferView|Array} [image] Image data. An array can be passed to manually set all levels + of the mipmap chain. If a single level is passed and mipmap filtering is being used, + generateMipmap() will be called to produce the remaining levels. + @param {number} [width] Texture width. Required for array or empty data. + @param {number} [height] Texture height. Required for array or empty data. + @param {Object} [options] Texture options. + @param {GLEnum} [options.internalFormat=RGBA8] Texture data internal format. Must be a sized format. + @param {GLEnum} [options.type] Type of data stored in the texture. Default based on + internalFormat. + @param {boolean} [options.flipY=false] Whether the y-axis should be flipped when unpacking the texture. + @param {boolean} [options.premultiplyAlpha=false] Whether the alpha channel should be pre-multiplied when unpacking the texture. + @param {GLEnum} [options.minFilter] Minification filter. Defaults to + LINEAR_MIPMAP_NEAREST if image data is provided, NEAREST otherwise. + @param {GLEnum} [options.magFilter] Magnification filter. Defaults to LINEAR + if image data is provided, NEAREST otherwise. + @param {GLEnum} [options.wrapS=REPEAT] Horizontal wrap mode. + @param {GLEnum} [options.wrapT=REPEAT] Vertical wrap mode. + @param {GLEnum} [options.compareMode=NONE] Comparison mode. + @param {GLEnum} [options.compareFunc=LEQUAL] Comparison function. + @param {GLEnum} [options.baseLevel] Base mipmap level. + @param {GLEnum} [options.maxLevel] Maximum mipmap level. + @param {GLEnum} [options.minLOD] Mimimum level of detail. + @param {GLEnum} [options.maxLOD] Maximum level of detail. + @param {GLEnum} [options.maxAnisotropy] Maximum anisotropy in filtering. + @return {Texture} New Texture object. + */ + createTexture2D(image, width, height, options) { + if (typeof image === "number") { + // Create empty texture just give width/height. + options = height; + height = width; + width = image; + image = null; + } else if (height === undefined) { + // Passing in a DOM element. Height/width not required. + options = width; + width = image.width; + height = image.height; + } + + return new Texture(this.gl, this.state, this.gl.TEXTURE_2D, image, width, height, undefined, false, options); + } + + /** + Create a 2D texture array. + + @method + @param {ArrayBufferView|Array} image Pixel data. An array can be passed to manually set all levels + of the mipmap chain. If a single level is passed and mipmap filtering is being used, + generateMipmap() will be called to produce the remaining levels. + @param {number} width Texture width. + @param {number} height Texture height. + @param {number} size Number of images in the array. + @param {Object} [options] Texture options. + @param {GLEnum} [options.internalFormat=RGBA8] Texture data internal format. Must be a sized format. + @param {GLEnum} [options.type] Type of data stored in the texture. Default based on + internalFormat. + @param {boolean} [options.flipY=false] Whether the y-axis should be flipped when unpacking the texture. + @param {GLEnum} [options.minFilter] Minification filter. Defaults to + LINEAR_MIPMAP_NEAREST if image data is provided, NEAREST otherwise. + @param {GLEnum} [options.magFilter] Magnification filter. Defaults to LINEAR + if image data is provided, NEAREST otherwise. + @param {GLEnum} [options.wrapS=REPEAT] Horizontal wrap mode. + @param {GLEnum} [options.wrapT=REPEAT] Vertical wrap mode. + @param {GLEnum} [options.wrapR=REPEAT] Depth wrap mode. + @param {GLEnum} [options.compareMode=NONE] Comparison mode. + @param {GLEnum} [options.compareFunc=LEQUAL] Comparison function. + @param {GLEnum} [options.baseLevel] Base mipmap level. + @param {GLEnum} [options.maxLevel] Maximum mipmap level. + @param {GLEnum} [options.minLOD] Mimimum level of detail. + @param {GLEnum} [options.maxLOD] Maximum level of detail. + @param {GLEnum} [options.maxAnisotropy] Maximum anisotropy in filtering. + @return {Texture} New Texture object. + */ + createTextureArray(image, width, height, depth, options) { + if (typeof image === "number") { + // Create empty texture just give width/height/depth. + options = depth; + depth = height; + height = width; + width = image; + image = null; + } + return new Texture(this.gl, this.state, this.gl.TEXTURE_2D_ARRAY, image, width, height, depth, true, options); + } + + /** + Create a 3D texture. + + @method + @param {ArrayBufferView|Array} image Pixel data. An array can be passed to manually set all levels + of the mipmap chain. If a single level is passed and mipmap filtering is being used, + generateMipmap() will be called to produce the remaining levels. + @param {number} width Texture width. + @param {number} height Texture height. + @param {number} depth Texture depth. + @param {Object} [options] Texture options. + @param {GLEnum} [options.internalFormat=RGBA8] Texture data internal format. Must be a sized format. + @param {GLEnum} [options.type] Type of data stored in the texture. Default based on + internalFormat. + @param {boolean} [options.flipY=false] Whether the y-axis should be flipped when unpacking the texture. + @param {GLEnum} [options.minFilter] Minification filter. Defaults to + LINEAR_MIPMAP_NEAREST if image data is provided, NEAREST otherwise. + @param {GLEnum} [options.magFilter] Magnification filter. Defaults to LINEAR + if image data is provided, NEAREST otherwise. + @param {GLEnum} [options.wrapS=REPEAT] Horizontal wrap mode. + @param {GLEnum} [options.wrapT=REPEAT] Vertical wrap mode. + @param {GLEnum} [options.wrapR=REPEAT] Depth wrap mode. + @param {GLEnum} [options.compareMode=NONE] Comparison mode. + @param {GLEnum} [options.compareFunc=LEQUAL] Comparison function. + @param {GLEnum} [options.baseLevel] Base mipmap level. + @param {GLEnum} [options.maxLevel] Maximum mipmap level. + @param {GLEnum} [options.minLOD] Mimimum level of detail. + @param {GLEnum} [options.maxLOD] Maximum level of detail. + @param {GLEnum} [options.maxAnisotropy] Maximum anisotropy in filtering. + @return {Texture} New Texture object. + */ + createTexture3D(image, width, height, depth, options) { + if (typeof image === "number") { + // Create empty texture just give width/height/depth. + options = depth; + depth = height; + height = width; + width = image; + image = null; + } + return new Texture(this.gl, this.state, this.gl.TEXTURE_3D, image, width, height, depth, true, options); + } + + /** + Create a cubemap. + + @method + @param {Object} options Texture options. + @param {DOMElement|ArrayBufferView} [options.negX] The image data for the negative X direction. + Can be any format that would be accepted by texImage2D. + @param {DOMElement|ArrayBufferView} [options.posX] The image data for the positive X direction. + Can be any format that would be accepted by texImage2D. + @param {DOMElement|ArrayBufferView} [options.negY] The image data for the negative Y direction. + Can be any format that would be accepted by texImage2D. + @param {DOMElement|ArrayBufferView} [options.posY] The image data for the positive Y direction. + Can be any format that would be accepted by texImage2D. + @param {DOMElement|ArrayBufferView} [options.negZ] The image data for the negative Z direction. + Can be any format that would be accepted by texImage2D. + @param {DOMElement|ArrayBufferView} [options.posZ] The image data for the positive Z direction. + Can be any format that would be accepted by texImage2D. + @param {number} [options.width] Cubemap side width. Defaults to the width of negX if negX is an image. + @param {number} [options.height] Cubemap side height. Defaults to the height of negX if negX is an image. + @param {GLEnum} [options.internalFormat=RGBA8] Texture data internal format. Must be a sized format. + @param {GLEnum} [options.type] Type of data stored in the texture. Default based on + internalFormat. + @param {boolean} [options.flipY=false] Whether the y-axis should be flipped when unpacking the image. + @param {boolean} [options.premultiplyAlpha=false] Whether the alpha channel should be pre-multiplied when unpacking the image. + @param {GLEnum} [options.minFilter] Minification filter. Defaults to + LINEAR_MIPMAP_NEAREST if image data is provided, NEAREST otherwise. + @param {GLEnum} [options.magFilter] Magnification filter. Defaults to LINEAR + if image data is provided, NEAREST otherwise. + @param {GLEnum} [options.wrapS=REPEAT] Horizontal wrap mode. + @param {GLEnum} [options.wrapT=REPEAT] Vertical wrap mode. + @param {GLEnum} [options.compareMode=NONE] Comparison mode. + @param {GLEnum} [options.compareFunc=LEQUAL] Comparison function. + @param {GLEnum} [options.baseLevel] Base mipmap level. + @param {GLEnum} [options.maxLevel] Maximum mipmap level. + @param {GLEnum} [options.minLOD] Mimimum level of detail. + @param {GLEnum} [options.maxLOD] Maximum level of detail. + @param {GLEnum} [options.maxAnisotropy] Maximum anisotropy in filtering. + @return {Cubemap} New Cubemap object. + */ + createCubemap(options) { + return new Cubemap(this.gl, this.state, options); + } + + /** + Create a renderbuffer. + + @method + @param {number} width Renderbuffer width. + @param {number} height Renderbuffer height. + @param {GLEnum} internalFormat Internal arrangement of the renderbuffer data. + @param {number} [samples=0] Number of MSAA samples. + @return {Renderbuffer} New Renderbuffer object. + */ + createRenderbuffer(width, height, internalFormat, samples = 0) { + return new Renderbuffer(this.gl, width, height, internalFormat, samples); + } + + /** + Create a framebuffer. + + @method + @return {Framebuffer} New Framebuffer object. + */ + createFramebuffer() { + return new Framebuffer(this.gl, this.state); + } + + /** + Create a query. + + @method + @param {GLEnum} target Information to query. + @return {Query} New Query object. + */ + createQuery(target) { + return new Query(this.gl, target); + } + + /** + Create a timer. + + @method + @return {Timer} New Timer object. + */ + createTimer() { + return new Timer(this.gl); + } + + /** + Create a DrawCall. A DrawCall manages the state associated with + a WebGL draw call including a program and associated vertex data, textures, + uniforms and uniform blocks. + + @method + @param {Program} program The program to use for this DrawCall. + @param {VertexArray} [vertexArray=null] Vertex data to use for drawing. + @return {DrawCall} New DrawCall object. + */ + createDrawCall(program, vertexArray, primitive) { + return new DrawCall(this.gl, this.state, program, vertexArray, primitive); + } + + // Enable extensions + initExtensions() { + this.gl.getExtension("EXT_color_buffer_float"); + this.gl.getExtension("OES_texture_float_linear"); + this.gl.getExtension("WEBGL_compressed_texture_s3tc"); + this.gl.getExtension("WEBGL_compressed_texture_s3tc_srgb"); + this.gl.getExtension("WEBGL_compressed_texture_etc"); + this.gl.getExtension("WEBGL_compressed_texture_astc"); + this.gl.getExtension("WEBGL_compressed_texture_pvrtc"); + this.gl.getExtension("EXT_disjoint_timer_query_webgl2"); + this.gl.getExtension("EXT_disjoint_timer_query"); + this.gl.getExtension("EXT_texture_filter_anisotropic"); + + this.state.extensions.debugShaders = this.gl.getExtension("WEBGL_debug_shaders"); + this.contextLostExt = this.gl.getExtension("WEBGL_lose_context"); + + // Draft extensions + this.gl.getExtension("KHR_parallel_shader_compile"); + this.state.extensions.multiDrawInstanced = this.gl.getExtension("WEBGL_multi_draw_instanced"); + } + +} diff --git a/build/module/constants.js b/build/module/constants.js new file mode 100644 index 00000000..a5db998f --- /dev/null +++ b/build/module/constants.js @@ -0,0 +1,814 @@ +/////////////////////////////////////////////////////////////////////////////////// +// The MIT License (MIT) +// +// Copyright (c) 2017 Tarek Sherif +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +/////////////////////////////////////////////////////////////////////////////////// + +// https://www.khronos.org/registry/webgl/specs/1.0/ +// https://www.khronos.org/registry/webgl/specs/latest/2.0/#1.1 +export const GL = { + DEPTH_BUFFER_BIT: 0x00000100, + STENCIL_BUFFER_BIT: 0x00000400, + COLOR_BUFFER_BIT: 0x00004000, + POINTS: 0x0000, + LINES: 0x0001, + LINE_LOOP: 0x0002, + LINE_STRIP: 0x0003, + TRIANGLES: 0x0004, + TRIANGLE_STRIP: 0x0005, + TRIANGLE_FAN: 0x0006, + ZERO: 0, + ONE: 1, + SRC_COLOR: 0x0300, + ONE_MINUS_SRC_COLOR: 0x0301, + SRC_ALPHA: 0x0302, + ONE_MINUS_SRC_ALPHA: 0x0303, + DST_ALPHA: 0x0304, + ONE_MINUS_DST_ALPHA: 0x0305, + DST_COLOR: 0x0306, + ONE_MINUS_DST_COLOR: 0x0307, + SRC_ALPHA_SATURATE: 0x0308, + FUNC_ADD: 0x8006, + BLEND_EQUATION: 0x8009, + BLEND_EQUATION_RGB: 0x8009, + BLEND_EQUATION_ALPHA: 0x883D, + FUNC_SUBTRACT: 0x800A, + FUNC_REVERSE_SUBTRACT: 0x800B, + BLEND_DST_RGB: 0x80C8, + BLEND_SRC_RGB: 0x80C9, + BLEND_DST_ALPHA: 0x80CA, + BLEND_SRC_ALPHA: 0x80CB, + CONSTANT_COLOR: 0x8001, + ONE_MINUS_CONSTANT_COLOR: 0x8002, + CONSTANT_ALPHA: 0x8003, + ONE_MINUS_CONSTANT_ALPHA: 0x8004, + BLEND_COLOR: 0x8005, + ARRAY_BUFFER: 0x8892, + ELEMENT_ARRAY_BUFFER: 0x8893, + ARRAY_BUFFER_BINDING: 0x8894, + ELEMENT_ARRAY_BUFFER_BINDING: 0x8895, + STREAM_DRAW: 0x88E0, + STATIC_DRAW: 0x88E4, + DYNAMIC_DRAW: 0x88E8, + BUFFER_SIZE: 0x8764, + BUFFER_USAGE: 0x8765, + CURRENT_VERTEX_ATTRIB: 0x8626, + FRONT: 0x0404, + BACK: 0x0405, + FRONT_AND_BACK: 0x0408, + CULL_FACE: 0x0B44, + BLEND: 0x0BE2, + DITHER: 0x0BD0, + STENCIL_TEST: 0x0B90, + DEPTH_TEST: 0x0B71, + SCISSOR_TEST: 0x0C11, + POLYGON_OFFSET_FILL: 0x8037, + SAMPLE_ALPHA_TO_COVERAGE: 0x809E, + SAMPLE_COVERAGE: 0x80A0, + NO_ERROR: 0, + INVALID_ENUM: 0x0500, + INVALID_VALUE: 0x0501, + INVALID_OPERATION: 0x0502, + OUT_OF_MEMORY: 0x0505, + CW: 0x0900, + CCW: 0x0901, + LINE_WIDTH: 0x0B21, + ALIASED_POINT_SIZE_RANGE: 0x846D, + ALIASED_LINE_WIDTH_RANGE: 0x846E, + CULL_FACE_MODE: 0x0B45, + FRONT_FACE: 0x0B46, + DEPTH_RANGE: 0x0B70, + DEPTH_WRITEMASK: 0x0B72, + DEPTH_CLEAR_VALUE: 0x0B73, + DEPTH_FUNC: 0x0B74, + STENCIL_CLEAR_VALUE: 0x0B91, + STENCIL_FUNC: 0x0B92, + STENCIL_FAIL: 0x0B94, + STENCIL_PASS_DEPTH_FAIL: 0x0B95, + STENCIL_PASS_DEPTH_PASS: 0x0B96, + STENCIL_REF: 0x0B97, + STENCIL_VALUE_MASK: 0x0B93, + STENCIL_WRITEMASK: 0x0B98, + STENCIL_BACK_FUNC: 0x8800, + STENCIL_BACK_FAIL: 0x8801, + STENCIL_BACK_PASS_DEPTH_FAIL: 0x8802, + STENCIL_BACK_PASS_DEPTH_PASS: 0x8803, + STENCIL_BACK_REF: 0x8CA3, + STENCIL_BACK_VALUE_MASK: 0x8CA4, + STENCIL_BACK_WRITEMASK: 0x8CA5, + VIEWPORT: 0x0BA2, + SCISSOR_BOX: 0x0C10, + COLOR_CLEAR_VALUE: 0x0C22, + COLOR_WRITEMASK: 0x0C23, + UNPACK_ALIGNMENT: 0x0CF5, + PACK_ALIGNMENT: 0x0D05, + MAX_TEXTURE_SIZE: 0x0D33, + MAX_VIEWPORT_DIMS: 0x0D3A, + SUBPIXEL_BITS: 0x0D50, + RED_BITS: 0x0D52, + GREEN_BITS: 0x0D53, + BLUE_BITS: 0x0D54, + ALPHA_BITS: 0x0D55, + DEPTH_BITS: 0x0D56, + STENCIL_BITS: 0x0D57, + POLYGON_OFFSET_UNITS: 0x2A00, + POLYGON_OFFSET_FACTOR: 0x8038, + TEXTURE_BINDING_2D: 0x8069, + SAMPLE_BUFFERS: 0x80A8, + SAMPLES: 0x80A9, + SAMPLE_COVERAGE_VALUE: 0x80AA, + SAMPLE_COVERAGE_INVERT: 0x80AB, + COMPRESSED_TEXTURE_FORMATS: 0x86A3, + DONT_CARE: 0x1100, + FASTEST: 0x1101, + NICEST: 0x1102, + GENERATE_MIPMAP_HINT: 0x8192, + BYTE: 0x1400, + UNSIGNED_BYTE: 0x1401, + SHORT: 0x1402, + UNSIGNED_SHORT: 0x1403, + INT: 0x1404, + UNSIGNED_INT: 0x1405, + FLOAT: 0x1406, + DEPTH_COMPONENT: 0x1902, + ALPHA: 0x1906, + RGB: 0x1907, + RGBA: 0x1908, + LUMINANCE: 0x1909, + LUMINANCE_ALPHA: 0x190A, + UNSIGNED_SHORT_4_4_4_4: 0x8033, + UNSIGNED_SHORT_5_5_5_1: 0x8034, + UNSIGNED_SHORT_5_6_5: 0x8363, + FRAGMENT_SHADER: 0x8B30, + VERTEX_SHADER: 0x8B31, + MAX_VERTEX_ATTRIBS: 0x8869, + MAX_VERTEX_UNIFORM_VECTORS: 0x8DFB, + MAX_VARYING_VECTORS: 0x8DFC, + MAX_COMBINED_TEXTURE_IMAGE_UNITS: 0x8B4D, + MAX_VERTEX_TEXTURE_IMAGE_UNITS: 0x8B4C, + MAX_TEXTURE_IMAGE_UNITS: 0x8872, + MAX_FRAGMENT_UNIFORM_VECTORS: 0x8DFD, + SHADER_TYPE: 0x8B4F, + DELETE_STATUS: 0x8B80, + LINK_STATUS: 0x8B82, + VALIDATE_STATUS: 0x8B83, + ATTACHED_SHADERS: 0x8B85, + ACTIVE_UNIFORMS: 0x8B86, + ACTIVE_ATTRIBUTES: 0x8B89, + SHADING_LANGUAGE_VERSION: 0x8B8C, + CURRENT_PROGRAM: 0x8B8D, + NEVER: 0x0200, + LESS: 0x0201, + EQUAL: 0x0202, + LEQUAL: 0x0203, + GREATER: 0x0204, + NOTEQUAL: 0x0205, + GEQUAL: 0x0206, + ALWAYS: 0x0207, + KEEP: 0x1E00, + REPLACE: 0x1E01, + INCR: 0x1E02, + DECR: 0x1E03, + INVERT: 0x150A, + INCR_WRAP: 0x8507, + DECR_WRAP: 0x8508, + VENDOR: 0x1F00, + RENDERER: 0x1F01, + VERSION: 0x1F02, + NEAREST: 0x2600, + LINEAR: 0x2601, + NEAREST_MIPMAP_NEAREST: 0x2700, + LINEAR_MIPMAP_NEAREST: 0x2701, + NEAREST_MIPMAP_LINEAR: 0x2702, + LINEAR_MIPMAP_LINEAR: 0x2703, + TEXTURE_MAG_FILTER: 0x2800, + TEXTURE_MIN_FILTER: 0x2801, + TEXTURE_WRAP_S: 0x2802, + TEXTURE_WRAP_T: 0x2803, + TEXTURE_2D: 0x0DE1, + TEXTURE: 0x1702, + TEXTURE_CUBE_MAP: 0x8513, + TEXTURE_BINDING_CUBE_MAP: 0x8514, + TEXTURE_CUBE_MAP_POSITIVE_X: 0x8515, + TEXTURE_CUBE_MAP_NEGATIVE_X: 0x8516, + TEXTURE_CUBE_MAP_POSITIVE_Y: 0x8517, + TEXTURE_CUBE_MAP_NEGATIVE_Y: 0x8518, + TEXTURE_CUBE_MAP_POSITIVE_Z: 0x8519, + TEXTURE_CUBE_MAP_NEGATIVE_Z: 0x851A, + MAX_CUBE_MAP_TEXTURE_SIZE: 0x851C, + TEXTURE0: 0x84C0, + TEXTURE1: 0x84C1, + TEXTURE2: 0x84C2, + TEXTURE3: 0x84C3, + TEXTURE4: 0x84C4, + TEXTURE5: 0x84C5, + TEXTURE6: 0x84C6, + TEXTURE7: 0x84C7, + TEXTURE8: 0x84C8, + TEXTURE9: 0x84C9, + TEXTURE10: 0x84CA, + TEXTURE11: 0x84CB, + TEXTURE12: 0x84CC, + TEXTURE13: 0x84CD, + TEXTURE14: 0x84CE, + TEXTURE15: 0x84CF, + TEXTURE16: 0x84D0, + TEXTURE17: 0x84D1, + TEXTURE18: 0x84D2, + TEXTURE19: 0x84D3, + TEXTURE20: 0x84D4, + TEXTURE21: 0x84D5, + TEXTURE22: 0x84D6, + TEXTURE23: 0x84D7, + TEXTURE24: 0x84D8, + TEXTURE25: 0x84D9, + TEXTURE26: 0x84DA, + TEXTURE27: 0x84DB, + TEXTURE28: 0x84DC, + TEXTURE29: 0x84DD, + TEXTURE30: 0x84DE, + TEXTURE31: 0x84DF, + ACTIVE_TEXTURE: 0x84E0, + REPEAT: 0x2901, + CLAMP_TO_EDGE: 0x812F, + MIRRORED_REPEAT: 0x8370, + FLOAT_VEC2: 0x8B50, + FLOAT_VEC3: 0x8B51, + FLOAT_VEC4: 0x8B52, + INT_VEC2: 0x8B53, + INT_VEC3: 0x8B54, + INT_VEC4: 0x8B55, + BOOL: 0x8B56, + BOOL_VEC2: 0x8B57, + BOOL_VEC3: 0x8B58, + BOOL_VEC4: 0x8B59, + FLOAT_MAT2: 0x8B5A, + FLOAT_MAT3: 0x8B5B, + FLOAT_MAT4: 0x8B5C, + SAMPLER_2D: 0x8B5E, + SAMPLER_CUBE: 0x8B60, + VERTEX_ATTRIB_ARRAY_ENABLED: 0x8622, + VERTEX_ATTRIB_ARRAY_SIZE: 0x8623, + VERTEX_ATTRIB_ARRAY_STRIDE: 0x8624, + VERTEX_ATTRIB_ARRAY_TYPE: 0x8625, + VERTEX_ATTRIB_ARRAY_NORMALIZED: 0x886A, + VERTEX_ATTRIB_ARRAY_POINTER: 0x8645, + VERTEX_ATTRIB_ARRAY_BUFFER_BINDING: 0x889F, + IMPLEMENTATION_COLOR_READ_TYPE: 0x8B9A, + IMPLEMENTATION_COLOR_READ_FORMAT: 0x8B9B, + COMPILE_STATUS: 0x8B81, + LOW_FLOAT: 0x8DF0, + MEDIUM_FLOAT: 0x8DF1, + HIGH_FLOAT: 0x8DF2, + LOW_INT: 0x8DF3, + MEDIUM_INT: 0x8DF4, + HIGH_INT: 0x8DF5, + FRAMEBUFFER: 0x8D40, + RENDERBUFFER: 0x8D41, + RGBA4: 0x8056, + RGB5_A1: 0x8057, + RGB565: 0x8D62, + DEPTH_COMPONENT16: 0x81A5, + STENCIL_INDEX: 0x1901, + STENCIL_INDEX8: 0x8D48, + DEPTH_STENCIL: 0x84F9, + RENDERBUFFER_WIDTH: 0x8D42, + RENDERBUFFER_HEIGHT: 0x8D43, + RENDERBUFFER_INTERNAL_FORMAT: 0x8D44, + RENDERBUFFER_RED_SIZE: 0x8D50, + RENDERBUFFER_GREEN_SIZE: 0x8D51, + RENDERBUFFER_BLUE_SIZE: 0x8D52, + RENDERBUFFER_ALPHA_SIZE: 0x8D53, + RENDERBUFFER_DEPTH_SIZE: 0x8D54, + RENDERBUFFER_STENCIL_SIZE: 0x8D55, + FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE: 0x8CD0, + FRAMEBUFFER_ATTACHMENT_OBJECT_NAME: 0x8CD1, + FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL: 0x8CD2, + FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE: 0x8CD3, + COLOR_ATTACHMENT0: 0x8CE0, + DEPTH_ATTACHMENT: 0x8D00, + STENCIL_ATTACHMENT: 0x8D20, + DEPTH_STENCIL_ATTACHMENT: 0x821A, + NONE: 0, + FRAMEBUFFER_COMPLETE: 0x8CD5, + FRAMEBUFFER_INCOMPLETE_ATTACHMENT: 0x8CD6, + FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: 0x8CD7, + FRAMEBUFFER_INCOMPLETE_DIMENSIONS: 0x8CD9, + FRAMEBUFFER_UNSUPPORTED: 0x8CDD, + FRAMEBUFFER_BINDING: 0x8CA6, + RENDERBUFFER_BINDING: 0x8CA7, + MAX_RENDERBUFFER_SIZE: 0x84E8, + INVALID_FRAMEBUFFER_OPERATION: 0x0506, + UNPACK_FLIP_Y_WEBGL: 0x9240, + UNPACK_PREMULTIPLY_ALPHA_WEBGL: 0x9241, + CONTEXT_LOST_WEBGL: 0x9242, + UNPACK_COLORSPACE_CONVERSION_WEBGL: 0x9243, + BROWSER_DEFAULT_WEBGL: 0x9244, + READ_BUFFER: 0x0C02, + UNPACK_ROW_LENGTH: 0x0CF2, + UNPACK_SKIP_ROWS: 0x0CF3, + UNPACK_SKIP_PIXELS: 0x0CF4, + PACK_ROW_LENGTH: 0x0D02, + PACK_SKIP_ROWS: 0x0D03, + PACK_SKIP_PIXELS: 0x0D04, + COLOR: 0x1800, + DEPTH: 0x1801, + STENCIL: 0x1802, + RED: 0x1903, + RGB8: 0x8051, + RGBA8: 0x8058, + RGB10_A2: 0x8059, + TEXTURE_BINDING_3D: 0x806A, + UNPACK_SKIP_IMAGES: 0x806D, + UNPACK_IMAGE_HEIGHT: 0x806E, + TEXTURE_3D: 0x806F, + TEXTURE_WRAP_R: 0x8072, + MAX_3D_TEXTURE_SIZE: 0x8073, + UNSIGNED_INT_2_10_10_10_REV: 0x8368, + MAX_ELEMENTS_VERTICES: 0x80E8, + MAX_ELEMENTS_INDICES: 0x80E9, + TEXTURE_MIN_LOD: 0x813A, + TEXTURE_MAX_LOD: 0x813B, + TEXTURE_BASE_LEVEL: 0x813C, + TEXTURE_MAX_LEVEL: 0x813D, + MIN: 0x8007, + MAX: 0x8008, + DEPTH_COMPONENT24: 0x81A6, + MAX_TEXTURE_LOD_BIAS: 0x84FD, + TEXTURE_COMPARE_MODE: 0x884C, + TEXTURE_COMPARE_FUNC: 0x884D, + CURRENT_QUERY: 0x8865, + QUERY_RESULT: 0x8866, + QUERY_RESULT_AVAILABLE: 0x8867, + STREAM_READ: 0x88E1, + STREAM_COPY: 0x88E2, + STATIC_READ: 0x88E5, + STATIC_COPY: 0x88E6, + DYNAMIC_READ: 0x88E9, + DYNAMIC_COPY: 0x88EA, + MAX_DRAW_BUFFERS: 0x8824, + DRAW_BUFFER0: 0x8825, + DRAW_BUFFER1: 0x8826, + DRAW_BUFFER2: 0x8827, + DRAW_BUFFER3: 0x8828, + DRAW_BUFFER4: 0x8829, + DRAW_BUFFER5: 0x882A, + DRAW_BUFFER6: 0x882B, + DRAW_BUFFER7: 0x882C, + DRAW_BUFFER8: 0x882D, + DRAW_BUFFER9: 0x882E, + DRAW_BUFFER10: 0x882F, + DRAW_BUFFER11: 0x8830, + DRAW_BUFFER12: 0x8831, + DRAW_BUFFER13: 0x8832, + DRAW_BUFFER14: 0x8833, + DRAW_BUFFER15: 0x8834, + MAX_FRAGMENT_UNIFORM_COMPONENTS: 0x8B49, + MAX_VERTEX_UNIFORM_COMPONENTS: 0x8B4A, + SAMPLER_3D: 0x8B5F, + SAMPLER_2D_SHADOW: 0x8B62, + FRAGMENT_SHADER_DERIVATIVE_HINT: 0x8B8B, + PIXEL_PACK_BUFFER: 0x88EB, + PIXEL_UNPACK_BUFFER: 0x88EC, + PIXEL_PACK_BUFFER_BINDING: 0x88ED, + PIXEL_UNPACK_BUFFER_BINDING: 0x88EF, + FLOAT_MAT2x3: 0x8B65, + FLOAT_MAT2x4: 0x8B66, + FLOAT_MAT3x2: 0x8B67, + FLOAT_MAT3x4: 0x8B68, + FLOAT_MAT4x2: 0x8B69, + FLOAT_MAT4x3: 0x8B6A, + SRGB: 0x8C40, + SRGB8: 0x8C41, + SRGB8_ALPHA8: 0x8C43, + COMPARE_REF_TO_TEXTURE: 0x884E, + RGBA32F: 0x8814, + RGB32F: 0x8815, + RGBA16F: 0x881A, + RGB16F: 0x881B, + VERTEX_ATTRIB_ARRAY_INTEGER: 0x88FD, + MAX_ARRAY_TEXTURE_LAYERS: 0x88FF, + MIN_PROGRAM_TEXEL_OFFSET: 0x8904, + MAX_PROGRAM_TEXEL_OFFSET: 0x8905, + MAX_VARYING_COMPONENTS: 0x8B4B, + TEXTURE_2D_ARRAY: 0x8C1A, + TEXTURE_BINDING_2D_ARRAY: 0x8C1D, + R11F_G11F_B10F: 0x8C3A, + UNSIGNED_INT_10F_11F_11F_REV: 0x8C3B, + RGB9_E5: 0x8C3D, + UNSIGNED_INT_5_9_9_9_REV: 0x8C3E, + TRANSFORM_FEEDBACK_BUFFER_MODE: 0x8C7F, + MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS: 0x8C80, + TRANSFORM_FEEDBACK_VARYINGS: 0x8C83, + TRANSFORM_FEEDBACK_BUFFER_START: 0x8C84, + TRANSFORM_FEEDBACK_BUFFER_SIZE: 0x8C85, + TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN: 0x8C88, + RASTERIZER_DISCARD: 0x8C89, + MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS: 0x8C8A, + MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS: 0x8C8B, + INTERLEAVED_ATTRIBS: 0x8C8C, + SEPARATE_ATTRIBS: 0x8C8D, + TRANSFORM_FEEDBACK_BUFFER: 0x8C8E, + TRANSFORM_FEEDBACK_BUFFER_BINDING: 0x8C8F, + RGBA32UI: 0x8D70, + RGB32UI: 0x8D71, + RGBA16UI: 0x8D76, + RGB16UI: 0x8D77, + RGBA8UI: 0x8D7C, + RGB8UI: 0x8D7D, + RGBA32I: 0x8D82, + RGB32I: 0x8D83, + RGBA16I: 0x8D88, + RGB16I: 0x8D89, + RGBA8I: 0x8D8E, + RGB8I: 0x8D8F, + RED_INTEGER: 0x8D94, + RGB_INTEGER: 0x8D98, + RGBA_INTEGER: 0x8D99, + SAMPLER_2D_ARRAY: 0x8DC1, + SAMPLER_2D_ARRAY_SHADOW: 0x8DC4, + SAMPLER_CUBE_SHADOW: 0x8DC5, + UNSIGNED_INT_VEC2: 0x8DC6, + UNSIGNED_INT_VEC3: 0x8DC7, + UNSIGNED_INT_VEC4: 0x8DC8, + INT_SAMPLER_2D: 0x8DCA, + INT_SAMPLER_3D: 0x8DCB, + INT_SAMPLER_CUBE: 0x8DCC, + INT_SAMPLER_2D_ARRAY: 0x8DCF, + UNSIGNED_INT_SAMPLER_2D: 0x8DD2, + UNSIGNED_INT_SAMPLER_3D: 0x8DD3, + UNSIGNED_INT_SAMPLER_CUBE: 0x8DD4, + UNSIGNED_INT_SAMPLER_2D_ARRAY: 0x8DD7, + DEPTH_COMPONENT32F: 0x8CAC, + DEPTH32F_STENCIL8: 0x8CAD, + FLOAT_32_UNSIGNED_INT_24_8_REV: 0x8DAD, + FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING: 0x8210, + FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE: 0x8211, + FRAMEBUFFER_ATTACHMENT_RED_SIZE: 0x8212, + FRAMEBUFFER_ATTACHMENT_GREEN_SIZE: 0x8213, + FRAMEBUFFER_ATTACHMENT_BLUE_SIZE: 0x8214, + FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE: 0x8215, + FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE: 0x8216, + FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE: 0x8217, + FRAMEBUFFER_DEFAULT: 0x8218, + UNSIGNED_INT_24_8: 0x84FA, + DEPTH24_STENCIL8: 0x88F0, + UNSIGNED_NORMALIZED: 0x8C17, + DRAW_FRAMEBUFFER_BINDING: 0x8CA6, + READ_FRAMEBUFFER: 0x8CA8, + DRAW_FRAMEBUFFER: 0x8CA9, + READ_FRAMEBUFFER_BINDING: 0x8CAA, + RENDERBUFFER_SAMPLES: 0x8CAB, + FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER: 0x8CD4, + MAX_COLOR_ATTACHMENTS: 0x8CDF, + COLOR_ATTACHMENT1: 0x8CE1, + COLOR_ATTACHMENT2: 0x8CE2, + COLOR_ATTACHMENT3: 0x8CE3, + COLOR_ATTACHMENT4: 0x8CE4, + COLOR_ATTACHMENT5: 0x8CE5, + COLOR_ATTACHMENT6: 0x8CE6, + COLOR_ATTACHMENT7: 0x8CE7, + COLOR_ATTACHMENT8: 0x8CE8, + COLOR_ATTACHMENT9: 0x8CE9, + COLOR_ATTACHMENT10: 0x8CEA, + COLOR_ATTACHMENT11: 0x8CEB, + COLOR_ATTACHMENT12: 0x8CEC, + COLOR_ATTACHMENT13: 0x8CED, + COLOR_ATTACHMENT14: 0x8CEE, + COLOR_ATTACHMENT15: 0x8CEF, + FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: 0x8D56, + MAX_SAMPLES: 0x8D57, + HALF_FLOAT: 0x140B, + RG: 0x8227, + RG_INTEGER: 0x8228, + R8: 0x8229, + RG8: 0x822B, + R16F: 0x822D, + R32F: 0x822E, + RG16F: 0x822F, + RG32F: 0x8230, + R8I: 0x8231, + R8UI: 0x8232, + R16I: 0x8233, + R16UI: 0x8234, + R32I: 0x8235, + R32UI: 0x8236, + RG8I: 0x8237, + RG8UI: 0x8238, + RG16I: 0x8239, + RG16UI: 0x823A, + RG32I: 0x823B, + RG32UI: 0x823C, + VERTEX_ARRAY_BINDING: 0x85B5, + R8_SNORM: 0x8F94, + RG8_SNORM: 0x8F95, + RGB8_SNORM: 0x8F96, + RGBA8_SNORM: 0x8F97, + SIGNED_NORMALIZED: 0x8F9C, + COPY_READ_BUFFER: 0x8F36, + COPY_WRITE_BUFFER: 0x8F37, + COPY_READ_BUFFER_BINDING: 0x8F36, + COPY_WRITE_BUFFER_BINDING: 0x8F37, + UNIFORM_BUFFER: 0x8A11, + UNIFORM_BUFFER_BINDING: 0x8A28, + UNIFORM_BUFFER_START: 0x8A29, + UNIFORM_BUFFER_SIZE: 0x8A2A, + MAX_VERTEX_UNIFORM_BLOCKS: 0x8A2B, + MAX_FRAGMENT_UNIFORM_BLOCKS: 0x8A2D, + MAX_COMBINED_UNIFORM_BLOCKS: 0x8A2E, + MAX_UNIFORM_BUFFER_BINDINGS: 0x8A2F, + MAX_UNIFORM_BLOCK_SIZE: 0x8A30, + MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS: 0x8A31, + MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS: 0x8A33, + UNIFORM_BUFFER_OFFSET_ALIGNMENT: 0x8A34, + ACTIVE_UNIFORM_BLOCKS: 0x8A36, + UNIFORM_TYPE: 0x8A37, + UNIFORM_SIZE: 0x8A38, + UNIFORM_BLOCK_INDEX: 0x8A3A, + UNIFORM_OFFSET: 0x8A3B, + UNIFORM_ARRAY_STRIDE: 0x8A3C, + UNIFORM_MATRIX_STRIDE: 0x8A3D, + UNIFORM_IS_ROW_MAJOR: 0x8A3E, + UNIFORM_BLOCK_BINDING: 0x8A3F, + UNIFORM_BLOCK_DATA_SIZE: 0x8A40, + UNIFORM_BLOCK_ACTIVE_UNIFORMS: 0x8A42, + UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES: 0x8A43, + UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER: 0x8A44, + UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER: 0x8A46, + INVALID_INDEX: 0xFFFFFFFF, + MAX_VERTEX_OUTPUT_COMPONENTS: 0x9122, + MAX_FRAGMENT_INPUT_COMPONENTS: 0x9125, + MAX_SERVER_WAIT_TIMEOUT: 0x9111, + OBJECT_TYPE: 0x9112, + SYNC_CONDITION: 0x9113, + SYNC_STATUS: 0x9114, + SYNC_FLAGS: 0x9115, + SYNC_FENCE: 0x9116, + SYNC_GPU_COMMANDS_COMPLETE: 0x9117, + UNSIGNALED: 0x9118, + SIGNALED: 0x9119, + ALREADY_SIGNALED: 0x911A, + TIMEOUT_EXPIRED: 0x911B, + CONDITION_SATISFIED: 0x911C, + WAIT_FAILED: 0x911D, + SYNC_FLUSH_COMMANDS_BIT: 0x00000001, + VERTEX_ATTRIB_ARRAY_DIVISOR: 0x88FE, + ANY_SAMPLES_PASSED: 0x8C2F, + ANY_SAMPLES_PASSED_CONSERVATIVE: 0x8D6A, + SAMPLER_BINDING: 0x8919, + RGB10_A2UI: 0x906F, + INT_2_10_10_10_REV: 0x8D9F, + TRANSFORM_FEEDBACK: 0x8E22, + TRANSFORM_FEEDBACK_PAUSED: 0x8E23, + TRANSFORM_FEEDBACK_ACTIVE: 0x8E24, + TRANSFORM_FEEDBACK_BINDING: 0x8E25, + TEXTURE_IMMUTABLE_FORMAT: 0x912F, + MAX_ELEMENT_INDEX: 0x8D6B, + TEXTURE_IMMUTABLE_LEVELS: 0x82DF, + TIMEOUT_IGNORED: -1, + MAX_CLIENT_WAIT_TIMEOUT_WEBGL: 0x9247, + + // https://www.khronos.org/registry/webgl/extensions/EXT_disjoint_timer_query_webgl2/ + QUERY_COUNTER_BITS_EXT: 0x8864, + TIME_ELAPSED_EXT: 0x88BF, + TIMESTAMP_EXT: 0x8E28, + GPU_DISJOINT_EXT: 0x8FBB, + + // https://www.khronos.org/registry/webgl/extensions/EXT_texture_filter_anisotropic/ + TEXTURE_MAX_ANISOTROPY_EXT: 0x84FE, + MAX_TEXTURE_MAX_ANISOTROPY_EXT: 0x84FF, + + // https://www.khronos.org/registry/webgl/extensions/WEBGL_debug_renderer_info/ + UNMASKED_VENDOR_WEBGL: 0x9245, + UNMASKED_RENDERER_WEBGL: 0x9246, + + // https://www.khronos.org/registry/webgl/extensions/KHR_parallel_shader_compile/ + COMPLETION_STATUS_KHR: 0x91B1, + + // https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_s3tc/ + COMPRESSED_RGB_S3TC_DXT1_EXT: 0x83F0, + COMPRESSED_RGBA_S3TC_DXT1_EXT: 0x83F1, + COMPRESSED_RGBA_S3TC_DXT3_EXT: 0x83F2, + COMPRESSED_RGBA_S3TC_DXT5_EXT: 0x83F3, + + // https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_s3tc_srgb/ + COMPRESSED_SRGB_S3TC_DXT1_EXT: 0x8C4C, + COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT: 0x8C4D, + COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT: 0x8C4E, + COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT: 0x8C4F, + + // https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_etc/ + COMPRESSED_R11_EAC: 0x9270, + COMPRESSED_SIGNED_R11_EAC: 0x9271, + COMPRESSED_RG11_EAC: 0x9272, + COMPRESSED_SIGNED_RG11_EAC: 0x9273, + COMPRESSED_RGB8_ETC2: 0x9274, + COMPRESSED_SRGB8_ETC2: 0x9275, + COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2: 0x9276, + COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2: 0x9277, + COMPRESSED_RGBA8_ETC2_EAC: 0x9278, + COMPRESSED_SRGB8_ALPHA8_ETC2_EAC: 0x9279, + + // https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_pvrtc/ + COMPRESSED_RGB_PVRTC_4BPPV1_IMG: 0x8C00, + COMPRESSED_RGB_PVRTC_2BPPV1_IMG: 0x8C01, + COMPRESSED_RGBA_PVRTC_4BPPV1_IMG: 0x8C02, + COMPRESSED_RGBA_PVRTC_2BPPV1_IMG: 0x8C03, + + // https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_astc/ + COMPRESSED_RGBA_ASTC_4x4_KHR: 0x93B0, + COMPRESSED_RGBA_ASTC_5x4_KHR: 0x93B1, + COMPRESSED_RGBA_ASTC_5x5_KHR: 0x93B2, + COMPRESSED_RGBA_ASTC_6x5_KHR: 0x93B3, + COMPRESSED_RGBA_ASTC_6x6_KHR: 0x93B4, + COMPRESSED_RGBA_ASTC_8x5_KHR: 0x93B5, + COMPRESSED_RGBA_ASTC_8x6_KHR: 0x93B6, + COMPRESSED_RGBA_ASTC_8x8_KHR: 0x93B7, + COMPRESSED_RGBA_ASTC_10x5_KHR: 0x93B8, + COMPRESSED_RGBA_ASTC_10x6_KHR: 0x93B9, + COMPRESSED_RGBA_ASTC_10x8_KHR: 0x93BA, + COMPRESSED_RGBA_ASTC_10x10_KHR: 0x93BB, + COMPRESSED_RGBA_ASTC_12x10_KHR: 0x93BC, + COMPRESSED_RGBA_ASTC_12x12_KHR: 0x93BD, + COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR: 0x93D0, + COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR: 0x93D1, + COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR: 0x93D2, + COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR: 0x93D3, + COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR: 0x93D4, + COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR: 0x93D5, + COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR: 0x93D6, + COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR: 0x93D7, + COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR: 0x93D8, + COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR: 0x93D9, + COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR: 0x93DA, + COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR: 0x93DB, + COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR: 0x93DC, + COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR: 0x93DD +}; + + +export const TYPE_SIZE = { + [GL.BYTE]: 1, + [GL.UNSIGNED_BYTE]: 1, + [GL.SHORT]: 2, + [GL.UNSIGNED_SHORT]: 2, + [GL.INT]: 4, + [GL.UNSIGNED_INT]: 4, + [GL.FLOAT]: 4 +}; + +// Internal format => [format, type] +export const TEXTURE_FORMATS = { + [GL.R8]: [ GL.RED, GL.UNSIGNED_BYTE ], + [GL.R8_SNORM]: [ GL.RED, GL.BYTE ], + [GL.R16F]: [ GL.RED, GL.FLOAT ], + [GL.R32F]: [ GL.RED, GL.FLOAT ], + [GL.R8UI]: [ GL.RED_INTEGER, GL.UNSIGNED_BYTE ], + [GL.R8I]: [ GL.RED_INTEGER, GL.BYTE ], + [GL.R16UI]: [ GL.RED_INTEGER, GL.UNSIGNED_SHORT ], + [GL.R16I]: [ GL.RED_INTEGER, GL.SHORT ], + [GL.R32UI]: [ GL.RED_INTEGER, GL.UNSIGNED_INT ], + [GL.R32I]: [ GL.RED_INTEGER, GL.INT ], + [GL.RG8]: [ GL.RG, GL.UNSIGNED_BYTE ], + [GL.RG8_SNORM]: [ GL.RG, GL.BYTE ], + [GL.RG16F]: [ GL.RG, GL.FLOAT ], + [GL.RG32F]: [ GL.RG, GL.FLOAT ], + [GL.RG8UI]: [ GL.RG_INTEGER, GL.UNSIGNED_BYTE ], + [GL.RG8I]: [ GL.RG_INTEGER, GL.BYTE ], + [GL.RG16UI]: [ GL.RG_INTEGER, GL.UNSIGNED_SHORT ], + [GL.RG16I]: [ GL.RG_INTEGER, GL.SHORT ], + [GL.RG32UI]: [ GL.RG_INTEGER, GL.UNSIGNED_INT ], + [GL.RG32I]: [ GL.RG_INTEGER, GL.INT ], + [GL.RGB8]: [ GL.RGB, GL.UNSIGNED_BYTE ], + [GL.SRGB8]: [ GL.RGB, GL.UNSIGNED_BYTE ], + [GL.RGB565]: [ GL.RGB, GL.UNSIGNED_SHORT_5_6_5 ], + [GL.RGB8_SNORM]: [ GL.RGB, GL.BYTE ], + [GL.R11F_G11F_B10F]: [ GL.RGB, GL.UNSIGNED_INT_10F_11F_11F_REV ], + [GL.RGB9_E5]: [ GL.RGB, GL.UNSIGNED_INT_5_9_9_9_REV ], + [GL.RGB16F]: [ GL.RGB, GL.FLOAT ], + [GL.RGB32F]: [ GL.RGB, GL.FLOAT ], + [GL.RGB8UI]: [ GL.RGB_INTEGER, GL.UNSIGNED_BYTE ], + [GL.RGB8I]: [ GL.RGB_INTEGER, GL.BYTE ], + [GL.RGB16UI]: [ GL.RGB_INTEGER, GL.UNSIGNED_SHORT ], + [GL.RGB16I]: [ GL.RGB_INTEGER, GL.SHORT ], + [GL.RGB32UI]: [ GL.RGB_INTEGER, GL.UNSIGNED_INT ], + [GL.RGB32I]: [ GL.RGB_INTEGER, GL.INT ], + [GL.RGBA8]: [ GL.RGBA, GL.UNSIGNED_BYTE ], + [GL.SRGB8_ALPHA8]: [ GL.RGBA, GL.UNSIGNED_BYTE ], + [GL.RGBA8_SNORM]: [ GL.RGBA, GL.BYTE ], + [GL.RGB5_A1]: [ GL.RGBA, GL.UNSIGNED_SHORT_5_5_5_1 ], + [GL.RGBA4]: [ GL.RGBA, GL.UNSIGNED_SHORT_4_4_4_4 ], + [GL.RGB10_A2]: [ GL.RGBA, GL.UNSIGNED_INT_2_10_10_10_REV ], + [GL.RGBA16F]: [ GL.RGBA, GL.FLOAT ], + [GL.RGBA32F]: [ GL.RGBA, GL.FLOAT ], + [GL.RGBA8UI]: [ GL.RGBA_INTEGER, GL.UNSIGNED_BYTE ], + [GL.RGBA8I]: [ GL.RGBA_INTEGER, GL.BYTE ], + [GL.RGB10_A2UI]: [ GL.RGBA_INTEGER, GL.UNSIGNED_INT_2_10_10_10_REV ], + [GL.RGBA16UI]: [ GL.RGBA_INTEGER, GL.UNSIGNED_SHORT ], + [GL.RGBA16I]: [ GL.RGBA_INTEGER, GL.SHORT ], + [GL.RGBA32I]: [ GL.RGBA_INTEGER, GL.INT ], + [GL.RGBA32UI]: [ GL.RGBA_INTEGER, GL.UNSIGNED_INT ], + [GL.DEPTH_COMPONENT16]: [ GL.DEPTH_COMPONENT, GL.UNSIGNED_SHORT ], + [GL.DEPTH_COMPONENT24]: [ GL.DEPTH_COMPONENT, GL.UNSIGNED_INT ], + [GL.DEPTH_COMPONENT32F]: [ GL.DEPTH_COMPONENT, GL.FLOAT ], + [GL.DEPTH24_STENCIL8]: [ GL.DEPTH_STENCIL, GL.UNSIGNED_INT_24_8 ], + [GL.DEPTH32F_STENCIL8]: [ GL.DEPTH_STENCIL, GL.FLOAT_32_UNSIGNED_INT_24_8_REV ] +}; + +export const COMPRESSED_TEXTURE_TYPES = { + [GL.COMPRESSED_RGB_S3TC_DXT1_EXT]: true, + [GL.COMPRESSED_RGBA_S3TC_DXT1_EXT]: true, + [GL.COMPRESSED_RGBA_S3TC_DXT3_EXT]: true, + [GL.COMPRESSED_RGBA_S3TC_DXT5_EXT]: true, + [GL.COMPRESSED_SRGB_S3TC_DXT1_EXT]: true, + [GL.COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT]: true, + [GL.COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT]: true, + [GL.COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT]: true, + [GL.COMPRESSED_R11_EAC]: true, + [GL.COMPRESSED_SIGNED_R11_EAC]: true, + [GL.COMPRESSED_RG11_EAC]: true, + [GL.COMPRESSED_SIGNED_RG11_EAC]: true, + [GL.COMPRESSED_RGB8_ETC2]: true, + [GL.COMPRESSED_SRGB8_ETC2]: true, + [GL.COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2]: true, + [GL.COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2]: true, + [GL.COMPRESSED_RGBA8_ETC2_EAC]: true, + [GL.COMPRESSED_SRGB8_ALPHA8_ETC2_EAC]: true, + [GL.COMPRESSED_RGBA_ASTC_4x4_KHR]: true, + [GL.COMPRESSED_RGBA_ASTC_5x4_KHR]: true, + [GL.COMPRESSED_RGBA_ASTC_5x5_KHR]: true, + [GL.COMPRESSED_RGBA_ASTC_6x5_KHR]: true, + [GL.COMPRESSED_RGBA_ASTC_6x6_KHR]: true, + [GL.COMPRESSED_RGBA_ASTC_8x5_KHR]: true, + [GL.COMPRESSED_RGBA_ASTC_8x6_KHR]: true, + [GL.COMPRESSED_RGBA_ASTC_8x8_KHR]: true, + [GL.COMPRESSED_RGBA_ASTC_10x5_KHR]: true, + [GL.COMPRESSED_RGBA_ASTC_10x6_KHR]: true, + [GL.COMPRESSED_RGBA_ASTC_10x8_KHR]: true, + [GL.COMPRESSED_RGBA_ASTC_10x10_KHR]: true, + [GL.COMPRESSED_RGBA_ASTC_12x10_KHR]: true, + [GL.COMPRESSED_RGBA_ASTC_12x12_KHR]: true, + [GL.COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR]: true, + [GL.COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR]: true, + [GL.COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR]: true, + [GL.COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR]: true, + [GL.COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR]: true, + [GL.COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR]: true, + [GL.COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR]: true, + [GL.COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR]: true, + [GL.COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR]: true, + [GL.COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR]: true, + [GL.COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR]: true, + [GL.COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR]: true, + [GL.COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR]: true, + [GL.COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR]: true, + [GL.COMPRESSED_RGB_PVRTC_4BPPV1_IMG]: true, + [GL.COMPRESSED_RGB_PVRTC_2BPPV1_IMG]: true, + [GL.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG]: true, + [GL.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG]: true +}; + +export const WEBGL_INFO = {}; + +export const DUMMY_UNIT_ARRAY = new Array(1); + +export const DUMMY_OBJECT = {}; + +// DEPRECATED +export const TEXTURE_FORMAT_DEFAULTS = { + [GL.UNSIGNED_BYTE]: { + [GL.RED]: GL.R8, + [GL.RG]: GL.RG8, + [GL.RGB]: GL.RGB8, + [GL.RGBA]: GL.RGBA8 + }, + + [GL.UNSIGNED_SHORT]: { + [GL.DEPTH_COMPONENT]: GL.DEPTH_COMPONENT16 + }, + + [GL.FLOAT]: { + [GL.RED]: GL.R16F, + [GL.RG]: GL.RG16F, + [GL.RGB]: GL.RGB16F, + [GL.RGBA]: GL.RGBA16F, + [GL.DEPTH_COMPONENT]: GL.DEPTH_COMPONENT32F + } +}; diff --git a/build/module/cubemap.js b/build/module/cubemap.js new file mode 100644 index 00000000..741386b1 --- /dev/null +++ b/build/module/cubemap.js @@ -0,0 +1,279 @@ +/////////////////////////////////////////////////////////////////////////////////// +// The MIT License (MIT) +// +// Copyright (c) 2017 Tarek Sherif +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +/////////////////////////////////////////////////////////////////////////////////// + +import { + GL, + WEBGL_INFO, + TEXTURE_FORMATS, + COMPRESSED_TEXTURE_TYPES, + DUMMY_OBJECT, + DUMMY_UNIT_ARRAY, + // DEPRECATED + TEXTURE_FORMAT_DEFAULTS +} from "./constants.js"; + +/** + Cubemap for environment mapping. + + @class + @prop {WebGLRenderingContext} gl The WebGL context. + @prop {WebGLTexture} texture Handle to the texture. + @prop {GLEnum} type Type of data stored in the texture. + @prop {GLEnum} format Layout of texture data. + @prop {GLEnum} internalFormat Internal arrangement of the texture data. + @prop {Number} currentUnit The current texture unit this cubemap is bound to. + @prop {boolean} flipY Whether the y-axis is flipped for this cubemap. + @prop {boolean} premultiplyAlpha Whether alpha should be pre-multiplied when loading this cubemap. + @prop {Object} appState Tracked GL state. +*/ +export class Cubemap { + + constructor(gl, appState, options) { + + this.gl = gl; + this.texture = null; + this.appState = appState; + + this.compressed = COMPRESSED_TEXTURE_TYPES[options.internalFormat]; + + if (options.format !== undefined) { + console.warn("Cubemap option 'format' is deprecated and will be removed. Use 'internalFormat' with a sized format instead."); + this.compressed = Boolean(COMPRESSED_TEXTURE_TYPES[options.format]); + if (options.type === undefined) { + options.type = options.format === GL.DEPTH_COMPONENT ? GL.UNSIGNED_SHORT : GL.UNSIGNED_BYTE; + } + if (options.internalFormat === undefined) { + if (this.compressed) { + options.internalFormat = options.format; + } else { + options.internalFormat = TEXTURE_FORMAT_DEFAULTS[options.type][options.format]; + } + } + } + + if (this.compressed) { + // For compressed textures, just need to provide one of format, internalFormat. + // The other will be the same. + this.internalFormat = options.internalFormat; + this.format = options.internalFormat; + this.type = GL.UNSIGNED_BYTE; + } else { + this.internalFormat = options.internalFormat !== undefined ? options.internalFormat : GL.RGBA8; + + let formatInfo = TEXTURE_FORMATS[this.internalFormat]; + this.format = formatInfo[0]; + this.type = options.type !== undefined ? options.type : formatInfo[1]; + } + + // -1 indicates unbound + this.currentUnit = -1; + + let arrayData = Array.isArray(options.negX); + let negX = arrayData ? options.negX[0] : options.negX; + + let { + width = negX.width, + height = negX.height, + flipY = false, + premultiplyAlpha = false, + minFilter = negX ? GL.LINEAR_MIPMAP_NEAREST : GL.NEAREST, + magFilter = negX ? GL.LINEAR : GL.NEAREST, + wrapS = GL.REPEAT, + wrapT = GL.REPEAT, + compareMode = GL.NONE, + compareFunc = GL.LEQUAL, + minLOD = null, + maxLOD = null, + baseLevel = null, + maxLevel = null, + maxAnisotropy = 1 + } = options; + + this.width = width; + this.height = height; + this.flipY = flipY; + this.premultiplyAlpha = premultiplyAlpha; + this.minFilter = minFilter; + this.magFilter = magFilter; + this.wrapS = wrapS; + this.wrapT = wrapT; + this.compareMode = compareMode; + this.compareFunc = compareFunc; + this.minLOD = minLOD; + this.maxLOD = maxLOD; + this.baseLevel = baseLevel; + this.maxLevel = maxLevel; + this.maxAnisotropy = Math.min(maxAnisotropy, WEBGL_INFO.MAX_TEXTURE_ANISOTROPY); + this.mipmaps = (minFilter === GL.LINEAR_MIPMAP_NEAREST || minFilter === GL.LINEAR_MIPMAP_LINEAR); + this.miplevelsProvided = arrayData && options.negX.length > 1; + this.levels = this.mipmaps ? Math.floor(Math.log2(Math.min(this.width, this.height))) + 1 : 1; + + this.restore(options); + } + + /** + Restore cubemap after context loss. + + @method + @param {Object} [options] Texture options. + @param {DOMElement|ArrayBufferView} [options.negX] The image data for the negative X direction. + Can be any format that would be accepted by texImage2D. + @param {DOMElement|ArrayBufferView} [options.posX] The image data for the positive X direction. + Can be any format that would be accepted by texImage2D. + @param {DOMElement|ArrayBufferView} [options.negY] The image data for the negative Y direction. + Can be any format that would be accepted by texImage2D. + @param {DOMElement|ArrayBufferView} [options.posY] The image data for the positive Y direction. + Can be any format that would be accepted by texImage2D. + @param {DOMElement|ArrayBufferView} [options.negZ] The image data for the negative Z direction. + Can be any format that would be accepted by texImage2D. + @param {DOMElement|ArrayBufferView} [options.posZ] The image data for the positive Z direction. + Can be any format that would be accepted by texImage2D. + @return {Cubemap} The Cubemap object. + */ + restore(options = DUMMY_OBJECT) { + this.texture = this.gl.createTexture(); + + if (this.currentUnit !== -1) { + this.appState.textures[this.currentUnit] = null; + } + + this.bind(0); + this.gl.pixelStorei(GL.UNPACK_FLIP_Y_WEBGL, this.flipY); + this.gl.pixelStorei(GL.UNPACK_PREMULTIPLY_ALPHA_WEBGL, this.premultiplyAlpha); + this.gl.texParameteri(GL.TEXTURE_CUBE_MAP, GL.TEXTURE_MAG_FILTER, this.magFilter); + this.gl.texParameteri(GL.TEXTURE_CUBE_MAP, GL.TEXTURE_MIN_FILTER, this.minFilter); + this.gl.texParameteri(GL.TEXTURE_CUBE_MAP, GL.TEXTURE_WRAP_S, this.wrapS); + this.gl.texParameteri(GL.TEXTURE_CUBE_MAP, GL.TEXTURE_WRAP_T, this.wrapT); + this.gl.texParameteri(GL.TEXTURE_CUBE_MAP, GL.TEXTURE_COMPARE_FUNC, this.compareFunc); + this.gl.texParameteri(GL.TEXTURE_CUBE_MAP, GL.TEXTURE_COMPARE_MODE, this.compareMode); + + if (this.baseLevel !== null) { + this.gl.texParameteri(GL.TEXTURE_CUBE_MAP, GL.TEXTURE_BASE_LEVEL, this.baseLevel); + } + + if (this.maxLevel !== null) { + this.gl.texParameteri(GL.TEXTURE_CUBE_MAP, GL.TEXTURE_MAX_LEVEL, this.maxLevel); + } + + if (this.minLOD !== null) { + this.gl.texParameteri(GL.TEXTURE_CUBE_MAP, GL.TEXTURE_MIN_LOD, this.minLOD); + } + + if (this.maxLOD !== null) { + this.gl.texParameteri(GL.TEXTURE_CUBE_MAP, GL.TEXTURE_MAX_LOD, this.maxLOD); + } + + if (this.maxAnisotropy > 1) { + this.gl.texParameteri(GL.TEXTURE_CUBE_MAP, GL.TEXTURE_MAX_ANISOTROPY_EXT, this.maxAnisotropy); + } + + this.gl.texStorage2D(GL.TEXTURE_CUBE_MAP, this.levels, this.internalFormat, this.width, this.height); + + let { negX, posX, negY, posY, negZ, posZ } = options; + + if (negX) { + this.faceData(GL.TEXTURE_CUBE_MAP_NEGATIVE_X, negX); + this.faceData(GL.TEXTURE_CUBE_MAP_POSITIVE_X, posX); + this.faceData(GL.TEXTURE_CUBE_MAP_NEGATIVE_Y, negY); + this.faceData(GL.TEXTURE_CUBE_MAP_POSITIVE_Y, posY); + this.faceData(GL.TEXTURE_CUBE_MAP_NEGATIVE_Z, negZ); + this.faceData(GL.TEXTURE_CUBE_MAP_POSITIVE_Z, posZ); + } + + if (this.mipmaps && !this.miplevelsProvided) { + this.gl.generateMipmap(GL.TEXTURE_CUBE_MAP); + } + + return this; + } + + /** + Delete this cubemap. + + @method + @return {Cubemap} The Cubemap object. + */ + delete() { + if (this.texture) { + this.gl.deleteTexture(this.texture); + this.texture = null; + this.appState.textures[this.currentUnit] = null; + this.currentUnit = -1; + } + + return this; + } + + // Input data for one cubemap face. + faceData(face, data) { + if (!Array.isArray(data)) { + DUMMY_UNIT_ARRAY[0] = data; + data = DUMMY_UNIT_ARRAY; + } + + let numLevels = this.mipmaps ? data.length : 1; + let width = this.width; + let height = this.height; + let i; + + if (this.compressed) { + for (i = 0; i < numLevels; ++i) { + this.gl.compressedTexSubImage2D(face, i, 0, 0, width, height, this.format, data[i]); + width = Math.max(width >> 1, 1); + height = Math.max(height >> 1, 1); + } + } else { + for (i = 0; i < numLevels; ++i) { + this.gl.texSubImage2D(face, i, 0, 0, width, height, this.format, this.type, data[i]); + width = Math.max(width >> 1, 1); + height = Math.max(height >> 1, 1); + } + } + + return this; + } + + // Bind this cubemap to a texture unit. + bind(unit) { + let currentTexture = this.appState.textures[unit]; + + if (currentTexture !== this) { + if (currentTexture) { + currentTexture.currentUnit = -1; + } + + if (this.currentUnit !== -1) { + this.appState.textures[this.currentUnit] = null; + } + + this.gl.activeTexture(GL.TEXTURE0 + unit); + this.gl.bindTexture(GL.TEXTURE_CUBE_MAP, this.texture); + + this.appState.textures[unit] = this; + this.currentUnit = unit; + } + + return this; + } + +} diff --git a/build/module/draw-call.js b/build/module/draw-call.js new file mode 100644 index 00000000..d6d7d083 --- /dev/null +++ b/build/module/draw-call.js @@ -0,0 +1,269 @@ +/////////////////////////////////////////////////////////////////////////////////// +// The MIT License (MIT) +// +// Copyright (c) 2017 Tarek Sherif +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +/////////////////////////////////////////////////////////////////////////////////// + +import { GL, WEBGL_INFO } from "./constants.js"; + +/** + A DrawCall represents the program and values of associated + attributes, uniforms and textures for a single draw call. + + @class + @prop {WebGLRenderingContext} gl The WebGL context. + @prop {Program} currentProgram The program to use for this draw call. + @prop {VertexArray} currentVertexArray Vertex array to use for this draw call. + @prop {TransformFeedback} currentTransformFeedback Transform feedback to use for this draw call. + @prop {Array} uniformBuffers Ordered list of active uniform buffers. + @prop {Array} uniformBlockNames Ordered list of uniform block names. + @prop {Number} uniformBlockCount Number of active uniform blocks for this draw call. + @prop {Object} uniformIndices Map of uniform names to indices in the uniform arrays. + @prop {Array} uniformNames Ordered list of uniform names. + @prop {Array} uniformValue Ordered list of uniform values. + @prop {number} uniformCount The number of active uniforms for this draw call. + @prop {Array} textures Array of active textures. + @prop {number} textureCount The number of active textures for this draw call. + @prop {GLEnum} primitive The primitive type being drawn. + @prop {Object} appState Tracked GL state. + @prop {GLsizei} numElements The number of element to draw. + @prop {GLsizei} numInstances The number of instances to draw. +*/ +export class DrawCall { + + constructor(gl, appState, program, vertexArray = null, primitive) { + this.gl = gl; + this.currentProgram = program; + this.drawPrimitive = GL.TRIANGLES; + this.currentVertexArray = vertexArray; + this.currentTransformFeedback = null; + this.appState = appState; + + this.uniformIndices = {}; + this.uniformNames = new Array(WEBGL_INFO.MAX_UNIFORMS); + this.uniformValues = new Array(WEBGL_INFO.MAX_UNIFORMS); + this.uniformCount = 0; + this.uniformBuffers = new Array(WEBGL_INFO.MAX_UNIFORM_BUFFERS); + this.uniformBlockNames = new Array(WEBGL_INFO.MAX_UNIFORM_BUFFERS); + this.uniformBlockCount = 0; + this.textures = new Array(WEBGL_INFO.MAX_TEXTURE_UNITS); + this.textureCount = 0; + + this.offsets = new Int32Array(1); + this.numElements = new Int32Array(1); + this.numInstances = new Int32Array(1); + + if (this.currentVertexArray) { + this.numElements[0] = this.currentVertexArray.numElements; + this.numInstances[0] = this.currentVertexArray.numInstances; + } + + this.numDraws = 1; + + if (primitive !== undefined) { + console.warn("Primitive argument to 'App.createDrawCall' is deprecated and will be removed. Use 'DrawCall.primitive' instead."); + this.primitive(primitive); + } + } + + /** + Set the current draw primitive for this draw call. + + @method + @param {GLEnum} primitive Primitive to draw. + @return {DrawCall} The DrawCall object. + */ + primitive(primitive) { + this.drawPrimitive = primitive; + + return this; + } + + /** + Set the current TransformFeedback object for draw. + + @method + @param {TransformFeedback} transformFeedback Transform Feedback to set. + @return {DrawCall} The DrawCall object. + */ + transformFeedback(transformFeedback) { + this.currentTransformFeedback = transformFeedback; + + return this; + } + + /** + Set the value for a uniform. Array uniforms are supported by + using appending "[0]" to the array name and passing a flat array + with all required values. + + @method + @param {string} name Uniform name. + @param {any} value Uniform value. + @return {DrawCall} The DrawCall object. + */ + uniform(name, value) { + let index = this.uniformIndices[name]; + if (index === undefined) { + index = this.uniformCount++; + this.uniformIndices[name] = index; + this.uniformNames[index] = name; + } + this.uniformValues[index] = value; + + return this; + } + + /** + Set texture to bind to a sampler uniform. + + @method + @param {string} name Sampler uniform name. + @param {Texture|Cubemap} texture Texture or Cubemap to bind. + @return {DrawCall} The DrawCall object. + */ + texture(name, texture) { + let unit = this.currentProgram.samplers[name]; + this.textures[unit] = texture; + + return this; + } + + /** + Set uniform buffer to bind to a uniform block. + + @method + @param {string} name Uniform block name. + @param {UniformBuffer} buffer Uniform buffer to bind. + @return {DrawCall} The DrawCall object. + */ + uniformBlock(name, buffer) { + let base = this.currentProgram.uniformBlocks[name]; + this.uniformBuffers[base] = buffer; + + return this; + } + + /** + Ranges in the vertex array to draw. Multiple arguments can be provided to set up + a multi-draw. + + @method + @param {...Array} counts Variable number of 2 or 3 element arrays, each containing: + + @return {DrawCall} The DrawCall object. + */ + drawRanges(...counts) { + this.numDraws = counts.length; + + if (this.offsets.length < this.numDraws) { + this.offsets = new Int32Array(this.numDraws); + } + + if (this.numElements.length < this.numDraws) { + this.numElements = new Int32Array(this.numDraws); + } + + if (this.numInstances.length < this.numDraws) { + this.numInstances = new Int32Array(this.numDraws); + } + + for (let i = 0; i < this.numDraws; ++i) { + let count = counts[i]; + + this.offsets[i] = count[0]; + this.numElements[i] = count[1]; + this.numInstances[i] = count[2] || 1; + } + + return this; + } + + /** + Draw based on current state. + + @method + @return {DrawCall} The DrawCall object. + */ + draw() { + let uniformNames = this.uniformNames; + let uniformValues = this.uniformValues; + let uniformBuffers = this.uniformBuffers; + let uniformBlockCount = this.currentProgram.uniformBlockCount; + let textures = this.textures; + let textureCount = this.currentProgram.samplerCount; + let indexed = false; + + this.currentProgram.bind(); + + if (this.currentVertexArray) { + this.currentVertexArray.bind(); + indexed = this.currentVertexArray.indexed; + } + + for (let uIndex = 0; uIndex < this.uniformCount; ++uIndex) { + this.currentProgram.uniform(uniformNames[uIndex], uniformValues[uIndex]); + } + + for (let base = 0; base < uniformBlockCount; ++base) { + uniformBuffers[base].bind(base); + } + + for (let tIndex = 0; tIndex < textureCount; ++tIndex) { + textures[tIndex].bind(tIndex); + } + + if (this.currentTransformFeedback) { + this.currentTransformFeedback.bind(); + this.gl.beginTransformFeedback(this.drawPrimitive); + } else if (this.appState.transformFeedback) { + this.gl.bindTransformFeedback(GL.TRANSFORM_FEEDBACK, null); + this.appState.transformFeedback = null; + } + + if (WEBGL_INFO.MULTI_DRAW_INSTANCED) { + let ext = this.appState.extensions.multiDrawInstanced; + if (indexed) { + ext.multiDrawElementsInstancedWEBGL(this.drawPrimitive, this.numElements, 0, this.currentVertexArray.indexType, this.offsets, 0, this.numInstances, 0, this.numDraws); + } else { + ext.multiDrawArraysInstancedWEBGL(this.drawPrimitive, this.offsets, 0, this.numElements, 0, this.numInstances, 0, this.numDraws); + } + } else if (indexed) { + for (let i = 0; i < this.numDraws; ++i) { + this.gl.drawElementsInstanced(this.drawPrimitive, this.numElements[i], this.currentVertexArray.indexType, this.offsets[i], this.numInstances[i]); + } + } else { + for (let i = 0; i < this.numDraws; ++i) { + this.gl.drawArraysInstanced(this.drawPrimitive, this.offsets[i], this.numElements[i], this.numInstances[i]); + } + } + + if (this.currentTransformFeedback) { + this.gl.endTransformFeedback(); + } + + return this; + } + +} diff --git a/build/module/framebuffer.js b/build/module/framebuffer.js new file mode 100644 index 00000000..d6d6c2ac --- /dev/null +++ b/build/module/framebuffer.js @@ -0,0 +1,330 @@ +/////////////////////////////////////////////////////////////////////////////////// +// The MIT License (MIT) +// +// Copyright (c) 2017 Tarek Sherif +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +/////////////////////////////////////////////////////////////////////////////////// + +import { GL } from "./constants.js"; +import { Texture } from "./texture.js"; +import { Renderbuffer } from "./renderbuffer.js"; + +/** + Offscreen drawing surface. + + @class + @prop {WebGLRenderingContext} gl The WebGL context. + @prop {WebGLFramebuffer} framebuffer Handle to the framebuffer. + @prop {number} width Framebuffer width. + @prop {number} height Framebuffer height. + @prop {Array} colorAttachments Array of color attachments. + @prop {Texture|Renderbuffer} depthAttachment Depth attachment. + @prop {Object} appState Tracked GL state. +*/ +export class Framebuffer { + + constructor(gl, appState) { + this.gl = gl; + this.framebuffer = null; + this.appState = appState; + + this.numColorTargets = 0; + + this.colorAttachments = []; + this.colorAttachmentEnums = []; + this.colorAttachmentTargets = []; + this.depthAttachment = null; + this.depthAttachmentTarget = null; + + this.width = 0; + this.height = 0; + + this.restore(); + } + + /** + Restore framebuffer after context loss. + + @method + @return {Framebuffer} The Framebuffer object. + */ + restore() { + if (this.appState.drawFramebuffer === this) { + this.appState.drawFramebuffer = null; + } + + if (this.appState.readFramebuffer === this) { + this.appState.readFramebuffer = null; + } + + this.framebuffer = this.gl.createFramebuffer(); + + return this; + } + + /** + Attach a color target to this framebuffer. + + @method + @param {number} index Color attachment index. + @param {Texture|Cubemap|Renderbuffer} attachment The texture, cubemap or renderbuffer to attach. + @param {GLEnum} [target] The texture target or layer to attach. If the texture is 3D or a texture array, + defaults to 0, otherwise to TEXTURE_2D. Ignored for renderbuffers. + @return {Framebuffer} The Framebuffer object. + */ + colorTarget(index, attachment, target = attachment.is3D ? 0 : GL.TEXTURE_2D) { + + if (index >= this.numColorTargets) { + let numColorTargets = index + 1; + this.colorAttachmentEnums.length = numColorTargets; + this.colorAttachments.length = numColorTargets; + this.colorAttachmentTargets.length = numColorTargets; + + for (let i = this.numColorTargets; i < numColorTargets - 1; ++i) { + this.colorAttachmentEnums[i] = GL.NONE; + this.colorAttachments[i] = null; + this.colorAttachmentTargets[i] = 0; + } + + this.numColorTargets = numColorTargets; + } + + this.colorAttachmentEnums[index] = GL.COLOR_ATTACHMENT0 + index; + this.colorAttachments[index] = attachment; + this.colorAttachmentTargets[index] = target; + + let currentFramebuffer = this.bindAndCaptureState(); + + + if (attachment instanceof Renderbuffer) { + this.gl.framebufferRenderbuffer(GL.DRAW_FRAMEBUFFER, this.colorAttachmentEnums[index], GL.RENDERBUFFER, attachment.renderbuffer); + } else if (attachment.is3D) { + this.gl.framebufferTextureLayer(GL.DRAW_FRAMEBUFFER, this.colorAttachmentEnums[index], attachment.texture, 0, target); + } else { + this.gl.framebufferTexture2D(GL.DRAW_FRAMEBUFFER, this.colorAttachmentEnums[index], target, attachment.texture, 0); + } + + this.gl.drawBuffers(this.colorAttachmentEnums); + + this.width = attachment.width; + this.height = attachment.height; + + this.restoreState(currentFramebuffer); + + return this; + } + + /** + Attach a depth target to this framebuffer. + + @method + @param {Texture|Cubemap|Renderbuffer} texture The texture, cubemap or renderbuffer to attach. + @param {GLEnum} [target] The texture target or layer to attach. If the texture is 3D or a texture array or renderbuffer, + defaults to 0, otherwise to TEXTURE_2D. Ignored for renderbuffers. + @return {Framebuffer} The Framebuffer object. + */ + depthTarget(attachment, target = attachment.is3D ? 0 : GL.TEXTURE_2D) { + + let currentFramebuffer = this.bindAndCaptureState(); + + this.depthAttachment = attachment; + this.depthAttachmentTarget = target; + + if (attachment instanceof Renderbuffer) { + this.gl.framebufferRenderbuffer(GL.DRAW_FRAMEBUFFER, GL.DEPTH_ATTACHMENT, GL.RENDERBUFFER, attachment.renderbuffer); + } else if (attachment.is3D) { + this.gl.framebufferTextureLayer(GL.DRAW_FRAMEBUFFER, GL.DEPTH_ATTACHMENT, attachment.texture, 0, target); + } else { + this.gl.framebufferTexture2D(GL.DRAW_FRAMEBUFFER, GL.DEPTH_ATTACHMENT, target, attachment.texture, 0); + } + + this.width = attachment.width; + this.height = attachment.height; + + this.restoreState(currentFramebuffer); + + return this; + } + + /** + Resize all attachments. + + @method + @param {number} [width=app.width] New width of the framebuffer. + @param {number} [height=app.height] New height of the framebuffer. + @return {Framebuffer} The Framebuffer object. + */ + resize(width = this.gl.drawingBufferWidth, height = this.gl.drawingBufferHeight) { + + let currentFramebuffer = this.bindAndCaptureState(); + + for (let i = 0; i < this.numColorTargets; ++i) { + let attachment = this.colorAttachments[i]; + + if (!attachment) { + continue; + } + + attachment.resize(width, height); + if (attachment instanceof Texture) { + // Texture resizing recreates the texture object. + if (attachment.is3D) { + this.gl.framebufferTextureLayer(GL.DRAW_FRAMEBUFFER, this.colorAttachmentEnums[i], attachment.texture, 0, this.colorAttachmentTargets[i]); + } else { + this.gl.framebufferTexture2D(GL.DRAW_FRAMEBUFFER, this.colorAttachmentEnums[i], this.colorAttachmentTargets[i], attachment.texture, 0); + } + } + } + + if (this.depthAttachment) { + this.depthAttachment.resize(width, height); + if (this.depthAttachment instanceof Texture) { + // Texture resizing recreates the texture object. + if (this.depthAttachment.is3D) { + this.gl.framebufferTextureLayer(GL.DRAW_FRAMEBUFFER, GL.DEPTH_ATTACHMENT, this.depthAttachment.texture, 0, this.depthAttachmentTarget); + } else { + this.gl.framebufferTexture2D(GL.DRAW_FRAMEBUFFER, GL.DEPTH_ATTACHMENT, this.depthAttachmentTarget, this.depthAttachment.texture, 0); + } + } + } + + this.width = width; + this.height = height; + + this.restoreState(currentFramebuffer); + + return this; + } + + /** + Delete this framebuffer. + + @method + @return {Framebuffer} The Framebuffer object. + */ + delete() { + if (this.framebuffer) { + this.gl.deleteFramebuffer(this.framebuffer); + this.framebuffer = null; + + if (this.appState.drawFramebuffer === this) { + this.gl.bindFramebuffer(GL.DRAW_FRAMEBUFFER, null); + this.appState.drawFramebuffer = null; + } + + if (this.appState.readFramebuffer === this) { + this.gl.bindFramebuffer(GL.READ_FRAMEBUFFER, null); + this.appState.readFramebuffer = null; + } + } + + return this; + } + + /** + Get the current status of this framebuffer. + + @method + @return {GLEnum} The current status of this framebuffer. + */ + getStatus() { + let currentFramebuffer = this.bindAndCaptureState(); + let status = this.gl.checkFramebufferStatus(GL.DRAW_FRAMEBUFFER); + this.restoreState(currentFramebuffer); + + return status; + } + + /** + Bind as the draw framebuffer + + @method + @ignore + @return {Framebuffer} The Framebuffer object. + */ + bindForDraw() { + if (this.appState.drawFramebuffer !== this) { + this.gl.bindFramebuffer(GL.DRAW_FRAMEBUFFER, this.framebuffer); + this.appState.drawFramebuffer = this; + } + + return this; + } + + /** + Bind as the read framebuffer + + @method + @ignore + @return {Framebuffer} The Framebuffer object. + */ + bindForRead() { + if (this.appState.readFramebuffer !== this) { + this.gl.bindFramebuffer(this.gl.READ_FRAMEBUFFER, this.framebuffer); + this.appState.readFramebuffer = this; + } + + return this; + } + + /** + Bind for a framebuffer state update. + Capture current binding so we can restore it later. + + @method + @ignore + @return {Framebuffer} The Framebuffer object. + */ + bindAndCaptureState() { + let currentFramebuffer = this.appState.drawFramebuffer; + + if (currentFramebuffer !== this) { + this.gl.bindFramebuffer(GL.DRAW_FRAMEBUFFER, this.framebuffer); + } + + return currentFramebuffer; + } + + /** + Bind restore previous binding after state update + + @method + @ignore + @return {Framebuffer} The Framebuffer object. + */ + restoreState(framebuffer) { + if (framebuffer !== this) { + this.gl.bindFramebuffer(GL.DRAW_FRAMEBUFFER, framebuffer ? framebuffer.framebuffer : null); + } + + return this; + } + + // TODO(Tarek): Transitional support for deprecated properties. + get colorTextures() { + console.error("Framebuffer.colorTextures is deprecated and will be removed. Please use Framebuffer.colorAttachments."); + return this.colorAttachments; + } + + get depthTexture() { + console.error("Framebuffer.depthTexture is deprecated and will be removed. Please use Framebuffer.depthAttachment."); + return this.depthAttachment; + } + +} diff --git a/build/module/picogl.js b/build/module/picogl.js new file mode 100644 index 00000000..75730a67 --- /dev/null +++ b/build/module/picogl.js @@ -0,0 +1,91 @@ +/////////////////////////////////////////////////////////////////////////////////// +// The MIT License (MIT) +// +// Copyright (c) 2017 Tarek Sherif +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +/////////////////////////////////////////////////////////////////////////////////// + +import { GL, WEBGL_INFO } from "./constants.js"; +import { App } from "./app.js"; + +let webglInfoInitialized = false; + +/** + Global PicoGL module. For convenience, all WebGL enums are stored + as properties of PicoGL (e.g. PicoGL.FLOAT, PicoGL.ONE_MINUS_SRC_ALPHA). + + @namespace PicoGL +*/ +export const PicoGL = Object.assign({ + version: "0.14.1", + + WEBGL_INFO, + + /** + Create a PicoGL app. The app is the primary entry point to PicoGL. It stores + the canvas, the WebGL context and all WebGL state. + + @function PicoGL.createApp + @param {DOMElement} canvas The canvas on which to create the WebGL context. + @param {Object} [contextAttributes] Context attributes to pass when calling getContext(). + @return {App} New App object. + */ + createApp(canvas, contextAttributes) { + let gl = canvas.getContext("webgl2", contextAttributes); + if (!webglInfoInitialized) { + WEBGL_INFO.MAX_TEXTURE_UNITS = gl.getParameter(GL.MAX_COMBINED_TEXTURE_IMAGE_UNITS); + WEBGL_INFO.MAX_UNIFORM_BUFFERS = gl.getParameter(GL.MAX_UNIFORM_BUFFER_BINDINGS); + WEBGL_INFO.MAX_UNIFORMS = Math.min( + gl.getParameter(GL.MAX_VERTEX_UNIFORM_VECTORS), + gl.getParameter(GL.MAX_FRAGMENT_UNIFORM_VECTORS) + ); + WEBGL_INFO.SAMPLES = gl.getParameter(GL.SAMPLES); + WEBGL_INFO.VENDOR = "(Unknown)"; + WEBGL_INFO.RENDERER = "(Unknown)"; + + // Extensions + WEBGL_INFO.FLOAT_RENDER_TARGETS = Boolean(gl.getExtension("EXT_color_buffer_float")); + WEBGL_INFO.LINEAR_FLOAT_TEXTURES = Boolean(gl.getExtension("OES_texture_float_linear")); + WEBGL_INFO.S3TC_TEXTURES = Boolean(gl.getExtension("WEBGL_compressed_texture_s3tc")); + WEBGL_INFO.S3TC_SRGB_TEXTURES = Boolean(gl.getExtension("WEBGL_compressed_texture_s3tc_srgb")); + WEBGL_INFO.ETC_TEXTURES = Boolean(gl.getExtension("WEBGL_compressed_texture_etc")); + WEBGL_INFO.ASTC_TEXTURES = Boolean(gl.getExtension("WEBGL_compressed_texture_astc")); + WEBGL_INFO.PVRTC_TEXTURES = Boolean(gl.getExtension("WEBGL_compressed_texture_pvrtc")); + WEBGL_INFO.LOSE_CONTEXT = Boolean(gl.getExtension("WEBGL_lose_context")); + WEBGL_INFO.DEBUG_SHADERS = Boolean(gl.getExtension("WEBGL_debug_shaders")); + WEBGL_INFO.GPU_TIMER = Boolean(gl.getExtension("EXT_disjoint_timer_query_webgl2") || gl.getExtension("EXT_disjoint_timer_query")); + + WEBGL_INFO.TEXTURE_ANISOTROPY = Boolean(gl.getExtension("EXT_texture_filter_anisotropic")); + WEBGL_INFO.MAX_TEXTURE_ANISOTROPY = WEBGL_INFO.TEXTURE_ANISOTROPY ? gl.getParameter(GL.MAX_TEXTURE_MAX_ANISOTROPY_EXT) : 1; + + WEBGL_INFO.DEBUG_RENDERER_INFO = Boolean(gl.getExtension("WEBGL_debug_renderer_info")); + if (WEBGL_INFO.DEBUG_RENDERER_INFO) { + WEBGL_INFO.VENDOR = gl.getParameter(GL.UNMASKED_VENDOR_WEBGL); + WEBGL_INFO.RENDERER = gl.getParameter(GL.UNMASKED_RENDERER_WEBGL); + } + + // Draft extensions + WEBGL_INFO.PARALLEL_SHADER_COMPILE = Boolean(gl.getExtension("KHR_parallel_shader_compile")); + WEBGL_INFO.MULTI_DRAW_INSTANCED = Boolean(gl.getExtension("WEBGL_multi_draw_instanced")); + + webglInfoInitialized = true; + } + return new App(gl, canvas); + } +}, GL); diff --git a/build/module/program.js b/build/module/program.js new file mode 100644 index 00000000..9ea400bc --- /dev/null +++ b/build/module/program.js @@ -0,0 +1,335 @@ +/////////////////////////////////////////////////////////////////////////////////// +// The MIT License (MIT) +// +// Copyright (c) 2017 Tarek Sherif +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +/////////////////////////////////////////////////////////////////////////////////// + +import { GL, WEBGL_INFO } from "./constants.js"; +import { Shader } from "./shader.js"; +import { + SingleComponentUniform, + MultiNumericUniform, + MultiBoolUniform, + MatrixUniform +} from "./uniforms.js"; + +/** + WebGL program consisting of compiled and linked vertex and fragment + shaders. + + @class + @prop {WebGLRenderingContext} gl The WebGL context. + @prop {WebGLProgram} program The WebGL program. + @prop {boolean} transformFeedback Whether this program is set up for transform feedback. + @prop {Object} uniforms Map of uniform names to handles. + @prop {Object} appState Tracked GL state. +*/ +export class Program { + + constructor(gl, appState, vsSource, fsSource, xformFeebackVars) { + this.gl = gl; + this.appState = appState; + this.program = null; + this.transformFeedbackVaryings = xformFeebackVars || null; + this.uniforms = {}; + this.uniformBlocks = {}; + this.uniformBlockCount = 0; + this.samplers = {}; + this.samplerCount = 0; + + this.vertexSource = null; + this.vertexShader = null; + this.fragmentSource = null; + this.fragmentShader = null; + this.linked = false; + + if (typeof vsSource === "string") { + this.vertexSource = vsSource; + } else { + this.vertexShader = vsSource; + } + + if (typeof fsSource === "string") { + this.fragmentSource = fsSource; + } else { + this.fragmentShader = fsSource; + } + + this.initialize(); + } + + /** + Restore program after context loss. Note that this + will stall for completion. App.restorePrograms + is the preferred method for program restoration as + it will parallelize compilation where available. + + @method + @return {Program} The Program object. + */ + restore() { + this.initialize(); + this.link(); + this.checkLinkage(); + + return this; + } + + /** + Get the vertex shader source translated for the platform's API. + + @method + @return {String} The translated vertex shader source. + */ + translatedVertexSource() { + if (this.vertexShader) { + return this.vertexShader.translatedSource(); + } else { + let vertexShader = new Shader(this.gl, this.appState, GL.VERTEX_SHADER, this.vertexSource); + let translatedSource = vertexShader.translatedSource(); + vertexShader.delete(); + return translatedSource; + } + } + + /** + Get the fragment shader source translated for the platform's API. + + @method + @return {String} The translated fragment shader source. + */ + translatedFragmentSource() { + if (this.fragmentShader) { + return this.fragmentShader.translatedSource(); + } else { + let fragmentShader = new Shader(this.gl, this.appState, GL.FRAGMENT_SHADER, this.fragmentSource); + let translatedSource = fragmentShader.translatedSource(); + fragmentShader.delete(); + return translatedSource; + } + } + + /** + Delete this program. + + @method + @return {Program} The Program object. + */ + delete() { + if (this.program) { + this.gl.deleteProgram(this.program); + this.program = null; + + if (this.appState.program === this) { + this.gl.useProgram(null); + this.appState.program = null; + } + } + + return this; + } + + // Initialize program state + initialize() { + if (this.appState.program === this) { + this.gl.useProgram(null); + this.appState.program = null; + } + + this.linked = false; + this.uniformBlockCount = 0; + this.samplerCount = 0; + + if (this.vertexSource) { + this.vertexShader = new Shader(this.gl, this.appState, GL.VERTEX_SHADER, this.vertexSource); + } + + if (this.fragmentSource) { + this.fragmentShader = new Shader(this.gl, this.appState, GL.FRAGMENT_SHADER, this.fragmentSource); + } + + this.program = this.gl.createProgram(); + + return this; + } + + // Attach shaders and link program. + // Done as a separate step to avoid stalls on compileShader + // when doing async compile. + link() { + this.gl.attachShader(this.program, this.vertexShader.shader); + this.gl.attachShader(this.program, this.fragmentShader.shader); + if (this.transformFeedbackVaryings) { + this.gl.transformFeedbackVaryings(this.program, this.transformFeedbackVaryings, GL.SEPARATE_ATTRIBS); + } + this.gl.linkProgram(this.program); + + return this; + } + + // Check if compilation is complete + checkCompletion() { + if (WEBGL_INFO.PARALLEL_SHADER_COMPILE) { + return this.gl.getProgramParameter(this.program, GL.COMPLETION_STATUS_KHR); + } + + return true; + } + + // Check if program linked. + // Will stall for completion. + checkLinkage() { + if (this.linked) { + return this; + } + + if (this.gl.getProgramParameter(this.program, GL.LINK_STATUS)) { + this.linked = true; + this.initVariables(); + } else { + console.error(this.gl.getProgramInfoLog(this.program)); + this.vertexShader.checkCompilation(); + this.fragmentShader.checkCompilation(); + } + + if (this.vertexSource) { + this.vertexShader.delete(); + this.vertexShader = null; + } + + if (this.fragmentSource) { + this.fragmentShader.delete(); + this.fragmentShader = null; + } + + return this; + } + + // Get variable handles from program + initVariables() { + this.bind(); + + let numUniforms = this.gl.getProgramParameter(this.program, GL.ACTIVE_UNIFORMS); + let textureUnit; + + for (let i = 0; i < numUniforms; ++i) { + let uniformInfo = this.gl.getActiveUniform(this.program, i); + let uniformHandle = this.gl.getUniformLocation(this.program, uniformInfo.name); + let UniformClass = null; + let type = uniformInfo.type; + let numElements = uniformInfo.size; + + switch (type) { + case GL.SAMPLER_2D: + case GL.INT_SAMPLER_2D: + case GL.UNSIGNED_INT_SAMPLER_2D: + case GL.SAMPLER_2D_SHADOW: + case GL.SAMPLER_2D_ARRAY: + case GL.INT_SAMPLER_2D_ARRAY: + case GL.UNSIGNED_INT_SAMPLER_2D_ARRAY: + case GL.SAMPLER_2D_ARRAY_SHADOW: + case GL.SAMPLER_CUBE: + case GL.INT_SAMPLER_CUBE: + case GL.UNSIGNED_INT_SAMPLER_CUBE: + case GL.SAMPLER_CUBE_SHADOW: + case GL.SAMPLER_3D: + case GL.INT_SAMPLER_3D: + case GL.UNSIGNED_INT_SAMPLER_3D: + textureUnit = this.samplerCount++; + this.samplers[uniformInfo.name] = textureUnit; + this.gl.uniform1i(uniformHandle, textureUnit); + break; + case GL.INT: + case GL.UNSIGNED_INT: + case GL.FLOAT: + UniformClass = numElements > 1 ? MultiNumericUniform : SingleComponentUniform; + break; + case GL.BOOL: + UniformClass = numElements > 1 ? MultiBoolUniform : SingleComponentUniform; + break; + case GL.FLOAT_VEC2: + case GL.INT_VEC2: + case GL.UNSIGNED_INT_VEC2: + case GL.FLOAT_VEC3: + case GL.INT_VEC3: + case GL.UNSIGNED_INT_VEC3: + case GL.FLOAT_VEC4: + case GL.INT_VEC4: + case GL.UNSIGNED_INT_VEC4: + UniformClass = MultiNumericUniform; + break; + case GL.BOOL_VEC2: + case GL.BOOL_VEC3: + case GL.BOOL_VEC4: + UniformClass = MultiBoolUniform; + break; + case GL.FLOAT_MAT2: + case GL.FLOAT_MAT3: + case GL.FLOAT_MAT4: + case GL.FLOAT_MAT2x3: + case GL.FLOAT_MAT2x4: + case GL.FLOAT_MAT3x2: + case GL.FLOAT_MAT3x4: + case GL.FLOAT_MAT4x2: + case GL.FLOAT_MAT4x3: + UniformClass = MatrixUniform; + break; + default: + console.error("Unrecognized type for uniform ", uniformInfo.name); + break; + } + + if (UniformClass) { + this.uniforms[uniformInfo.name] = new UniformClass(this.gl, uniformHandle, type, numElements); + } + } + + let numUniformBlocks = this.gl.getProgramParameter(this.program, GL.ACTIVE_UNIFORM_BLOCKS); + + for (let i = 0; i < numUniformBlocks; ++i) { + let blockName = this.gl.getActiveUniformBlockName(this.program, i); + let blockIndex = this.gl.getUniformBlockIndex(this.program, blockName); + + let uniformBlockBase = this.uniformBlockCount++; + this.gl.uniformBlockBinding(this.program, blockIndex, uniformBlockBase); + this.uniformBlocks[blockName] = uniformBlockBase; + } + } + + // Set the value of a uniform. + uniform(name, value) { + // some uniforms are optimized out + if (this.uniforms[name]) { + this.uniforms[name].set(value); + } + + return this; + } + + // Use this program. + bind() { + if (this.appState.program !== this) { + this.gl.useProgram(this.program); + this.appState.program = this; + } + + return this; + } +} diff --git a/build/module/query.js b/build/module/query.js new file mode 100644 index 00000000..7eff76ed --- /dev/null +++ b/build/module/query.js @@ -0,0 +1,125 @@ +/////////////////////////////////////////////////////////////////////////////////// +// The MIT License (MIT) +// +// Copyright (c) 2017 Tarek Sherif +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +/////////////////////////////////////////////////////////////////////////////////// + +import { GL } from "./constants.js"; + +/** + Generic query object. + + @class + @prop {WebGLRenderingContext} gl The WebGL context. + @prop {WebGLQuery} query Query object. + @prop {GLEnum} target The type of information being queried. + @prop {boolean} active Whether or not a query is currently in progress. + @prop {Any} result The result of the query (only available after a call to ready() returns true). +*/ +export class Query { + + constructor(gl, target) { + this.gl = gl; + this.query = null; + this.target = target; + this.active = false; + this.result = null; + + this.restore(); + } + + /** + Restore query after context loss. + + @method + @return {Query} The Query object. + */ + restore() { + this.query = this.gl.createQuery(); + this.active = false; + this.result = null; + + return this; + } + + /** + Begin a query. + + @method + @return {Query} The Query object. + */ + begin() { + if (!this.active) { + this.gl.beginQuery(this.target, this.query); + this.result = null; + } + + return this; + } + + /** + End a query. + + @method + @return {Query} The Query object. + */ + end() { + if (!this.active) { + this.gl.endQuery(this.target); + this.active = true; + } + + return this; + } + + /** + Check if query result is available. + + @method + @return {boolean} If results are available. + */ + ready() { + if (this.active && this.gl.getQueryParameter(this.query, GL.QUERY_RESULT_AVAILABLE)) { + this.active = false; + // Note(Tarek): Casting because FF incorrectly returns booleans. + // https://bugzilla.mozilla.org/show_bug.cgi?id=1422714 + this.result = Number(this.gl.getQueryParameter(this.query, GL.QUERY_RESULT)); + return true; + } + + return false; + } + + /** + Delete this query. + + @method + @return {Query} The Query object. + */ + delete() { + if (this.query) { + this.gl.deleteQuery(this.query); + this.query = null; + } + + return this; + } + +} diff --git a/build/module/renderbuffer.js b/build/module/renderbuffer.js new file mode 100644 index 00000000..1922ebd1 --- /dev/null +++ b/build/module/renderbuffer.js @@ -0,0 +1,91 @@ +/////////////////////////////////////////////////////////////////////////////////// +// The MIT License (MIT) +// +// Copyright (c) 2017 Tarek Sherif +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +/////////////////////////////////////////////////////////////////////////////////// + +import { GL } from "./constants.js"; + +/** + Offscreen drawing attachment. + + @class + @prop {WebGLRenderingContext} gl The WebGL context. + @prop {WebGLRenderbuffer} renderbuffer Handle to the renderbuffer. + @prop {number} width Renderbuffer width. + @prop {number} height Renderbuffer height. + @prop {GLEnum} internalFormat Internal arrangement of the renderbuffer data. + @prop {number} samples Number of MSAA samples. +*/ +export class Renderbuffer { + constructor(gl, width, height, internalFormat, samples = 0) { + this.gl = gl; + this.renderbuffer = null; + this.width = width; + this.height = height; + this.internalFormat = internalFormat; + this.samples = samples; + this.restore(); + } + + /** + Restore renderbuffer after context loss. + + @method + @return {Renderbuffer} The Renderbuffer object. + */ + restore() { + this.renderbuffer = this.gl.createRenderbuffer(); + this.resize(this.width, this.height); + + return this; + } + + /** + Resize the renderbuffer. + + @method + @param {number} width New width of the renderbuffer. + @param {number} height New height of the renderbuffer. + @return {Renderbuffer} The Renderbuffer object. + */ + resize(width, height) { + this.width = width; + this.height = height; + this.gl.bindRenderbuffer(GL.RENDERBUFFER, this.renderbuffer); + this.gl.renderbufferStorageMultisample(GL.RENDERBUFFER, this.samples, this.internalFormat, this.width, this.height); + this.gl.bindRenderbuffer(GL.RENDERBUFFER, null); + + return this; + } + + /** + Delete this renderbuffer. + + @method + @return {Renderbuffer} The Renderbuffer object. + */ + delete() { + this.gl.deleteRenderbuffer(this.renderbuffer); + this.renderbuffer = null; + + return this; + } +} diff --git a/build/module/shader.js b/build/module/shader.js new file mode 100644 index 00000000..bd367656 --- /dev/null +++ b/build/module/shader.js @@ -0,0 +1,102 @@ +/////////////////////////////////////////////////////////////////////////////////// +// The MIT License (MIT) +// +// Copyright (c) 2017 Tarek Sherif +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +/////////////////////////////////////////////////////////////////////////////////// + +import { GL, WEBGL_INFO } from "./constants.js"; + +/** + WebGL shader. + + @class + @prop {WebGLRenderingContext} gl The WebGL context. + @prop {WebGLShader} shader The shader. +*/ +export class Shader { + + constructor(gl, appState, type, source) { + this.gl = gl; + this.appState = appState; + this.shader = null; + this.type = type; + this.source = source; + + this.restore(); + } + + /** + Restore shader after context loss. + + @method + @return {Shader} The Shader object. + */ + restore() { + this.shader = this.gl.createShader(this.type); + this.gl.shaderSource(this.shader, this.source); + this.gl.compileShader(this.shader); + + return this; + } + + /** + Get the shader source translated for the platform's API. + + @method + @return {String} The translated shader source. + */ + translatedSource() { + if (WEBGL_INFO.DEBUG_SHADERS) { + return this.appState.extensions.debugShaders.getTranslatedShaderSource(this.shader); + } else { + return "(Unavailable)"; + } + } + + /** + Delete this shader. + + @method + @return {Shader} The Shader object. + */ + delete() { + if (this.shader) { + this.gl.deleteShader(this.shader); + this.shader = null; + } + + return this; + } + + + checkCompilation() { + if (!this.gl.getShaderParameter(this.shader, GL.COMPILE_STATUS)) { + let i, lines; + + console.error(this.gl.getShaderInfoLog(this.shader)); + lines = this.source.split("\n"); + for (i = 0; i < lines.length; ++i) { + console.error(`${i + 1}: ${lines[i]}`); + } + } + + return this; + } +} diff --git a/build/module/texture.js b/build/module/texture.js new file mode 100644 index 00000000..5b171df8 --- /dev/null +++ b/build/module/texture.js @@ -0,0 +1,349 @@ +/////////////////////////////////////////////////////////////////////////////////// +// The MIT License (MIT) +// +// Copyright (c) 2017 Tarek Sherif +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +/////////////////////////////////////////////////////////////////////////////////// + +import { + GL, + WEBGL_INFO, + TEXTURE_FORMATS, + COMPRESSED_TEXTURE_TYPES, + DUMMY_OBJECT, + DUMMY_UNIT_ARRAY, + // DEPRECATED + TEXTURE_FORMAT_DEFAULTS +} from "./constants.js"; + +/** + General-purpose texture. + + @class + @prop {WebGLRenderingContext} gl The WebGL context. + @prop {WebGLTexture} texture Handle to the texture. + @prop {number} width Texture width. + @prop {number} height Texture height. + @prop {number} depth Texture depth. + @prop {GLEnum} binding Binding point for the texture. + @prop {GLEnum} type Type of data stored in the texture. + @prop {GLEnum} format Layout of texture data. + @prop {GLEnum} internalFormat Internal arrangement of the texture data. + @prop {number} currentUnit The current texture unit this texture is bound to. + @prop {boolean} is3D Whether this texture contains 3D data. + @prop {boolean} flipY Whether the y-axis is flipped for this texture. + @prop {boolean} premultiplyAlpha Whether alpha should be pre-multiplied when loading this texture. + @prop {boolean} mipmaps Whether this texture is using mipmap filtering + (and thus should have a complete mipmap chain). + @prop {Object} appState Tracked GL state. +*/ +export class Texture { + constructor(gl, appState, binding, image, width = image.width, height = image.height, depth, is3D, options = DUMMY_OBJECT) { + + this.gl = gl; + this.binding = binding; + this.texture = null; + this.width = width || 0; + this.height = height || 0; + this.depth = depth || 0; + this.is3D = is3D; + this.appState = appState; + + this.compressed = Boolean(COMPRESSED_TEXTURE_TYPES[options.internalFormat]); + + if (options.format !== undefined) { + console.warn("Texture option 'format' is deprecated and will be removed. Use 'internalFormat' with a sized format instead."); + this.compressed = Boolean(COMPRESSED_TEXTURE_TYPES[options.format]); + if (options.type === undefined) { + options.type = options.format === GL.DEPTH_COMPONENT ? GL.UNSIGNED_SHORT : GL.UNSIGNED_BYTE; + } + if (options.internalFormat === undefined) { + if (this.compressed) { + options.internalFormat = options.format; + } else { + options.internalFormat = TEXTURE_FORMAT_DEFAULTS[options.type][options.format]; + } + } + } + + if (this.compressed) { + // For compressed textures, just need to provide one of format, internalFormat. + // The other will be the same. + this.internalFormat = options.internalFormat; + this.format = this.internalFormat; + this.type = GL.UNSIGNED_BYTE; + } else { + this.internalFormat = options.internalFormat !== undefined ? options.internalFormat : GL.RGBA8; + + let formatInfo = TEXTURE_FORMATS[this.internalFormat]; + this.format = formatInfo[0]; + this.type = options.type !== undefined ? options.type : formatInfo[1]; + } + + // -1 indicates unbound + this.currentUnit = -1; + + // Sampling parameters + let { + minFilter = image ? GL.LINEAR_MIPMAP_NEAREST : GL.NEAREST, + magFilter = image ? GL.LINEAR : GL.NEAREST, + wrapS = GL.REPEAT, + wrapT = GL.REPEAT, + wrapR = GL.REPEAT, + compareMode = GL.NONE, + compareFunc = GL.LEQUAL, + minLOD = null, + maxLOD = null, + baseLevel = null, + maxLevel = null, + maxAnisotropy = 1, + flipY = false, + premultiplyAlpha = false + } = options; + + this.minFilter = minFilter; + this.magFilter = magFilter; + this.wrapS = wrapS; + this.wrapT = wrapT; + this.wrapR = wrapR; + this.compareMode = compareMode; + this.compareFunc = compareFunc; + this.minLOD = minLOD; + this.maxLOD = maxLOD; + this.baseLevel = baseLevel; + this.maxLevel = maxLevel; + this.maxAnisotropy = Math.min(maxAnisotropy, WEBGL_INFO.MAX_TEXTURE_ANISOTROPY); + this.flipY = flipY; + this.premultiplyAlpha = premultiplyAlpha; + this.mipmaps = (minFilter === GL.LINEAR_MIPMAP_NEAREST || minFilter === GL.LINEAR_MIPMAP_LINEAR); + + this.restore(image); + } + + /** + Restore texture after context loss. + + @method + @param {DOMElement|ArrayBufferView|Array} [image] Image data. An array can be passed to manually set all levels + of the mipmap chain. If a single level is passed and mipmap filtering is being used, + generateMipmap() will be called to produce the remaining levels. + @return {Texture} The Texture object. + */ + restore(image) { + this.texture = null; + this.resize(this.width, this.height, this.depth); + + if (image) { + this.data(image); + } + + return this; + } + + /** + Re-allocate texture storage. + + @method + @param {number} width Image width. + @param {number} height Image height. + @param {number} [depth] Image depth or number of images. Required when passing 3D or texture array data. + @return {Texture} The Texture object. + */ + resize(width, height, depth) { + depth = depth || 0; + + if (this.texture && width === this.width && height === this.height && depth === this.depth) { + return this; + } + + this.gl.deleteTexture(this.texture); + if (this.currentUnit !== -1) { + this.appState.textures[this.currentUnit] = null; + } + + this.texture = this.gl.createTexture(); + this.bind(Math.max(this.currentUnit, 0)); + + this.width = width; + this.height = height; + this.depth = depth; + + this.gl.texParameteri(this.binding, GL.TEXTURE_MIN_FILTER, this.minFilter); + this.gl.texParameteri(this.binding, GL.TEXTURE_MAG_FILTER, this.magFilter); + this.gl.texParameteri(this.binding, GL.TEXTURE_WRAP_S, this.wrapS); + this.gl.texParameteri(this.binding, GL.TEXTURE_WRAP_T, this.wrapT); + this.gl.texParameteri(this.binding, GL.TEXTURE_WRAP_R, this.wrapR); + this.gl.texParameteri(this.binding, GL.TEXTURE_COMPARE_FUNC, this.compareFunc); + this.gl.texParameteri(this.binding, GL.TEXTURE_COMPARE_MODE, this.compareMode); + this.gl.pixelStorei(GL.UNPACK_FLIP_Y_WEBGL, this.flipY); + this.gl.pixelStorei(GL.UNPACK_PREMULTIPLY_ALPHA_WEBGL, this.premultiplyAlpha); + + if (this.minLOD !== null) { + this.gl.texParameterf(this.binding, GL.TEXTURE_MIN_LOD, this.minLOD); + } + + if (this.maxLOD !== null) { + this.gl.texParameterf(this.binding, GL.TEXTURE_MAX_LOD, this.maxLOD); + } + + if (this.baseLevel !== null) { + this.gl.texParameteri(this.binding, GL.TEXTURE_BASE_LEVEL, this.baseLevel); + } + + if (this.maxLevel !== null) { + this.gl.texParameteri(this.binding, GL.TEXTURE_MAX_LEVEL, this.maxLevel); + } + + if (this.maxAnisotropy > 1) { + this.gl.texParameteri(this.binding, GL.TEXTURE_MAX_ANISOTROPY_EXT, this.maxAnisotropy); + } + + let levels; + if (this.is3D) { + if (this.mipmaps) { + levels = Math.floor(Math.log2(Math.max(Math.max(this.width, this.height), this.depth))) + 1; + } else { + levels = 1; + } + this.gl.texStorage3D(this.binding, levels, this.internalFormat, this.width, this.height, this.depth); + } else { + if (this.mipmaps) { + levels = Math.floor(Math.log2(Math.max(this.width, this.height))) + 1; + } else { + levels = 1; + } + this.gl.texStorage2D(this.binding, levels, this.internalFormat, this.width, this.height); + } + + return this; + } + + /** + Set the image data for the texture. An array can be passed to manually set all levels + of the mipmap chain. If a single level is passed and mipmap filtering is being used, + generateMipmap() will be called to produce the remaining levels. + NOTE: the data must fit the currently-allocated storage! + + @method + @param {ImageElement|ArrayBufferView|Array} data Image data. If an array is passed, it will be + used to set mip map levels. + @return {Texture} The Texture object. + */ + data(data) { + if (!Array.isArray(data)) { + DUMMY_UNIT_ARRAY[0] = data; + data = DUMMY_UNIT_ARRAY; + } + + let numLevels = this.mipmaps ? data.length : 1; + let width = this.width; + let height = this.height; + let depth = this.depth; + let generateMipmaps = this.mipmaps && data.length === 1; + let i; + + this.bind(Math.max(this.currentUnit, 0)); + + if (this.compressed) { + if (this.is3D) { + for (i = 0; i < numLevels; ++i) { + this.gl.compressedTexSubImage3D(this.binding, i, 0, 0, 0, width, height, depth, this.format, data[i]); + width = Math.max(width >> 1, 1); + height = Math.max(height >> 1, 1); + depth = Math.max(depth >> 1, 1); + } + } else { + for (i = 0; i < numLevels; ++i) { + this.gl.compressedTexSubImage2D(this.binding, i, 0, 0, width, height, this.format, data[i]); + width = Math.max(width >> 1, 1); + height = Math.max(height >> 1, 1); + } + } + } else if (this.is3D) { + for (i = 0; i < numLevels; ++i) { + this.gl.texSubImage3D(this.binding, i, 0, 0, 0, width, height, depth, this.format, this.type, data[i]); + width = Math.max(width >> 1, 1); + height = Math.max(height >> 1, 1); + depth = Math.max(depth >> 1, 1); + } + } else { + for (i = 0; i < numLevels; ++i) { + this.gl.texSubImage2D(this.binding, i, 0, 0, width, height, this.format, this.type, data[i]); + width = Math.max(width >> 1, 1); + height = Math.max(height >> 1, 1); + } + } + + if (generateMipmaps) { + this.gl.generateMipmap(this.binding); + } + + return this; + } + + /** + Delete this texture. + + @method + @return {Texture} The Texture object. + */ + delete() { + if (this.texture) { + this.gl.deleteTexture(this.texture); + this.texture = null; + + if (this.currentUnit !== -1 && this.appState.textures[this.currentUnit] === this) { + this.appState.textures[this.currentUnit] = null; + this.currentUnit = -1; + } + } + + return this; + } + + /** + Bind this texture to a texture unit. + + @method + @ignore + @return {Texture} The Texture object. + */ + bind(unit) { + let currentTexture = this.appState.textures[unit]; + + if (currentTexture !== this) { + if (currentTexture) { + currentTexture.currentUnit = -1; + } + + if (this.currentUnit !== -1) { + this.appState.textures[this.currentUnit] = null; + } + + this.gl.activeTexture(GL.TEXTURE0 + unit); + this.gl.bindTexture(this.binding, this.texture); + + this.appState.textures[unit] = this; + this.currentUnit = unit; + } + + return this; + } + +} diff --git a/build/module/timer.js b/build/module/timer.js new file mode 100644 index 00000000..e660b27d --- /dev/null +++ b/build/module/timer.js @@ -0,0 +1,161 @@ +/////////////////////////////////////////////////////////////////////////////////// +// The MIT License (MIT) +// +// Copyright (c) 2017 Tarek Sherif +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +/////////////////////////////////////////////////////////////////////////////////// + +import { GL, WEBGL_INFO } from "./constants.js"; +import { Query } from "./query.js"; + +/** + Rendering timer. + + @class + @prop {WebGLRenderingContext} gl The WebGL context. + @prop {Object} cpuTimer Timer for CPU. Will be window.performance, if available, or window.Date. + @prop {WebGLQuery} gpuTimerQuery Timer query object for GPU (if gpu timing is supported). + @prop {boolean} gpuTimerQueryInProgress Whether a gpu timer query is currently in progress. + @prop {number} cpuStartTime When the last CPU timing started. + @prop {number} cpuTime Time spent on CPU during last timing. Only valid if ready() returns true. + @prop {number} gpuTime Time spent on GPU during last timing. Only valid if ready() returns true. + Will remain 0 if extension EXT_disjoint_timer_query_webgl2 is unavailable. +*/ +export class Timer { + + constructor(gl) { + this.gl = gl; + this.cpuTimer = window.performance || window.Date; + + this.gpuTimerQuery = null; + + this.cpuStartTime = 0; + this.cpuTime = 0; + this.gpuTime = 0; + + this.restore(); + } + + /** + Restore timer after context loss. + + @method + @return {Timer} The Timer object. + */ + restore() { + if (WEBGL_INFO.GPU_TIMER) { + if (this.gpuTimerQuery) { + this.gpuTimerQuery.restore(); + } else { + this.gpuTimerQuery = new Query(this.gl, GL.TIME_ELAPSED_EXT); + } + } + + this.cpuStartTime = 0; + this.cpuTime = 0; + this.gpuTime = 0; + + return this; + } + + + /** + Start timing. + + @method + @return {Timer} The Timer object. + */ + start() { + if (WEBGL_INFO.GPU_TIMER) { + if (!this.gpuTimerQuery.active) { + this.gpuTimerQuery.begin(); + this.cpuStartTime = this.cpuTimer.now(); + } + } else { + this.cpuStartTime = this.cpuTimer.now(); + } + + return this; + } + + + /** + Stop timing. + + @method + @return {Timer} The Timer object. + */ + end() { + if (WEBGL_INFO.GPU_TIMER) { + if (!this.gpuTimerQuery.active) { + this.gpuTimerQuery.end(); + this.cpuTime = this.cpuTimer.now() - this.cpuStartTime; + } + } else { + this.cpuTime = this.cpuTimer.now() - this.cpuStartTime; + } + + return this; + } + + /** + Check if timing results are available. If + this method returns true, the cpuTime and + gpuTime properties will be set to valid + values. + + @method + @return {boolean} If results are available. + */ + ready() { + if (WEBGL_INFO.GPU_TIMER) { + if (!this.gpuTimerQuery.active) { + return false; + } + + var gpuTimerAvailable = this.gpuTimerQuery.ready(); + var gpuTimerDisjoint = this.gl.getParameter(GL.GPU_DISJOINT_EXT); + + if (gpuTimerAvailable && !gpuTimerDisjoint) { + this.gpuTime = this.gpuTimerQuery.result / 1000000; + return true; + } else { + return false; + } + } else { + return Boolean(this.cpuStartTime); + } + } + + /** + Delete this timer. + + @method + @return {Timer} The Timer object. + */ + delete() { + if (this.gpuTimerQuery) { + this.gpuTimerQuery.delete(); + this.gpuTimerQuery = null; + } + + return this; + } + +} diff --git a/build/module/transform-feedback.js b/build/module/transform-feedback.js new file mode 100644 index 00000000..ed9d9dd3 --- /dev/null +++ b/build/module/transform-feedback.js @@ -0,0 +1,117 @@ +/////////////////////////////////////////////////////////////////////////////////// +// The MIT License (MIT) +// +// Copyright (c) 2017 Tarek Sherif +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +/////////////////////////////////////////////////////////////////////////////////// + +import { GL } from "./constants.js"; + +/** + Tranform feedback object. + + @class + @prop {WebGLRenderingContext} gl The WebGL context. + @prop {WebGLTransformFeedback} transformFeedback Transform feedback object. + @prop {Object} appState Tracked GL state. +*/ +export class TransformFeedback { + + constructor(gl, appState) { + this.gl = gl; + this.appState = appState; + this.transformFeedback = null; + + this.restore(); + } + + /** + Restore transform feedback after context loss. + + @method + @return {TransformFeedback} The TransformFeedback object. + */ + restore() { + if (this.appState.transformFeedback === this) { + this.appState.transformFeedback = null; + } + + this.transformFeedback = this.gl.createTransformFeedback(); + + return this; + } + + /** + Bind a feedback buffer to capture transform output. + + @method + @param {number} index Index of transform feedback varying to capture. + @param {VertexBuffer} buffer Buffer to record output into. + @return {TransformFeedback} The TransformFeedback object. + */ + feedbackBuffer(index, buffer) { + this.gl.bindTransformFeedback(GL.TRANSFORM_FEEDBACK, this.transformFeedback); + this.gl.bindBufferBase(GL.TRANSFORM_FEEDBACK_BUFFER, index, buffer.buffer); + + // TODO(Tarek): Firefox doesn't properly unbind TRANSFORM_FEEDBACK_BUFFER + // bindings when TRANSFORM_FEEDBACK is unbound. + // https://bugzilla.mozilla.org/show_bug.cgi?id=1541396 + this.gl.bindTransformFeedback(GL.TRANSFORM_FEEDBACK, null); + this.gl.bindBufferBase(GL.TRANSFORM_FEEDBACK_BUFFER, index, null); + + return this; + } + + /** + Delete this transform feedback. + + @method + @return {TransformFeedback} The TransformFeedback object. + */ + delete() { + if (this.transformFeedback) { + this.gl.deleteTransformFeedback(this.transformFeedback); + this.transformFeedback = null; + + if (this.appState.transformFeedback === this) { + this.gl.bindTransformFeedback(GL.TRANSFORM_FEEDBACK, null); + this.appState.transformFeedback = null; + } + } + + return this; + } + + /** + Bind this transform feedback. + + @method + @ignore + @return {TransformFeedback} The TransformFeedback object. + */ + bind() { + if (this.appState.transformFeedback !== this) { + this.gl.bindTransformFeedback(GL.TRANSFORM_FEEDBACK, this.transformFeedback); + this.appState.transformFeedback = this; + } + + return this; + } + +} diff --git a/build/module/uniform-buffer.js b/build/module/uniform-buffer.js new file mode 100644 index 00000000..d8428b87 --- /dev/null +++ b/build/module/uniform-buffer.js @@ -0,0 +1,286 @@ +/////////////////////////////////////////////////////////////////////////////////// +// The MIT License (MIT) +// +// Copyright (c) 2017 Tarek Sherif +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +/////////////////////////////////////////////////////////////////////////////////// + +import { GL } from "./constants.js"; + +/** + Storage for uniform data. Data is stored in std140 layout. + + @class + @prop {WebGLRenderingContext} gl The WebGL context. + @prop {WebGLBuffer} buffer Allocated buffer storage. + @prop {Float32Array} data Buffer data. + @prop {Object} dataViews Map of base data types to matching ArrayBufferViews of the buffer data. + @prop {Array} offsets Offsets into the array for each item in the buffer. + @prop {Array} sizes Size of the item at the given offset. + @prop {Array} types The base type of the item at the given offset (FLOAT, INT or UNSIGNED_INT). + @prop {number} size The size of the buffer (in 4-byte items). + @prop {GLEnum} usage Usage pattern of the buffer. +*/ +export class UniformBuffer { + + constructor(gl, appState, layout, usage = gl.DYNAMIC_DRAW) { + this.gl = gl; + this.buffer = null; + this.dataViews = {}; + this.offsets = new Array(layout.length); + this.sizes = new Array(layout.length); + this.types = new Array(layout.length); + this.size = 0; + this.usage = usage; + this.appState = appState; + + // -1 indicates unbound + this.currentBase = -1; + + for (let i = 0, len = layout.length; i < len; ++i) { + let type = layout[i]; + switch(type) { + case GL.FLOAT: + case GL.INT: + case GL.UNSIGNED_INT: + case GL.BOOL: + this.offsets[i] = this.size; + this.sizes[i] = 1; + + if (type === GL.INT) { + this.types[i] = GL.INT; + } else if (type === GL.UNSIGNED_INT) { + this.types[i] = GL.UNSIGNED_INT; + } else { + this.types[i] = GL.FLOAT; + } + + this.size++; + break; + case GL.FLOAT_VEC2: + case GL.INT_VEC2: + case GL.UNSIGNED_INT_VEC2: + case GL.BOOL_VEC2: + this.size += this.size % 2; + this.offsets[i] = this.size; + this.sizes[i] = 2; + + if (type === GL.INT_VEC2) { + this.types[i] = GL.INT; + } else if (type === GL.UNSIGNED_INT_VEC2) { + this.types[i] = GL.UNSIGNED_INT; + } else { + this.types[i] = GL.FLOAT; + } + + this.size += 2; + break; + case GL.FLOAT_VEC3: + case GL.INT_VEC3: + case GL.UNSIGNED_INT_VEC3: + case GL.BOOL_VEC3: + case GL.FLOAT_VEC4: + case GL.INT_VEC4: + case GL.UNSIGNED_INT_VEC4: + case GL.BOOL_VEC4: + this.size += (4 - this.size % 4) % 4; + this.offsets[i] = this.size; + this.sizes[i] = 4; + + if (type === GL.INT_VEC4 || type === GL.INT_VEC3) { + this.types[i] = GL.INT; + } else if (type === GL.UNSIGNED_INT_VEC4 || type === GL.UNSIGNED_INT_VEC3) { + this.types[i] = GL.UNSIGNED_INT; + } else { + this.types[i] = GL.FLOAT; + } + + this.size += 4; + break; + case GL.FLOAT_MAT2: + case GL.FLOAT_MAT2x3: + case GL.FLOAT_MAT2x4: + this.size += (4 - this.size % 4) % 4; + this.offsets[i] = this.size; + this.sizes[i] = 8; + this.types[i] = GL.FLOAT; + + this.size += 8; + break; + case GL.FLOAT_MAT3: + case GL.FLOAT_MAT3x2: + case GL.FLOAT_MAT3x4: + this.size += (4 - this.size % 4) % 4; + this.offsets[i] = this.size; + this.sizes[i] = 12; + this.types[i] = GL.FLOAT; + + this.size += 12; + break; + case GL.FLOAT_MAT4: + case GL.FLOAT_MAT4x2: + case GL.FLOAT_MAT4x3: + this.size += (4 - this.size % 4) % 4; + this.offsets[i] = this.size; + this.sizes[i] = 16; + this.types[i] = GL.FLOAT; + + this.size += 16; + break; + default: + console.error("Unsupported type for uniform buffer."); + } + } + + this.size += (4 - this.size % 4) % 4; + + this.data = new Float32Array(this.size); + this.dataViews[GL.FLOAT] = this.data; + this.dataViews[GL.INT] = new Int32Array(this.data.buffer); + this.dataViews[GL.UNSIGNED_INT] = new Uint32Array(this.data.buffer); + + this.dirtyStart = this.size; + this.dirtyEnd = 0; + + this.restore(); + } + + /** + Restore uniform buffer after context loss. + + @method + @return {UniformBuffer} The UniformBuffer object. + */ + restore() { + if (this.currentBase !== -1 && this.appState.uniformBuffers[this.currentBase] === this) { + this.appState.uniformBuffers[this.currentBase] = null; + } + + this.buffer = this.gl.createBuffer(); + this.gl.bindBuffer(GL.UNIFORM_BUFFER, this.buffer); + this.gl.bufferData(GL.UNIFORM_BUFFER, this.size * 4, this.usage); + this.gl.bindBuffer(GL.UNIFORM_BUFFER, null); + + return this; + } + + /** + Update data for a given item in the buffer. NOTE: Data is not + sent the the GPU until the update() method is called! + + @method + @param {number} index Index in the layout of item to set. + @param {ArrayBufferView} value Value to store at the layout location. + @return {UniformBuffer} The UniformBuffer object. + */ + set(index, value) { + let view = this.dataViews[this.types[index]]; + let offset = this.offsets[index]; + let size = this.sizes[index]; + + if (this.sizes[index] === 1) { + view[offset] = value; + } else { + view.set(value, offset); + } + + if (offset < this.dirtyStart) { + this.dirtyStart = offset; + } + + if (this.dirtyEnd < offset + size) { + this.dirtyEnd = offset + size; + } + + return this; + } + + /** + Send stored buffer data to the GPU. + + @method + @return {UniformBuffer} The UniformBuffer object. + */ + update() { + if (this.dirtyStart >= this.dirtyEnd) { + return this; + } + + let data = this.data.subarray(this.dirtyStart, this.dirtyEnd); + let offset = this.dirtyStart * 4; + + this.gl.bindBuffer(this.gl.UNIFORM_BUFFER, this.buffer); + this.gl.bufferSubData(this.gl.UNIFORM_BUFFER, offset, data); + this.gl.bindBuffer(this.gl.UNIFORM_BUFFER, null); + + this.dirtyStart = this.size; + this.dirtyEnd = 0; + + return this; + } + + /** + Delete this uniform buffer. + + @method + @return {UniformBuffer} The UniformBuffer object. + */ + delete() { + if (this.buffer) { + this.gl.deleteBuffer(this.buffer); + this.buffer = null; + + if (this.currentBase !== -1 && this.appState.uniformBuffers[this.currentBase] === this) { + this.appState.uniformBuffers[this.currentBase] = null; + } + } + + return this; + } + + /** + Bind this uniform buffer to the given base. + + @method + @ignore + @return {UniformBuffer} The UniformBuffer object. + */ + bind(base) { + let currentBuffer = this.appState.uniformBuffers[base]; + + if (currentBuffer !== this) { + + if (currentBuffer) { + currentBuffer.currentBase = -1; + } + + if (this.currentBase !== -1) { + this.appState.uniformBuffers[this.currentBase] = null; + } + + this.gl.bindBufferBase(this.gl.UNIFORM_BUFFER, base, this.buffer); + + this.appState.uniformBuffers[base] = this; + this.currentBase = base; + } + + return this; + } + +} diff --git a/build/module/uniforms.js b/build/module/uniforms.js new file mode 100644 index 00000000..81b0ddfb --- /dev/null +++ b/build/module/uniforms.js @@ -0,0 +1,226 @@ +/////////////////////////////////////////////////////////////////////////////////// +// The MIT License (MIT) +// +// Copyright (c) 2017 Tarek Sherif +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +/////////////////////////////////////////////////////////////////////////////////// + +import { GL } from "./constants.js"; + +// Classes to manage uniform value updates, including +// caching current values. + +const UNIFORM_FUNC_NAME = {}; +UNIFORM_FUNC_NAME[GL.BOOL] = "uniform1i"; +UNIFORM_FUNC_NAME[GL.INT] = "uniform1i"; +UNIFORM_FUNC_NAME[GL.SAMPLER_2D] = "uniform1i"; +UNIFORM_FUNC_NAME[GL.INT_SAMPLER_2D] = "uniform1i"; +UNIFORM_FUNC_NAME[GL.UNSIGNED_INT_SAMPLER_2D] = "uniform1i"; +UNIFORM_FUNC_NAME[GL.SAMPLER_2D_SHADOW] = "uniform1i"; +UNIFORM_FUNC_NAME[GL.SAMPLER_2D_ARRAY] = "uniform1i"; +UNIFORM_FUNC_NAME[GL.INT_SAMPLER_2D_ARRAY] = "uniform1i"; +UNIFORM_FUNC_NAME[GL.UNSIGNED_INT_SAMPLER_2D_ARRAY] = "uniform1i"; +UNIFORM_FUNC_NAME[GL.SAMPLER_2D_ARRAY_SHADOW] = "uniform1i"; +UNIFORM_FUNC_NAME[GL.SAMPLER_CUBE] = "uniform1i"; +UNIFORM_FUNC_NAME[GL.INT_SAMPLER_CUBE] = "uniform1i"; +UNIFORM_FUNC_NAME[GL.UNSIGNED_INT_SAMPLER_CUBE] = "uniform1i"; +UNIFORM_FUNC_NAME[GL.SAMPLER_CUBE_SHADOW] = "uniform1i"; +UNIFORM_FUNC_NAME[GL.SAMPLER_3D] = "uniform1i"; +UNIFORM_FUNC_NAME[GL.INT_SAMPLER_3D] = "uniform1i"; +UNIFORM_FUNC_NAME[GL.UNSIGNED_INT_SAMPLER_3D] = "uniform1i"; +UNIFORM_FUNC_NAME[GL.UNSIGNED_INT] = "uniform1ui"; +UNIFORM_FUNC_NAME[GL.FLOAT] = "uniform1f"; +UNIFORM_FUNC_NAME[GL.FLOAT_VEC2] = "uniform2f"; +UNIFORM_FUNC_NAME[GL.FLOAT_VEC3] = "uniform3f"; +UNIFORM_FUNC_NAME[GL.FLOAT_VEC4] = "uniform4f"; +UNIFORM_FUNC_NAME[GL.INT_VEC2] = "uniform2i"; +UNIFORM_FUNC_NAME[GL.INT_VEC3] = "uniform3i"; +UNIFORM_FUNC_NAME[GL.INT_VEC4] = "uniform4i"; +UNIFORM_FUNC_NAME[GL.UNSIGNED_INT_VEC2] = "uniform2ui"; +UNIFORM_FUNC_NAME[GL.UNSIGNED_INT_VEC3] = "uniform3ui"; +UNIFORM_FUNC_NAME[GL.UNSIGNED_INT_VEC4] = "uniform4ui"; +UNIFORM_FUNC_NAME[GL.BOOL_VEC2] = "uniform2i"; +UNIFORM_FUNC_NAME[GL.BOOL_VEC3] = "uniform3i"; +UNIFORM_FUNC_NAME[GL.BOOL_VEC4] = "uniform4i"; +UNIFORM_FUNC_NAME[GL.FLOAT_MAT2] = "uniformMatrix2fv"; +UNIFORM_FUNC_NAME[GL.FLOAT_MAT3] = "uniformMatrix3fv"; +UNIFORM_FUNC_NAME[GL.FLOAT_MAT4] = "uniformMatrix4fv"; +UNIFORM_FUNC_NAME[GL.FLOAT_MAT2x3] = "uniformMatrix2x3fv"; +UNIFORM_FUNC_NAME[GL.FLOAT_MAT2x4] = "uniformMatrix2x4fv"; +UNIFORM_FUNC_NAME[GL.FLOAT_MAT3x2] = "uniformMatrix3x2fv"; +UNIFORM_FUNC_NAME[GL.FLOAT_MAT3x4] = "uniformMatrix3x4fv"; +UNIFORM_FUNC_NAME[GL.FLOAT_MAT4x2] = "uniformMatrix4x2fv"; +UNIFORM_FUNC_NAME[GL.FLOAT_MAT4x3] = "uniformMatrix4x3fv"; + +const UNIFORM_COMPONENT_COUNT = {}; +UNIFORM_COMPONENT_COUNT[GL.BOOL] = 1; +UNIFORM_COMPONENT_COUNT[GL.INT] = 1; +UNIFORM_COMPONENT_COUNT[GL.SAMPLER_2D] = 1; +UNIFORM_COMPONENT_COUNT[GL.INT_SAMPLER_2D] = 1; +UNIFORM_COMPONENT_COUNT[GL.UNSIGNED_INT_SAMPLER_2D] = 1; +UNIFORM_COMPONENT_COUNT[GL.SAMPLER_2D_SHADOW] = 1; +UNIFORM_COMPONENT_COUNT[GL.SAMPLER_2D_ARRAY] = 1; +UNIFORM_COMPONENT_COUNT[GL.INT_SAMPLER_2D_ARRAY] = 1; +UNIFORM_COMPONENT_COUNT[GL.UNSIGNED_INT_SAMPLER_2D_ARRAY] = 1; +UNIFORM_COMPONENT_COUNT[GL.SAMPLER_2D_ARRAY_SHADOW] = 1; +UNIFORM_COMPONENT_COUNT[GL.SAMPLER_CUBE] = 1; +UNIFORM_COMPONENT_COUNT[GL.INT_SAMPLER_CUBE] = 1; +UNIFORM_COMPONENT_COUNT[GL.UNSIGNED_INT_SAMPLER_CUBE] = 1; +UNIFORM_COMPONENT_COUNT[GL.SAMPLER_CUBE_SHADOW] = 1; +UNIFORM_COMPONENT_COUNT[GL.SAMPLER_3D] = 1; +UNIFORM_COMPONENT_COUNT[GL.INT_SAMPLER_3D] = 1; +UNIFORM_COMPONENT_COUNT[GL.UNSIGNED_INT_SAMPLER_3D] = 1; +UNIFORM_COMPONENT_COUNT[GL.UNSIGNED_INT] = 1; +UNIFORM_COMPONENT_COUNT[GL.FLOAT] = 1; +UNIFORM_COMPONENT_COUNT[GL.FLOAT_VEC2] = 2; +UNIFORM_COMPONENT_COUNT[GL.FLOAT_VEC3] = 3; +UNIFORM_COMPONENT_COUNT[GL.FLOAT_VEC4] = 4; +UNIFORM_COMPONENT_COUNT[GL.INT_VEC2] = 2; +UNIFORM_COMPONENT_COUNT[GL.INT_VEC3] = 3; +UNIFORM_COMPONENT_COUNT[GL.INT_VEC4] = 4; +UNIFORM_COMPONENT_COUNT[GL.UNSIGNED_INT_VEC2] = 2; +UNIFORM_COMPONENT_COUNT[GL.UNSIGNED_INT_VEC3] = 3; +UNIFORM_COMPONENT_COUNT[GL.UNSIGNED_INT_VEC4] = 4; +UNIFORM_COMPONENT_COUNT[GL.BOOL_VEC2] = 2; +UNIFORM_COMPONENT_COUNT[GL.BOOL_VEC3] = 3; +UNIFORM_COMPONENT_COUNT[GL.BOOL_VEC4] = 4; +UNIFORM_COMPONENT_COUNT[GL.FLOAT_MAT2] = 4; +UNIFORM_COMPONENT_COUNT[GL.FLOAT_MAT3] = 9; +UNIFORM_COMPONENT_COUNT[GL.FLOAT_MAT4] = 16; +UNIFORM_COMPONENT_COUNT[GL.FLOAT_MAT2x3] = 6; +UNIFORM_COMPONENT_COUNT[GL.FLOAT_MAT2x4] = 8; +UNIFORM_COMPONENT_COUNT[GL.FLOAT_MAT3x2] = 6; +UNIFORM_COMPONENT_COUNT[GL.FLOAT_MAT3x4] = 12; +UNIFORM_COMPONENT_COUNT[GL.FLOAT_MAT4x2] = 8; +UNIFORM_COMPONENT_COUNT[GL.FLOAT_MAT4x3] = 12; + +const UNIFORM_CACHE_CLASS = {}; +UNIFORM_CACHE_CLASS[GL.INT] = Int32Array; +UNIFORM_CACHE_CLASS[GL.SAMPLER_2D] = Int32Array; +UNIFORM_CACHE_CLASS[GL.INT_SAMPLER_2D] = Int32Array; +UNIFORM_CACHE_CLASS[GL.UNSIGNED_INT_SAMPLER_2D] = Int32Array; +UNIFORM_CACHE_CLASS[GL.SAMPLER_2D_SHADOW] = Int32Array; +UNIFORM_CACHE_CLASS[GL.SAMPLER_2D_ARRAY] = Int32Array; +UNIFORM_CACHE_CLASS[GL.INT_SAMPLER_2D_ARRAY] = Int32Array; +UNIFORM_CACHE_CLASS[GL.UNSIGNED_INT_SAMPLER_2D_ARRAY] = Int32Array; +UNIFORM_CACHE_CLASS[GL.SAMPLER_2D_ARRAY_SHADOW] = Int32Array; +UNIFORM_CACHE_CLASS[GL.SAMPLER_CUBE] = Int32Array; +UNIFORM_CACHE_CLASS[GL.INT_SAMPLER_CUBE] = Int32Array; +UNIFORM_CACHE_CLASS[GL.UNSIGNED_INT_SAMPLER_CUBE] = Int32Array; +UNIFORM_CACHE_CLASS[GL.SAMPLER_CUBE_SHADOW] = Int32Array; +UNIFORM_CACHE_CLASS[GL.SAMPLER_3D] = Int32Array; +UNIFORM_CACHE_CLASS[GL.INT_SAMPLER_3D] = Int32Array; +UNIFORM_CACHE_CLASS[GL.UNSIGNED_INT_SAMPLER_3D] = Int32Array; +UNIFORM_CACHE_CLASS[GL.UNSIGNED_INT] = Uint32Array; +UNIFORM_CACHE_CLASS[GL.FLOAT] = Float32Array; +UNIFORM_CACHE_CLASS[GL.FLOAT_VEC2] = Float32Array; +UNIFORM_CACHE_CLASS[GL.FLOAT_VEC3] = Float32Array; +UNIFORM_CACHE_CLASS[GL.FLOAT_VEC4] = Float32Array; +UNIFORM_CACHE_CLASS[GL.INT_VEC2] = Int32Array; +UNIFORM_CACHE_CLASS[GL.INT_VEC3] = Int32Array; +UNIFORM_CACHE_CLASS[GL.INT_VEC4] = Int32Array; +UNIFORM_CACHE_CLASS[GL.UNSIGNED_INT_VEC2] = Uint32Array; +UNIFORM_CACHE_CLASS[GL.UNSIGNED_INT_VEC3] = Uint32Array; +UNIFORM_CACHE_CLASS[GL.UNSIGNED_INT_VEC4] = Uint32Array; + +export class SingleComponentUniform { + + constructor(gl, handle, type) { + this.gl = gl; + this.handle = handle; + this.glFuncName = UNIFORM_FUNC_NAME[type]; + this.cache = type === GL.BOOL ? false : 0; + } + + set(value) { + if (this.cache !== value) { + this.gl[this.glFuncName](this.handle, value); + this.cache = value; + } + } + +} + +export class MultiNumericUniform { + + constructor(gl, handle, type, count) { + this.gl = gl; + this.handle = handle; + this.glFuncName = UNIFORM_FUNC_NAME[type] + "v"; + this.count = count; + this.cache = new UNIFORM_CACHE_CLASS[type](UNIFORM_COMPONENT_COUNT[type] * count); + } + + set(value) { + for (let i = 0, len = value.length; i < len; ++i) { + if (this.cache[i] !== value[i]) { + this.gl[this.glFuncName](this.handle, value); + this.cache.set(value); + return; + } + } + } + +} + +export class MultiBoolUniform { + + constructor(gl, handle, type, count) { + this.gl = gl; + this.handle = handle; + this.glFuncName = UNIFORM_FUNC_NAME[type] + "v"; + this.count = count; + this.cache = new Array(UNIFORM_COMPONENT_COUNT[type] * count).fill(false); + } + + set(value) { + for (let i = 0, len = value.length; i < len; ++i) { + if (this.cache[i] !== value[i]) { + this.gl[this.glFuncName](this.handle, value); + for (let j = i; j < len; j++) { + this.cache[j] = value[j]; + } + return; + } + } + } + +} + +export class MatrixUniform { + + constructor(gl, handle, type, count) { + this.gl = gl; + this.handle = handle; + this.glFuncName = UNIFORM_FUNC_NAME[type]; + this.count = count; + this.cache = new Float32Array(UNIFORM_COMPONENT_COUNT[type] * count); + } + + set(value) { + for (let i = 0, len = value.length; i < len; ++i) { + if (this.cache[i] !== value[i]) { + this.gl[this.glFuncName](this.handle, false, value); + this.cache.set(value); + return; + } + } + } + +} diff --git a/build/module/vertex-array.js b/build/module/vertex-array.js new file mode 100644 index 00000000..19b1519a --- /dev/null +++ b/build/module/vertex-array.js @@ -0,0 +1,229 @@ +// Copyright (c) 2017 Tarek Sherif +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +/////////////////////////////////////////////////////////////////////////////////// + +import { GL, TYPE_SIZE, DUMMY_OBJECT } from "./constants.js"; + +/** + Organizes vertex buffer and attribute state. + + @class + @prop {WebGLRenderingContext} gl The WebGL context. + @prop {WebGLVertexArrayObject} vertexArray Vertex array object. + @prop {number} numElements Number of elements in the vertex array. + @prop {boolean} indexed Whether this vertex array is set up for indexed drawing. + @prop {GLenum} indexType Data type of the indices. + @prop {number} numInstances Number of instances to draw with this vertex array. + @prop {Object} appState Tracked GL state. +*/ +export class VertexArray { + + constructor(gl, appState) { + this.gl = gl; + this.appState = appState; + this.vertexArray = null; + this.indexType = null; + this.indexed = false; + this.numElements = 0; + this.numInstances = 1; + this.offsets = 0; + this.numDraws = 1; + } + + /** + Restore vertex array after context loss. + + @method + @return {VertexArray} The VertexArray object. + */ + restore() { + if (this.appState.vertexArray === this) { + this.appState.vertexArray = null; + } + + // re-allocate at gl level, if necessary + if (this.vertexArray !== null) { + this.vertexArray = this.gl.createVertexArray(); + } + + return this; + } + + + /** + Bind an per-vertex attribute buffer to this vertex array. + + @method + @param {number} attributeIndex The attribute location to bind to. + @param {VertexBuffer} vertexBuffer The VertexBuffer to bind. + @param {Object} [options] Attribute pointer options. These will override those provided in the + VertexBuffer. + @param {GLEnum} [options.type] Type of data stored in the buffer. + @param {GLEnum} [options.size] Number of components per vertex. + @param {GLEnum} [options.stride] Number of bytes between the start of data for each vertex. + @param {GLEnum} [options.offset] Number of bytes before the start of data for the first vertex. + @param {GLEnum} [options.normalized] Data is integer data that should be normalized to a float. + @param {GLEnum} [options.integer] Pass data as integers. + @return {VertexArray} The VertexArray object. + */ + vertexAttributeBuffer(attributeIndex, vertexBuffer, options = DUMMY_OBJECT) { + this.attributeBuffer(attributeIndex, vertexBuffer, options, false); + + return this; + } + + /** + Bind an per-instance attribute buffer to this vertex array. + + @method + @param {number} attributeIndex The attribute location to bind to. + @param {VertexBuffer} vertexBuffer The VertexBuffer to bind. + @param {Object} [options] Attribute pointer options. These will override those provided in the + VertexBuffer. + @param {GLEnum} [options.type] Type of data stored in the buffer. + @param {GLEnum} [options.size] Number of components per vertex. + @param {GLEnum} [options.stride] Number of bytes between the start of data for each vertex. + @param {GLEnum} [options.offset] Number of bytes before the start of data for the first vertex. + @param {GLEnum} [options.normalized] Data is integer data that should be normalized to a float. + @param {GLEnum} [options.integer] Pass data as integers. + @return {VertexArray} The VertexArray object. + */ + instanceAttributeBuffer(attributeIndex, vertexBuffer, options = DUMMY_OBJECT) { + this.attributeBuffer(attributeIndex, vertexBuffer, options, true); + + return this; + } + + /** + Bind an index buffer to this vertex array. + + @method + @param {VertexBuffer} vertexBuffer The VertexBuffer to bind. + @return {VertexArray} The VertexArray object. + */ + indexBuffer(vertexBuffer) { + // allocate at gl level, if necessary + if (this.vertexArray === null) { + this.vertexArray = this.gl.createVertexArray(); + } + + this.bind(); + this.gl.bindBuffer(GL.ELEMENT_ARRAY_BUFFER, vertexBuffer.buffer); + + this.numElements = vertexBuffer.numItems * 3; + this.indexType = vertexBuffer.type; + this.indexed = true; + + return this; + } + + /** + Delete this vertex array. + + @method + @return {VertexArray} The VertexArray object. + */ + delete() { + if (this.vertexArray) { + this.gl.deleteVertexArray(this.vertexArray); + this.vertexArray = null; + + if (this.appState.vertexArray === this) { + this.gl.bindVertexArray(null); + this.appState.vertexArray = null; + } + } + + return this; + } + + // Bind this vertex array. + bind() { + if (this.appState.vertexArray !== this) { + this.gl.bindVertexArray(this.vertexArray); + this.appState.vertexArray = this; + } + + return this; + } + + // Generic attribute buffer attachment + attributeBuffer(attributeIndex, vertexBuffer, options = {}, instanced) { + // allocate at gl level, if necessary + if (this.vertexArray === null) { + this.vertexArray = this.gl.createVertexArray(); + } + + this.bind(); + this.gl.bindBuffer(GL.ARRAY_BUFFER, vertexBuffer.buffer); + + let { + type = vertexBuffer.type, + size = vertexBuffer.itemSize, + stride = 0, + offset = 0, + normalized = false, + integer = Boolean(vertexBuffer.integer && !normalized) + } = options; + + let numColumns = vertexBuffer.numColumns; + + if (stride === 0) { + // Set explicitly for matrix buffers + stride = numColumns * size * TYPE_SIZE[type]; + } + + for (let i = 0; i < numColumns; ++i) { + if (integer) { + this.gl.vertexAttribIPointer( + attributeIndex + i, + size, + type, + stride, + offset + i * size * TYPE_SIZE[type]); + } else { + this.gl.vertexAttribPointer( + attributeIndex + i, + size, + type, + normalized, + stride, + offset + i * size * TYPE_SIZE[type]); + } + + if (instanced) { + this.gl.vertexAttribDivisor(attributeIndex + i, 1); + } + + this.gl.enableVertexAttribArray(attributeIndex + i); + } + + if (this.numDraws === 1) { + if (instanced) { + this.numInstances = vertexBuffer.numItems; + } else { + this.numElements = this.numElements || vertexBuffer.numItems; + } + } + + this.gl.bindBuffer(GL.ARRAY_BUFFER, null); + + return this; + } +} diff --git a/build/module/vertex-buffer.js b/build/module/vertex-buffer.js new file mode 100644 index 00000000..89b314fa --- /dev/null +++ b/build/module/vertex-buffer.js @@ -0,0 +1,187 @@ +/////////////////////////////////////////////////////////////////////////////////// +// The MIT License (MIT) +// +// Copyright (c) 2017 Tarek Sherif +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +/////////////////////////////////////////////////////////////////////////////////// + +import { GL, TYPE_SIZE } from "./constants.js"; + +const INTEGER_TYPES = { + [GL.BYTE]: true, + [GL.UNSIGNED_BYTE]: true, + [GL.SHORT]: true, + [GL.UNSIGNED_SHORT]: true, + [GL.INT]: true, + [GL.UNSIGNED_INT]: true +}; + +/** + Storage for vertex data. + + @class + @prop {WebGLRenderingContext} gl The WebGL context. + @prop {WebGLBuffer} buffer Allocated buffer storage. + @prop {GLEnum} type The type of data stored in the buffer. + @prop {number} itemSize Number of array elements per vertex. + @prop {number} numItems Number of vertices represented. + @prop {GLEnum} usage The usage pattern of the buffer. + @prop {boolean} indexArray Whether this is an index array. + @prop {GLEnum} binding GL binding point (ARRAY_BUFFER or ELEMENT_ARRAY_BUFFER). + @prop {Object} appState Tracked GL state. +*/ +export class VertexBuffer { + + constructor(gl, appState, type, itemSize, data, usage = gl.STATIC_DRAW, indexArray) { + let numColumns; + switch(type) { + case GL.FLOAT_MAT4: + case GL.FLOAT_MAT4x2: + case GL.FLOAT_MAT4x3: + numColumns = 4; + break; + case GL.FLOAT_MAT3: + case GL.FLOAT_MAT3x2: + case GL.FLOAT_MAT3x4: + numColumns = 3; + break; + case GL.FLOAT_MAT2: + case GL.FLOAT_MAT2x3: + case GL.FLOAT_MAT2x4: + numColumns = 2; + break; + default: + numColumns = 1; + } + + switch(type) { + case GL.FLOAT_MAT4: + case GL.FLOAT_MAT3x4: + case GL.FLOAT_MAT2x4: + itemSize = 4; + type = GL.FLOAT; + break; + case GL.FLOAT_MAT3: + case GL.FLOAT_MAT4x3: + case GL.FLOAT_MAT2x3: + itemSize = 3; + type = GL.FLOAT; + break; + case GL.FLOAT_MAT2: + case GL.FLOAT_MAT3x2: + case GL.FLOAT_MAT4x2: + itemSize = 2; + type = GL.FLOAT; + break; + } + + let dataLength; + let byteLength; + if (typeof data === "number") { + dataLength = data; + if (type) { + data *= TYPE_SIZE[type]; + } + byteLength = data; + } else { + dataLength = data.length; + byteLength = data.byteLength; + } + + this.gl = gl; + this.buffer = null; + this.appState = appState; + this.type = type; + this.itemSize = itemSize; // In bytes for interleaved arrays. + this.numItems = type ? dataLength / (itemSize * numColumns) : byteLength / itemSize; + this.numColumns = numColumns; + this.byteLength = byteLength; + this.usage = usage; + this.indexArray = Boolean(indexArray); + this.integer = Boolean(INTEGER_TYPES[this.type]); + this.binding = this.indexArray ? GL.ELEMENT_ARRAY_BUFFER : GL.ARRAY_BUFFER; + + this.restore(data); + } + + /** + Restore vertex buffer after context loss. + + @method + @param {ArrayBufferView|number} data Buffer data itself or the total + number of elements to be allocated. + @return {VertexBuffer} The VertexBuffer object. + */ + restore(data) { + if (!data) { + data = this.byteLength; + } + + // Don't want to update vertex array bindings + if (this.appState.vertexArray) { + this.gl.bindVertexArray(null); + this.appState.vertexArray = null; + } + + this.buffer = this.gl.createBuffer(); + this.gl.bindBuffer(this.binding, this.buffer); + this.gl.bufferData(this.binding, data, this.usage); + this.gl.bindBuffer(this.binding, null); + + return this; + } + + /** + Update data in this buffer. NOTE: the data must fit + the originally-allocated buffer! + + @method + @param {VertexBufferView} data Data to store in the buffer. + @return {VertexBuffer} The VertexBuffer object. + */ + data(data) { + // Don't want to update vertex array bindings + if (this.appState.vertexArray) { + this.gl.bindVertexArray(null); + this.appState.vertexArray = null; + } + + this.gl.bindBuffer(this.binding, this.buffer); + this.gl.bufferSubData(this.binding, 0, data); + this.gl.bindBuffer(this.binding, null); + + return this; + } + + /** + Delete this array buffer. + + @method + @return {VertexBuffer} The VertexBuffer object. + */ + delete() { + if (this.buffer) { + this.gl.deleteBuffer(this.buffer); + this.buffer = null; + } + + return this; + } + +} diff --git a/build/picogl.min.js b/build/picogl.min.js index 636ed0ed..87d45a1b 100644 --- a/build/picogl.min.js +++ b/build/picogl.min.js @@ -1,6 +1,4 @@ -/* -PicoGL.js v0.14.0 - +/*! The MIT License (MIT) Copyright (c) 2017 Tarek Sherif @@ -21,6 +19,6 @@ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.PicoGL=e():t.PicoGL=e()}("undefined"!=typeof self?self:this,function(){return function(t){var e={};function i(r){if(e[r])return e[r].exports;var s=e[r]={i:r,l:!1,exports:{}};return t[r].call(s.exports,s,s.exports,i),s.l=!0,s.exports}return i.m=t,i.c=e,i.d=function(t,e,r){i.o(t,e)||Object.defineProperty(t,e,{configurable:!1,enumerable:!0,get:r})},i.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return i.d(e,"a",e),e},i.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},i.p="",i(i.s=5)}([function(t,e,i){"use strict";const r={DEPTH_BUFFER_BIT:256,STENCIL_BUFFER_BIT:1024,COLOR_BUFFER_BIT:16384,POINTS:0,LINES:1,LINE_LOOP:2,LINE_STRIP:3,TRIANGLES:4,TRIANGLE_STRIP:5,TRIANGLE_FAN:6,ZERO:0,ONE:1,SRC_COLOR:768,ONE_MINUS_SRC_COLOR:769,SRC_ALPHA:770,ONE_MINUS_SRC_ALPHA:771,DST_ALPHA:772,ONE_MINUS_DST_ALPHA:773,DST_COLOR:774,ONE_MINUS_DST_COLOR:775,SRC_ALPHA_SATURATE:776,FUNC_ADD:32774,BLEND_EQUATION:32777,BLEND_EQUATION_RGB:32777,BLEND_EQUATION_ALPHA:34877,FUNC_SUBTRACT:32778,FUNC_REVERSE_SUBTRACT:32779,BLEND_DST_RGB:32968,BLEND_SRC_RGB:32969,BLEND_DST_ALPHA:32970,BLEND_SRC_ALPHA:32971,CONSTANT_COLOR:32769,ONE_MINUS_CONSTANT_COLOR:32770,CONSTANT_ALPHA:32771,ONE_MINUS_CONSTANT_ALPHA:32772,BLEND_COLOR:32773,ARRAY_BUFFER:34962,ELEMENT_ARRAY_BUFFER:34963,ARRAY_BUFFER_BINDING:34964,ELEMENT_ARRAY_BUFFER_BINDING:34965,STREAM_DRAW:35040,STATIC_DRAW:35044,DYNAMIC_DRAW:35048,BUFFER_SIZE:34660,BUFFER_USAGE:34661,CURRENT_VERTEX_ATTRIB:34342,FRONT:1028,BACK:1029,FRONT_AND_BACK:1032,CULL_FACE:2884,BLEND:3042,DITHER:3024,STENCIL_TEST:2960,DEPTH_TEST:2929,SCISSOR_TEST:3089,POLYGON_OFFSET_FILL:32823,SAMPLE_ALPHA_TO_COVERAGE:32926,SAMPLE_COVERAGE:32928,NO_ERROR:0,INVALID_ENUM:1280,INVALID_VALUE:1281,INVALID_OPERATION:1282,OUT_OF_MEMORY:1285,CW:2304,CCW:2305,LINE_WIDTH:2849,ALIASED_POINT_SIZE_RANGE:33901,ALIASED_LINE_WIDTH_RANGE:33902,CULL_FACE_MODE:2885,FRONT_FACE:2886,DEPTH_RANGE:2928,DEPTH_WRITEMASK:2930,DEPTH_CLEAR_VALUE:2931,DEPTH_FUNC:2932,STENCIL_CLEAR_VALUE:2961,STENCIL_FUNC:2962,STENCIL_FAIL:2964,STENCIL_PASS_DEPTH_FAIL:2965,STENCIL_PASS_DEPTH_PASS:2966,STENCIL_REF:2967,STENCIL_VALUE_MASK:2963,STENCIL_WRITEMASK:2968,STENCIL_BACK_FUNC:34816,STENCIL_BACK_FAIL:34817,STENCIL_BACK_PASS_DEPTH_FAIL:34818,STENCIL_BACK_PASS_DEPTH_PASS:34819,STENCIL_BACK_REF:36003,STENCIL_BACK_VALUE_MASK:36004,STENCIL_BACK_WRITEMASK:36005,VIEWPORT:2978,SCISSOR_BOX:3088,COLOR_CLEAR_VALUE:3106,COLOR_WRITEMASK:3107,UNPACK_ALIGNMENT:3317,PACK_ALIGNMENT:3333,MAX_TEXTURE_SIZE:3379,MAX_VIEWPORT_DIMS:3386,SUBPIXEL_BITS:3408,RED_BITS:3410,GREEN_BITS:3411,BLUE_BITS:3412,ALPHA_BITS:3413,DEPTH_BITS:3414,STENCIL_BITS:3415,POLYGON_OFFSET_UNITS:10752,POLYGON_OFFSET_FACTOR:32824,TEXTURE_BINDING_2D:32873,SAMPLE_BUFFERS:32936,SAMPLES:32937,SAMPLE_COVERAGE_VALUE:32938,SAMPLE_COVERAGE_INVERT:32939,COMPRESSED_TEXTURE_FORMATS:34467,DONT_CARE:4352,FASTEST:4353,NICEST:4354,GENERATE_MIPMAP_HINT:33170,BYTE:5120,UNSIGNED_BYTE:5121,SHORT:5122,UNSIGNED_SHORT:5123,INT:5124,UNSIGNED_INT:5125,FLOAT:5126,DEPTH_COMPONENT:6402,ALPHA:6406,RGB:6407,RGBA:6408,LUMINANCE:6409,LUMINANCE_ALPHA:6410,UNSIGNED_SHORT_4_4_4_4:32819,UNSIGNED_SHORT_5_5_5_1:32820,UNSIGNED_SHORT_5_6_5:33635,FRAGMENT_SHADER:35632,VERTEX_SHADER:35633,MAX_VERTEX_ATTRIBS:34921,MAX_VERTEX_UNIFORM_VECTORS:36347,MAX_VARYING_VECTORS:36348,MAX_COMBINED_TEXTURE_IMAGE_UNITS:35661,MAX_VERTEX_TEXTURE_IMAGE_UNITS:35660,MAX_TEXTURE_IMAGE_UNITS:34930,MAX_FRAGMENT_UNIFORM_VECTORS:36349,SHADER_TYPE:35663,DELETE_STATUS:35712,LINK_STATUS:35714,VALIDATE_STATUS:35715,ATTACHED_SHADERS:35717,ACTIVE_UNIFORMS:35718,ACTIVE_ATTRIBUTES:35721,SHADING_LANGUAGE_VERSION:35724,CURRENT_PROGRAM:35725,NEVER:512,LESS:513,EQUAL:514,LEQUAL:515,GREATER:516,NOTEQUAL:517,GEQUAL:518,ALWAYS:519,KEEP:7680,REPLACE:7681,INCR:7682,DECR:7683,INVERT:5386,INCR_WRAP:34055,DECR_WRAP:34056,VENDOR:7936,RENDERER:7937,VERSION:7938,NEAREST:9728,LINEAR:9729,NEAREST_MIPMAP_NEAREST:9984,LINEAR_MIPMAP_NEAREST:9985,NEAREST_MIPMAP_LINEAR:9986,LINEAR_MIPMAP_LINEAR:9987,TEXTURE_MAG_FILTER:10240,TEXTURE_MIN_FILTER:10241,TEXTURE_WRAP_S:10242,TEXTURE_WRAP_T:10243,TEXTURE_2D:3553,TEXTURE:5890,TEXTURE_CUBE_MAP:34067,TEXTURE_BINDING_CUBE_MAP:34068,TEXTURE_CUBE_MAP_POSITIVE_X:34069,TEXTURE_CUBE_MAP_NEGATIVE_X:34070,TEXTURE_CUBE_MAP_POSITIVE_Y:34071,TEXTURE_CUBE_MAP_NEGATIVE_Y:34072,TEXTURE_CUBE_MAP_POSITIVE_Z:34073,TEXTURE_CUBE_MAP_NEGATIVE_Z:34074,MAX_CUBE_MAP_TEXTURE_SIZE:34076,TEXTURE0:33984,TEXTURE1:33985,TEXTURE2:33986,TEXTURE3:33987,TEXTURE4:33988,TEXTURE5:33989,TEXTURE6:33990,TEXTURE7:33991,TEXTURE8:33992,TEXTURE9:33993,TEXTURE10:33994,TEXTURE11:33995,TEXTURE12:33996,TEXTURE13:33997,TEXTURE14:33998,TEXTURE15:33999,TEXTURE16:34e3,TEXTURE17:34001,TEXTURE18:34002,TEXTURE19:34003,TEXTURE20:34004,TEXTURE21:34005,TEXTURE22:34006,TEXTURE23:34007,TEXTURE24:34008,TEXTURE25:34009,TEXTURE26:34010,TEXTURE27:34011,TEXTURE28:34012,TEXTURE29:34013,TEXTURE30:34014,TEXTURE31:34015,ACTIVE_TEXTURE:34016,REPEAT:10497,CLAMP_TO_EDGE:33071,MIRRORED_REPEAT:33648,FLOAT_VEC2:35664,FLOAT_VEC3:35665,FLOAT_VEC4:35666,INT_VEC2:35667,INT_VEC3:35668,INT_VEC4:35669,BOOL:35670,BOOL_VEC2:35671,BOOL_VEC3:35672,BOOL_VEC4:35673,FLOAT_MAT2:35674,FLOAT_MAT3:35675,FLOAT_MAT4:35676,SAMPLER_2D:35678,SAMPLER_CUBE:35680,VERTEX_ATTRIB_ARRAY_ENABLED:34338,VERTEX_ATTRIB_ARRAY_SIZE:34339,VERTEX_ATTRIB_ARRAY_STRIDE:34340,VERTEX_ATTRIB_ARRAY_TYPE:34341,VERTEX_ATTRIB_ARRAY_NORMALIZED:34922,VERTEX_ATTRIB_ARRAY_POINTER:34373,VERTEX_ATTRIB_ARRAY_BUFFER_BINDING:34975,IMPLEMENTATION_COLOR_READ_TYPE:35738,IMPLEMENTATION_COLOR_READ_FORMAT:35739,COMPILE_STATUS:35713,LOW_FLOAT:36336,MEDIUM_FLOAT:36337,HIGH_FLOAT:36338,LOW_INT:36339,MEDIUM_INT:36340,HIGH_INT:36341,FRAMEBUFFER:36160,RENDERBUFFER:36161,RGBA4:32854,RGB5_A1:32855,RGB565:36194,DEPTH_COMPONENT16:33189,STENCIL_INDEX:6401,STENCIL_INDEX8:36168,DEPTH_STENCIL:34041,RENDERBUFFER_WIDTH:36162,RENDERBUFFER_HEIGHT:36163,RENDERBUFFER_INTERNAL_FORMAT:36164,RENDERBUFFER_RED_SIZE:36176,RENDERBUFFER_GREEN_SIZE:36177,RENDERBUFFER_BLUE_SIZE:36178,RENDERBUFFER_ALPHA_SIZE:36179,RENDERBUFFER_DEPTH_SIZE:36180,RENDERBUFFER_STENCIL_SIZE:36181,FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE:36048,FRAMEBUFFER_ATTACHMENT_OBJECT_NAME:36049,FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL:36050,FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE:36051,COLOR_ATTACHMENT0:36064,DEPTH_ATTACHMENT:36096,STENCIL_ATTACHMENT:36128,DEPTH_STENCIL_ATTACHMENT:33306,NONE:0,FRAMEBUFFER_COMPLETE:36053,FRAMEBUFFER_INCOMPLETE_ATTACHMENT:36054,FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:36055,FRAMEBUFFER_INCOMPLETE_DIMENSIONS:36057,FRAMEBUFFER_UNSUPPORTED:36061,FRAMEBUFFER_BINDING:36006,RENDERBUFFER_BINDING:36007,MAX_RENDERBUFFER_SIZE:34024,INVALID_FRAMEBUFFER_OPERATION:1286,UNPACK_FLIP_Y_WEBGL:37440,UNPACK_PREMULTIPLY_ALPHA_WEBGL:37441,CONTEXT_LOST_WEBGL:37442,UNPACK_COLORSPACE_CONVERSION_WEBGL:37443,BROWSER_DEFAULT_WEBGL:37444,READ_BUFFER:3074,UNPACK_ROW_LENGTH:3314,UNPACK_SKIP_ROWS:3315,UNPACK_SKIP_PIXELS:3316,PACK_ROW_LENGTH:3330,PACK_SKIP_ROWS:3331,PACK_SKIP_PIXELS:3332,COLOR:6144,DEPTH:6145,STENCIL:6146,RED:6403,RGB8:32849,RGBA8:32856,RGB10_A2:32857,TEXTURE_BINDING_3D:32874,UNPACK_SKIP_IMAGES:32877,UNPACK_IMAGE_HEIGHT:32878,TEXTURE_3D:32879,TEXTURE_WRAP_R:32882,MAX_3D_TEXTURE_SIZE:32883,UNSIGNED_INT_2_10_10_10_REV:33640,MAX_ELEMENTS_VERTICES:33e3,MAX_ELEMENTS_INDICES:33001,TEXTURE_MIN_LOD:33082,TEXTURE_MAX_LOD:33083,TEXTURE_BASE_LEVEL:33084,TEXTURE_MAX_LEVEL:33085,MIN:32775,MAX:32776,DEPTH_COMPONENT24:33190,MAX_TEXTURE_LOD_BIAS:34045,TEXTURE_COMPARE_MODE:34892,TEXTURE_COMPARE_FUNC:34893,CURRENT_QUERY:34917,QUERY_RESULT:34918,QUERY_RESULT_AVAILABLE:34919,STREAM_READ:35041,STREAM_COPY:35042,STATIC_READ:35045,STATIC_COPY:35046,DYNAMIC_READ:35049,DYNAMIC_COPY:35050,MAX_DRAW_BUFFERS:34852,DRAW_BUFFER0:34853,DRAW_BUFFER1:34854,DRAW_BUFFER2:34855,DRAW_BUFFER3:34856,DRAW_BUFFER4:34857,DRAW_BUFFER5:34858,DRAW_BUFFER6:34859,DRAW_BUFFER7:34860,DRAW_BUFFER8:34861,DRAW_BUFFER9:34862,DRAW_BUFFER10:34863,DRAW_BUFFER11:34864,DRAW_BUFFER12:34865,DRAW_BUFFER13:34866,DRAW_BUFFER14:34867,DRAW_BUFFER15:34868,MAX_FRAGMENT_UNIFORM_COMPONENTS:35657,MAX_VERTEX_UNIFORM_COMPONENTS:35658,SAMPLER_3D:35679,SAMPLER_2D_SHADOW:35682,FRAGMENT_SHADER_DERIVATIVE_HINT:35723,PIXEL_PACK_BUFFER:35051,PIXEL_UNPACK_BUFFER:35052,PIXEL_PACK_BUFFER_BINDING:35053,PIXEL_UNPACK_BUFFER_BINDING:35055,FLOAT_MAT2x3:35685,FLOAT_MAT2x4:35686,FLOAT_MAT3x2:35687,FLOAT_MAT3x4:35688,FLOAT_MAT4x2:35689,FLOAT_MAT4x3:35690,SRGB:35904,SRGB8:35905,SRGB8_ALPHA8:35907,COMPARE_REF_TO_TEXTURE:34894,RGBA32F:34836,RGB32F:34837,RGBA16F:34842,RGB16F:34843,VERTEX_ATTRIB_ARRAY_INTEGER:35069,MAX_ARRAY_TEXTURE_LAYERS:35071,MIN_PROGRAM_TEXEL_OFFSET:35076,MAX_PROGRAM_TEXEL_OFFSET:35077,MAX_VARYING_COMPONENTS:35659,TEXTURE_2D_ARRAY:35866,TEXTURE_BINDING_2D_ARRAY:35869,R11F_G11F_B10F:35898,UNSIGNED_INT_10F_11F_11F_REV:35899,RGB9_E5:35901,UNSIGNED_INT_5_9_9_9_REV:35902,TRANSFORM_FEEDBACK_BUFFER_MODE:35967,MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS:35968,TRANSFORM_FEEDBACK_VARYINGS:35971,TRANSFORM_FEEDBACK_BUFFER_START:35972,TRANSFORM_FEEDBACK_BUFFER_SIZE:35973,TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN:35976,RASTERIZER_DISCARD:35977,MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS:35978,MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS:35979,INTERLEAVED_ATTRIBS:35980,SEPARATE_ATTRIBS:35981,TRANSFORM_FEEDBACK_BUFFER:35982,TRANSFORM_FEEDBACK_BUFFER_BINDING:35983,RGBA32UI:36208,RGB32UI:36209,RGBA16UI:36214,RGB16UI:36215,RGBA8UI:36220,RGB8UI:36221,RGBA32I:36226,RGB32I:36227,RGBA16I:36232,RGB16I:36233,RGBA8I:36238,RGB8I:36239,RED_INTEGER:36244,RGB_INTEGER:36248,RGBA_INTEGER:36249,SAMPLER_2D_ARRAY:36289,SAMPLER_2D_ARRAY_SHADOW:36292,SAMPLER_CUBE_SHADOW:36293,UNSIGNED_INT_VEC2:36294,UNSIGNED_INT_VEC3:36295,UNSIGNED_INT_VEC4:36296,INT_SAMPLER_2D:36298,INT_SAMPLER_3D:36299,INT_SAMPLER_CUBE:36300,INT_SAMPLER_2D_ARRAY:36303,UNSIGNED_INT_SAMPLER_2D:36306,UNSIGNED_INT_SAMPLER_3D:36307,UNSIGNED_INT_SAMPLER_CUBE:36308,UNSIGNED_INT_SAMPLER_2D_ARRAY:36311,DEPTH_COMPONENT32F:36012,DEPTH32F_STENCIL8:36013,FLOAT_32_UNSIGNED_INT_24_8_REV:36269,FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING:33296,FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE:33297,FRAMEBUFFER_ATTACHMENT_RED_SIZE:33298,FRAMEBUFFER_ATTACHMENT_GREEN_SIZE:33299,FRAMEBUFFER_ATTACHMENT_BLUE_SIZE:33300,FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE:33301,FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE:33302,FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE:33303,FRAMEBUFFER_DEFAULT:33304,UNSIGNED_INT_24_8:34042,DEPTH24_STENCIL8:35056,UNSIGNED_NORMALIZED:35863,DRAW_FRAMEBUFFER_BINDING:36006,READ_FRAMEBUFFER:36008,DRAW_FRAMEBUFFER:36009,READ_FRAMEBUFFER_BINDING:36010,RENDERBUFFER_SAMPLES:36011,FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER:36052,MAX_COLOR_ATTACHMENTS:36063,COLOR_ATTACHMENT1:36065,COLOR_ATTACHMENT2:36066,COLOR_ATTACHMENT3:36067,COLOR_ATTACHMENT4:36068,COLOR_ATTACHMENT5:36069,COLOR_ATTACHMENT6:36070,COLOR_ATTACHMENT7:36071,COLOR_ATTACHMENT8:36072,COLOR_ATTACHMENT9:36073,COLOR_ATTACHMENT10:36074,COLOR_ATTACHMENT11:36075,COLOR_ATTACHMENT12:36076,COLOR_ATTACHMENT13:36077,COLOR_ATTACHMENT14:36078,COLOR_ATTACHMENT15:36079,FRAMEBUFFER_INCOMPLETE_MULTISAMPLE:36182,MAX_SAMPLES:36183,HALF_FLOAT:5131,RG:33319,RG_INTEGER:33320,R8:33321,RG8:33323,R16F:33325,R32F:33326,RG16F:33327,RG32F:33328,R8I:33329,R8UI:33330,R16I:33331,R16UI:33332,R32I:33333,R32UI:33334,RG8I:33335,RG8UI:33336,RG16I:33337,RG16UI:33338,RG32I:33339,RG32UI:33340,VERTEX_ARRAY_BINDING:34229,R8_SNORM:36756,RG8_SNORM:36757,RGB8_SNORM:36758,RGBA8_SNORM:36759,SIGNED_NORMALIZED:36764,COPY_READ_BUFFER:36662,COPY_WRITE_BUFFER:36663,COPY_READ_BUFFER_BINDING:36662,COPY_WRITE_BUFFER_BINDING:36663,UNIFORM_BUFFER:35345,UNIFORM_BUFFER_BINDING:35368,UNIFORM_BUFFER_START:35369,UNIFORM_BUFFER_SIZE:35370,MAX_VERTEX_UNIFORM_BLOCKS:35371,MAX_FRAGMENT_UNIFORM_BLOCKS:35373,MAX_COMBINED_UNIFORM_BLOCKS:35374,MAX_UNIFORM_BUFFER_BINDINGS:35375,MAX_UNIFORM_BLOCK_SIZE:35376,MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS:35377,MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS:35379,UNIFORM_BUFFER_OFFSET_ALIGNMENT:35380,ACTIVE_UNIFORM_BLOCKS:35382,UNIFORM_TYPE:35383,UNIFORM_SIZE:35384,UNIFORM_BLOCK_INDEX:35386,UNIFORM_OFFSET:35387,UNIFORM_ARRAY_STRIDE:35388,UNIFORM_MATRIX_STRIDE:35389,UNIFORM_IS_ROW_MAJOR:35390,UNIFORM_BLOCK_BINDING:35391,UNIFORM_BLOCK_DATA_SIZE:35392,UNIFORM_BLOCK_ACTIVE_UNIFORMS:35394,UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES:35395,UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER:35396,UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER:35398,INVALID_INDEX:4294967295,MAX_VERTEX_OUTPUT_COMPONENTS:37154,MAX_FRAGMENT_INPUT_COMPONENTS:37157,MAX_SERVER_WAIT_TIMEOUT:37137,OBJECT_TYPE:37138,SYNC_CONDITION:37139,SYNC_STATUS:37140,SYNC_FLAGS:37141,SYNC_FENCE:37142,SYNC_GPU_COMMANDS_COMPLETE:37143,UNSIGNALED:37144,SIGNALED:37145,ALREADY_SIGNALED:37146,TIMEOUT_EXPIRED:37147,CONDITION_SATISFIED:37148,WAIT_FAILED:37149,SYNC_FLUSH_COMMANDS_BIT:1,VERTEX_ATTRIB_ARRAY_DIVISOR:35070,ANY_SAMPLES_PASSED:35887,ANY_SAMPLES_PASSED_CONSERVATIVE:36202,SAMPLER_BINDING:35097,RGB10_A2UI:36975,INT_2_10_10_10_REV:36255,TRANSFORM_FEEDBACK:36386,TRANSFORM_FEEDBACK_PAUSED:36387,TRANSFORM_FEEDBACK_ACTIVE:36388,TRANSFORM_FEEDBACK_BINDING:36389,TEXTURE_IMMUTABLE_FORMAT:37167,MAX_ELEMENT_INDEX:36203,TEXTURE_IMMUTABLE_LEVELS:33503,TIMEOUT_IGNORED:-1,MAX_CLIENT_WAIT_TIMEOUT_WEBGL:37447,QUERY_COUNTER_BITS_EXT:34916,TIME_ELAPSED_EXT:35007,TIMESTAMP_EXT:36392,GPU_DISJOINT_EXT:36795,TEXTURE_MAX_ANISOTROPY_EXT:34046,MAX_TEXTURE_MAX_ANISOTROPY_EXT:34047,UNMASKED_VENDOR_WEBGL:37445,UNMASKED_RENDERER_WEBGL:37446,COMPLETION_STATUS_KHR:37297,COMPRESSED_RGB_S3TC_DXT1_EXT:33776,COMPRESSED_RGBA_S3TC_DXT1_EXT:33777,COMPRESSED_RGBA_S3TC_DXT3_EXT:33778,COMPRESSED_RGBA_S3TC_DXT5_EXT:33779,COMPRESSED_SRGB_S3TC_DXT1_EXT:35916,COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT:35917,COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT:35918,COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:35919,COMPRESSED_R11_EAC:37488,COMPRESSED_SIGNED_R11_EAC:37489,COMPRESSED_RG11_EAC:37490,COMPRESSED_SIGNED_RG11_EAC:37491,COMPRESSED_RGB8_ETC2:37492,COMPRESSED_SRGB8_ETC2:37493,COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2:37494,COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2:37495,COMPRESSED_RGBA8_ETC2_EAC:37496,COMPRESSED_SRGB8_ALPHA8_ETC2_EAC:37497,COMPRESSED_RGB_PVRTC_4BPPV1_IMG:35840,COMPRESSED_RGB_PVRTC_2BPPV1_IMG:35841,COMPRESSED_RGBA_PVRTC_4BPPV1_IMG:35842,COMPRESSED_RGBA_PVRTC_2BPPV1_IMG:35843,COMPRESSED_RGBA_ASTC_4x4_KHR:37808,COMPRESSED_RGBA_ASTC_5x4_KHR:37809,COMPRESSED_RGBA_ASTC_5x5_KHR:37810,COMPRESSED_RGBA_ASTC_6x5_KHR:37811,COMPRESSED_RGBA_ASTC_6x6_KHR:37812,COMPRESSED_RGBA_ASTC_8x5_KHR:37813,COMPRESSED_RGBA_ASTC_8x6_KHR:37814,COMPRESSED_RGBA_ASTC_8x8_KHR:37815,COMPRESSED_RGBA_ASTC_10x5_KHR:37816,COMPRESSED_RGBA_ASTC_10x6_KHR:37817,COMPRESSED_RGBA_ASTC_10x8_KHR:37818,COMPRESSED_RGBA_ASTC_10x10_KHR:37819,COMPRESSED_RGBA_ASTC_12x10_KHR:37820,COMPRESSED_RGBA_ASTC_12x12_KHR:37821,COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR:37840,COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR:37841,COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR:37842,COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR:37843,COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR:37844,COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR:37845,COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR:37846,COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR:37847,COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR:37848,COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR:37849,COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR:37850,COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR:37851,COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR:37852,COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR:37853};e.d=r;const s={[r.BYTE]:1,[r.UNSIGNED_BYTE]:1,[r.SHORT]:2,[r.UNSIGNED_SHORT]:2,[r.INT]:4,[r.UNSIGNED_INT]:4,[r.FLOAT]:4};e.g=s;const E={[r.R8]:[r.RED,r.UNSIGNED_BYTE],[r.R8_SNORM]:[r.RED,r.BYTE],[r.R16F]:[r.RED,r.FLOAT],[r.R32F]:[r.RED,r.FLOAT],[r.R8UI]:[r.RED_INTEGER,r.UNSIGNED_BYTE],[r.R8I]:[r.RED_INTEGER,r.BYTE],[r.R16UI]:[r.RED_INTEGER,r.UNSIGNED_SHORT],[r.R16I]:[r.RED_INTEGER,r.SHORT],[r.R32UI]:[r.RED_INTEGER,r.UNSIGNED_INT],[r.R32I]:[r.RED_INTEGER,r.INT],[r.RG8]:[r.RG,r.UNSIGNED_BYTE],[r.RG8_SNORM]:[r.RG,r.BYTE],[r.RG16F]:[r.RG,r.FLOAT],[r.RG32F]:[r.RG,r.FLOAT],[r.RG8UI]:[r.RG_INTEGER,r.UNSIGNED_BYTE],[r.RG8I]:[r.RG_INTEGER,r.BYTE],[r.RG16UI]:[r.RG_INTEGER,r.UNSIGNED_SHORT],[r.RG16I]:[r.RG_INTEGER,r.SHORT],[r.RG32UI]:[r.RG_INTEGER,r.UNSIGNED_INT],[r.RG32I]:[r.RG_INTEGER,r.INT],[r.RGB8]:[r.RGB,r.UNSIGNED_BYTE],[r.SRGB8]:[r.RGB,r.UNSIGNED_BYTE],[r.RGB565]:[r.RGB,r.UNSIGNED_SHORT_5_6_5],[r.RGB8_SNORM]:[r.RGB,r.BYTE],[r.R11F_G11F_B10F]:[r.RGB,r.UNSIGNED_INT_10F_11F_11F_REV],[r.RGB9_E5]:[r.RGB,r.UNSIGNED_INT_5_9_9_9_REV],[r.RGB16F]:[r.RGB,r.FLOAT],[r.RGB32F]:[r.RGB,r.FLOAT],[r.RGB8UI]:[r.RGB_INTEGER,r.UNSIGNED_BYTE],[r.RGB8I]:[r.RGB_INTEGER,r.BYTE],[r.RGB16UI]:[r.RGB_INTEGER,r.UNSIGNED_SHORT],[r.RGB16I]:[r.RGB_INTEGER,r.SHORT],[r.RGB32UI]:[r.RGB_INTEGER,r.UNSIGNED_INT],[r.RGB32I]:[r.RGB_INTEGER,r.INT],[r.RGBA8]:[r.RGBA,r.UNSIGNED_BYTE],[r.SRGB8_ALPHA8]:[r.RGBA,r.UNSIGNED_BYTE],[r.RGBA8_SNORM]:[r.RGBA,r.BYTE],[r.RGB5_A1]:[r.RGBA,r.UNSIGNED_SHORT_5_5_5_1],[r.RGBA4]:[r.RGBA,r.UNSIGNED_SHORT_4_4_4_4],[r.RGB10_A2]:[r.RGBA,r.UNSIGNED_INT_2_10_10_10_REV],[r.RGBA16F]:[r.RGBA,r.FLOAT],[r.RGBA32F]:[r.RGBA,r.FLOAT],[r.RGBA8UI]:[r.RGBA_INTEGER,r.UNSIGNED_BYTE],[r.RGBA8I]:[r.RGBA_INTEGER,r.BYTE],[r.RGB10_A2UI]:[r.RGBA_INTEGER,r.UNSIGNED_INT_2_10_10_10_REV],[r.RGBA16UI]:[r.RGBA_INTEGER,r.UNSIGNED_SHORT],[r.RGBA16I]:[r.RGBA_INTEGER,r.SHORT],[r.RGBA32I]:[r.RGBA_INTEGER,r.INT],[r.RGBA32UI]:[r.RGBA_INTEGER,r.UNSIGNED_INT],[r.DEPTH_COMPONENT16]:[r.DEPTH_COMPONENT,r.UNSIGNED_SHORT],[r.DEPTH_COMPONENT24]:[r.DEPTH_COMPONENT,r.UNSIGNED_INT],[r.DEPTH_COMPONENT32F]:[r.DEPTH_COMPONENT,r.FLOAT],[r.DEPTH24_STENCIL8]:[r.DEPTH_STENCIL,r.UNSIGNED_INT_24_8],[r.DEPTH32F_STENCIL8]:[r.DEPTH_STENCIL,r.FLOAT_32_UNSIGNED_INT_24_8_REV]};e.e=E;const _={[r.COMPRESSED_RGB_S3TC_DXT1_EXT]:!0,[r.COMPRESSED_RGBA_S3TC_DXT1_EXT]:!0,[r.COMPRESSED_RGBA_S3TC_DXT3_EXT]:!0,[r.COMPRESSED_RGBA_S3TC_DXT5_EXT]:!0,[r.COMPRESSED_SRGB_S3TC_DXT1_EXT]:!0,[r.COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT]:!0,[r.COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT]:!0,[r.COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT]:!0,[r.COMPRESSED_R11_EAC]:!0,[r.COMPRESSED_SIGNED_R11_EAC]:!0,[r.COMPRESSED_RG11_EAC]:!0,[r.COMPRESSED_SIGNED_RG11_EAC]:!0,[r.COMPRESSED_RGB8_ETC2]:!0,[r.COMPRESSED_SRGB8_ETC2]:!0,[r.COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2]:!0,[r.COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2]:!0,[r.COMPRESSED_RGBA8_ETC2_EAC]:!0,[r.COMPRESSED_SRGB8_ALPHA8_ETC2_EAC]:!0,[r.COMPRESSED_RGBA_ASTC_4x4_KHR]:!0,[r.COMPRESSED_RGBA_ASTC_5x4_KHR]:!0,[r.COMPRESSED_RGBA_ASTC_5x5_KHR]:!0,[r.COMPRESSED_RGBA_ASTC_6x5_KHR]:!0,[r.COMPRESSED_RGBA_ASTC_6x6_KHR]:!0,[r.COMPRESSED_RGBA_ASTC_8x5_KHR]:!0,[r.COMPRESSED_RGBA_ASTC_8x6_KHR]:!0,[r.COMPRESSED_RGBA_ASTC_8x8_KHR]:!0,[r.COMPRESSED_RGBA_ASTC_10x5_KHR]:!0,[r.COMPRESSED_RGBA_ASTC_10x6_KHR]:!0,[r.COMPRESSED_RGBA_ASTC_10x8_KHR]:!0,[r.COMPRESSED_RGBA_ASTC_10x10_KHR]:!0,[r.COMPRESSED_RGBA_ASTC_12x10_KHR]:!0,[r.COMPRESSED_RGBA_ASTC_12x12_KHR]:!0,[r.COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR]:!0,[r.COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR]:!0,[r.COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR]:!0,[r.COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR]:!0,[r.COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR]:!0,[r.COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR]:!0,[r.COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR]:!0,[r.COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR]:!0,[r.COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR]:!0,[r.COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR]:!0,[r.COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR]:!0,[r.COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR]:!0,[r.COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR]:!0,[r.COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR]:!0,[r.COMPRESSED_RGB_PVRTC_4BPPV1_IMG]:!0,[r.COMPRESSED_RGB_PVRTC_2BPPV1_IMG]:!0,[r.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG]:!0,[r.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG]:!0};e.a=_;e.h={};const h=new Array(1);e.c=h;e.b={};const a={[r.UNSIGNED_BYTE]:{[r.RED]:r.R8,[r.RG]:r.RG8,[r.RGB]:r.RGB8,[r.RGBA]:r.RGBA8},[r.UNSIGNED_SHORT]:{[r.DEPTH_COMPONENT]:r.DEPTH_COMPONENT16},[r.FLOAT]:{[r.RED]:r.R16F,[r.RG]:r.RG16F,[r.RGB]:r.RGB16F,[r.RGBA]:r.RGBA16F,[r.DEPTH_COMPONENT]:r.DEPTH_COMPONENT32F}};e.f=a},function(t,e,i){"use strict";var r=i(0);e.a=class{constructor(t,e,i,s,E=s.width,_=s.height,h,a,R=r.b){if(this.gl=t,this.binding=i,this.texture=null,this.width=E||0,this.height=_||0,this.depth=h||0,this.is3D=a,this.appState=e,this.compressed=Boolean(r.a[R.internalFormat]),void 0!==R.format&&(console.warn("Texture option 'format' is deprecated and will be removed. Use 'internalFormat' with a sized format instead."),this.compressed=Boolean(r.a[R.format]),void 0===R.type&&(R.type=R.format===r.d.DEPTH_COMPONENT?r.d.UNSIGNED_SHORT:r.d.UNSIGNED_BYTE),void 0===R.internalFormat&&(this.compressed?R.internalFormat=R.format:R.internalFormat=r.f[R.type][R.format])),this.compressed)this.internalFormat=R.internalFormat,this.format=this.internalFormat,this.type=r.d.UNSIGNED_BYTE;else{this.internalFormat=void 0!==R.internalFormat?R.internalFormat:r.d.RGBA8;let t=r.e[this.internalFormat];this.format=t[0],this.type=void 0!==R.type?R.type:t[1]}this.currentUnit=-1;let{minFilter:T=(s?r.d.LINEAR_MIPMAP_NEAREST:r.d.NEAREST),magFilter:A=(s?r.d.LINEAR:r.d.NEAREST),wrapS:n=r.d.REPEAT,wrapT:S=r.d.REPEAT,wrapR:l=r.d.REPEAT,compareMode:u=r.d.NONE,compareFunc:N=r.d.LEQUAL,minLOD:d=null,maxLOD:o=null,baseLevel:I=null,maxLevel:F=null,maxAnisotropy:M=1,flipY:O=!1,premultiplyAlpha:C=!1}=R;this.minFilter=T,this.magFilter=A,this.wrapS=n,this.wrapT=S,this.wrapR=l,this.compareMode=u,this.compareFunc=N,this.minLOD=d,this.maxLOD=o,this.baseLevel=I,this.maxLevel=F,this.maxAnisotropy=Math.min(M,r.h.MAX_TEXTURE_ANISOTROPY),this.flipY=O,this.premultiplyAlpha=C,this.mipmaps=T===r.d.LINEAR_MIPMAP_NEAREST||T===r.d.LINEAR_MIPMAP_LINEAR,this.restore(s)}restore(t){return this.texture=null,this.resize(this.width,this.height,this.depth),t&&this.data(t),this}resize(t,e,i){if(i=i||0,this.texture&&t===this.width&&e===this.height&&i===this.depth)return this;let s;return this.gl.deleteTexture(this.texture),-1!==this.currentUnit&&(this.appState.textures[this.currentUnit]=null),this.texture=this.gl.createTexture(),this.bind(Math.max(this.currentUnit,0)),this.width=t,this.height=e,this.depth=i,this.gl.texParameteri(this.binding,r.d.TEXTURE_MIN_FILTER,this.minFilter),this.gl.texParameteri(this.binding,r.d.TEXTURE_MAG_FILTER,this.magFilter),this.gl.texParameteri(this.binding,r.d.TEXTURE_WRAP_S,this.wrapS),this.gl.texParameteri(this.binding,r.d.TEXTURE_WRAP_T,this.wrapT),this.gl.texParameteri(this.binding,r.d.TEXTURE_WRAP_R,this.wrapR),this.gl.texParameteri(this.binding,r.d.TEXTURE_COMPARE_FUNC,this.compareFunc),this.gl.texParameteri(this.binding,r.d.TEXTURE_COMPARE_MODE,this.compareMode),this.gl.pixelStorei(r.d.UNPACK_FLIP_Y_WEBGL,this.flipY),this.gl.pixelStorei(r.d.UNPACK_PREMULTIPLY_ALPHA_WEBGL,this.premultiplyAlpha),null!==this.minLOD&&this.gl.texParameterf(this.binding,r.d.TEXTURE_MIN_LOD,this.minLOD),null!==this.maxLOD&&this.gl.texParameterf(this.binding,r.d.TEXTURE_MAX_LOD,this.maxLOD),null!==this.baseLevel&&this.gl.texParameteri(this.binding,r.d.TEXTURE_BASE_LEVEL,this.baseLevel),null!==this.maxLevel&&this.gl.texParameteri(this.binding,r.d.TEXTURE_MAX_LEVEL,this.maxLevel),this.maxAnisotropy>1&&this.gl.texParameteri(this.binding,r.d.TEXTURE_MAX_ANISOTROPY_EXT,this.maxAnisotropy),this.is3D?(s=this.mipmaps?Math.floor(Math.log2(Math.max(Math.max(this.width,this.height),this.depth)))+1:1,this.gl.texStorage3D(this.binding,s,this.internalFormat,this.width,this.height,this.depth)):(s=this.mipmaps?Math.floor(Math.log2(Math.max(this.width,this.height)))+1:1,this.gl.texStorage2D(this.binding,s,this.internalFormat,this.width,this.height)),this}data(t){Array.isArray(t)||(r.c[0]=t,t=r.c);let e,i=this.mipmaps?t.length:1,s=this.width,E=this.height,_=this.depth,h=this.mipmaps&&1===t.length;if(this.bind(Math.max(this.currentUnit,0)),this.compressed)if(this.is3D)for(e=0;e>1,1),E=Math.max(E>>1,1),_=Math.max(_>>1,1);else for(e=0;e>1,1),E=Math.max(E>>1,1);else if(this.is3D)for(e=0;e>1,1),E=Math.max(E>>1,1),_=Math.max(_>>1,1);else for(e=0;e>1,1),E=Math.max(E>>1,1);return h&&this.gl.generateMipmap(this.binding),this}delete(){return this.texture&&(this.gl.deleteTexture(this.texture),this.texture=null,-1!==this.currentUnit&&this.appState.textures[this.currentUnit]===this&&(this.appState.textures[this.currentUnit]=null,this.currentUnit=-1)),this}bind(t){let e=this.appState.textures[t];return e!==this&&(e&&(e.currentUnit=-1),-1!==this.currentUnit&&(this.appState.textures[this.currentUnit]=null),this.gl.activeTexture(r.d.TEXTURE0+t),this.gl.bindTexture(this.binding,this.texture),this.appState.textures[t]=this,this.currentUnit=t),this}}},function(t,e,i){"use strict";var r=i(0);e.a=class{constructor(t,e,i,r,s=0){this.gl=t,this.renderbuffer=null,this.width=e,this.height=i,this.internalFormat=r,this.samples=s,this.restore()}restore(){return this.renderbuffer=this.gl.createRenderbuffer(),this.resize(this.width,this.height),this}resize(t,e){return this.width=t,this.height=e,this.gl.bindRenderbuffer(r.d.RENDERBUFFER,this.renderbuffer),this.gl.renderbufferStorageMultisample(r.d.RENDERBUFFER,this.samples,this.internalFormat,this.width,this.height),this.gl.bindRenderbuffer(r.d.RENDERBUFFER,null),this}delete(){return this.gl.deleteRenderbuffer(this.renderbuffer),this.renderbuffer=null,this}}},function(t,e,i){"use strict";var r=i(0);e.a=class{constructor(t,e,i,r){this.gl=t,this.appState=e,this.shader=null,this.type=i,this.source=r,this.restore()}restore(){return this.shader=this.gl.createShader(this.type),this.gl.shaderSource(this.shader,this.source),this.gl.compileShader(this.shader),this}translatedSource(){return r.h.DEBUG_SHADERS?this.appState.extensions.debugShaders.getTranslatedShaderSource(this.shader):"(Unavailable)"}delete(){return this.shader&&(this.gl.deleteShader(this.shader),this.shader=null),this}checkCompilation(){if(!this.gl.getShaderParameter(this.shader,r.d.COMPILE_STATUS)){let t,e;for(console.error(this.gl.getShaderInfoLog(this.shader)),e=this.source.split("\n"),t=0;t{t.preventDefault()}),this.canvas.addEventListener("webglcontextrestored",()=>{this.initExtensions(),this.contextRestoredHandler&&this.contextRestoredHandler()})}loseContext(){return this.contextLostExt&&this.contextLostExt.loseContext(),this}restoreContext(){return this.contextLostExt&&this.contextLostExt.restoreContext(),this}onContextRestored(t){return this.contextRestoredHandler=t,this}colorMask(t,e,i,r){return this.gl.colorMask(t,e,i,r),this}clearColor(t,e,i,r){return this.gl.clearColor(t,e,i,r),this}clearMask(t){return this.clearBits=t,this}clear(){return this.gl.clear(this.clearBits),this}drawFramebuffer(t){return t.bindForDraw(),this}readFramebuffer(t){return t.bindForRead(),this}defaultDrawFramebuffer(){return null!==this.state.drawFramebuffer&&(this.gl.bindFramebuffer(this.gl.DRAW_FRAMEBUFFER,null),this.state.drawFramebuffer=null),this}defaultReadFramebuffer(){return null!==this.state.readFramebuffer&&(this.gl.bindFramebuffer(this.gl.READ_FRAMEBUFFER,null),this.state.readFramebuffer=null),this}blitFramebuffer(t,e=r.b){let i=this.state.readFramebuffer,s=this.state.drawFramebuffer,E=i?i.width:this.width,_=i?i.height:this.height,h=s?s.width:this.width,a=s?s.height:this.height,{srcStartX:R=0,srcStartY:T=0,srcEndX:A=E,srcEndY:n=_,dstStartX:S=0,dstStartY:l=0,dstEndX:u=h,dstEndY:N=a,filter:d=r.d.NEAREST}=e;return this.gl.blitFramebuffer(R,T,A,n,S,l,u,N,t,d),this}depthRange(t,e){return this.gl.depthRange(t,e),this}depthTest(){return this.gl.enable(this.gl.DEPTH_TEST),this}noDepthTest(){return this.gl.disable(this.gl.DEPTH_TEST),this}depthMask(t){return this.gl.depthMask(t),this}depthFunc(t){return this.gl.depthFunc(t),this}blend(){return this.gl.enable(this.gl.BLEND),this}noBlend(){return this.gl.disable(this.gl.BLEND),this}blendFunc(t,e){return this.gl.blendFunc(t,e),this}blendFuncSeparate(t,e,i,r){return this.gl.blendFuncSeparate(t,e,i,r),this}stencilTest(){return this.gl.enable(this.gl.STENCIL_TEST),this}noStencilTest(){return this.gl.disable(this.gl.STENCIL_TEST),this}scissorTest(){return this.gl.enable(this.gl.SCISSOR_TEST),this}noScissorTest(){return this.gl.disable(this.gl.SCISSOR_TEST),this}scissor(t,e,i,r){return this.gl.scissor(t,e,i,r),this}stencilMask(t){return this.gl.stencilMask(t),this}stencilMaskSeparate(t,e){return this.gl.stencilMaskSeparate(t,e),this}stencilFunc(t,e,i){return this.gl.stencilFunc(t,e,i),this}stencilFuncSeparate(t,e,i,r){return this.gl.stencilFuncSeparate(t,e,i,r),this}stencilOp(t,e,i){return this.gl.stencilOp(t,e,i),this}stencilOpSeparate(t,e,i,r){return this.gl.stencilOpSeparate(t,e,i,r),this}rasterize(){return this.gl.disable(this.gl.RASTERIZER_DISCARD),this}noRasterize(){return this.gl.enable(this.gl.RASTERIZER_DISCARD),this}cullBackfaces(){return this.gl.enable(this.gl.CULL_FACE),this}drawBackfaces(){return this.gl.disable(this.gl.CULL_FACE),this}readPixel(t,e,i,s=r.b){let{format:E=r.d.RGBA,type:_=r.d.UNSIGNED_BYTE}=s;return this.gl.readPixels(t,e,1,1,E,_,i),this}viewport(t,e,i,r){return this.viewportWidth===i&&this.viewportHeight===r&&this.viewportX===t&&this.viewportY===e||(this.viewportX=t,this.viewportY=e,this.viewportWidth=i,this.viewportHeight=r,this.gl.viewport(t,e,this.viewportWidth,this.viewportHeight)),this}defaultViewport(){return this.viewport(0,0,this.width,this.height),this}resize(t,e){return this.canvas.width=t,this.canvas.height=e,this.width=this.gl.drawingBufferWidth,this.height=this.gl.drawingBufferHeight,this.viewport(0,0,this.width,this.height),this}createProgram(t,e,i){return new a.a(this.gl,this.state,t,e,i).link().checkLinkage()}createPrograms(...t){return new Promise((e,i)=>{let r=t.length,s=new Array(r),E=new Array(r),_=r;for(let e=0;e{let t=0;for(let e=0;e<_;++e)if(E[e].checkCompletion()){if(E[e].checkLinkage(),!E[e].linked)return void i(new Error("Program linkage failed"));++t}else E[e-t]=E[e];0==(_-=t)?e(s):requestAnimationFrame(h)};h()})}restorePrograms(...t){return new Promise((e,i)=>{let r=t.length,s=t.slice(),E=r;for(let e=0;e{let t=0;for(let e=0;e1,this.levels=this.mipmaps?Math.floor(Math.log2(Math.min(this.width,this.height)))+1:1,this.restore(i)}restore(t=r.b){this.texture=this.gl.createTexture(),-1!==this.currentUnit&&(this.appState.textures[this.currentUnit]=null),this.bind(0),this.gl.pixelStorei(r.d.UNPACK_FLIP_Y_WEBGL,this.flipY),this.gl.pixelStorei(r.d.UNPACK_PREMULTIPLY_ALPHA_WEBGL,this.premultiplyAlpha),this.gl.texParameteri(r.d.TEXTURE_CUBE_MAP,r.d.TEXTURE_MAG_FILTER,this.magFilter),this.gl.texParameteri(r.d.TEXTURE_CUBE_MAP,r.d.TEXTURE_MIN_FILTER,this.minFilter),this.gl.texParameteri(r.d.TEXTURE_CUBE_MAP,r.d.TEXTURE_WRAP_S,this.wrapS),this.gl.texParameteri(r.d.TEXTURE_CUBE_MAP,r.d.TEXTURE_WRAP_T,this.wrapT),this.gl.texParameteri(r.d.TEXTURE_CUBE_MAP,r.d.TEXTURE_COMPARE_FUNC,this.compareFunc),this.gl.texParameteri(r.d.TEXTURE_CUBE_MAP,r.d.TEXTURE_COMPARE_MODE,this.compareMode),null!==this.baseLevel&&this.gl.texParameteri(r.d.TEXTURE_CUBE_MAP,r.d.TEXTURE_BASE_LEVEL,this.baseLevel),null!==this.maxLevel&&this.gl.texParameteri(r.d.TEXTURE_CUBE_MAP,r.d.TEXTURE_MAX_LEVEL,this.maxLevel),null!==this.minLOD&&this.gl.texParameteri(r.d.TEXTURE_CUBE_MAP,r.d.TEXTURE_MIN_LOD,this.minLOD),null!==this.maxLOD&&this.gl.texParameteri(r.d.TEXTURE_CUBE_MAP,r.d.TEXTURE_MAX_LOD,this.maxLOD),this.maxAnisotropy>1&&this.gl.texParameteri(r.d.TEXTURE_CUBE_MAP,r.d.TEXTURE_MAX_ANISOTROPY_EXT,this.maxAnisotropy),this.gl.texStorage2D(r.d.TEXTURE_CUBE_MAP,this.levels,this.internalFormat,this.width,this.height);let{negX:e,posX:i,negY:s,posY:E,negZ:_,posZ:h}=t;return e&&(this.faceData(r.d.TEXTURE_CUBE_MAP_NEGATIVE_X,e),this.faceData(r.d.TEXTURE_CUBE_MAP_POSITIVE_X,i),this.faceData(r.d.TEXTURE_CUBE_MAP_NEGATIVE_Y,s),this.faceData(r.d.TEXTURE_CUBE_MAP_POSITIVE_Y,E),this.faceData(r.d.TEXTURE_CUBE_MAP_NEGATIVE_Z,_),this.faceData(r.d.TEXTURE_CUBE_MAP_POSITIVE_Z,h)),this.mipmaps&&!this.miplevelsProvided&&this.gl.generateMipmap(r.d.TEXTURE_CUBE_MAP),this}delete(){return this.texture&&(this.gl.deleteTexture(this.texture),this.texture=null,this.appState.textures[this.currentUnit]=null,this.currentUnit=-1),this}faceData(t,e){Array.isArray(e)||(r.c[0]=e,e=r.c);let i,s=this.mipmaps?e.length:1,E=this.width,_=this.height;if(this.compressed)for(i=0;i>1,1),_=Math.max(_>>1,1);else for(i=0;i>1,1),_=Math.max(_>>1,1);return this}bind(t){let e=this.appState.textures[t];return e!==this&&(e&&(e.currentUnit=-1),-1!==this.currentUnit&&(this.appState.textures[this.currentUnit]=null),this.gl.activeTexture(r.d.TEXTURE0+t),this.gl.bindTexture(r.d.TEXTURE_CUBE_MAP,this.texture),this.appState.textures[t]=this,this.currentUnit=t),this}}},function(t,e,i){"use strict";var r=i(0);e.a=class{constructor(t,e,i,s=null,E){this.gl=t,this.currentProgram=i,this.drawPrimitive=r.d.TRIANGLES,this.currentVertexArray=s,this.currentTransformFeedback=null,this.appState=e,this.uniformIndices={},this.uniformNames=new Array(r.h.MAX_UNIFORMS),this.uniformValues=new Array(r.h.MAX_UNIFORMS),this.uniformCount=0,this.uniformBuffers=new Array(r.h.MAX_UNIFORM_BUFFERS),this.uniformBlockNames=new Array(r.h.MAX_UNIFORM_BUFFERS),this.uniformBlockCount=0,this.textures=new Array(r.h.MAX_TEXTURE_UNITS),this.textureCount=0,this.offsets=new Int32Array(1),this.numElements=new Int32Array(1),this.numInstances=new Int32Array(1),this.currentVertexArray&&(this.numElements[0]=this.currentVertexArray.numElements,this.numInstances[0]=this.currentVertexArray.numInstances),this.numDraws=1,void 0!==E&&(console.warn("Primitive argument to 'App.createDrawCall' is deprecated and will be removed. Use 'DrawCall.primitive' instead."),this.primitive(E))}primitive(t){return this.drawPrimitive=t,this}transformFeedback(t){return this.currentTransformFeedback=t,this}uniform(t,e){let i=this.uniformIndices[t];return void 0===i&&(i=this.uniformCount++,this.uniformIndices[t]=i,this.uniformNames[i]=t),this.uniformValues[i]=e,this}texture(t,e){let i=this.currentProgram.samplers[t];return this.textures[i]=e,this}uniformBlock(t,e){let i=this.currentProgram.uniformBlocks[t];return this.uniformBuffers[i]=e,this}drawRanges(...t){this.numDraws=t.length,this.offsets.length=this.numColorTargets){let e=t+1;this.colorAttachmentEnums.length=e,this.colorAttachments.length=e,this.colorAttachmentTargets.length=e;for(let t=this.numColorTargets;t1?E.c:E.d;break;case r.d.BOOL:_=a>1?E.b:E.d;break;case r.d.FLOAT_VEC2:case r.d.INT_VEC2:case r.d.UNSIGNED_INT_VEC2:case r.d.FLOAT_VEC3:case r.d.INT_VEC3:case r.d.UNSIGNED_INT_VEC3:case r.d.FLOAT_VEC4:case r.d.INT_VEC4:case r.d.UNSIGNED_INT_VEC4:_=E.c;break;case r.d.BOOL_VEC2:case r.d.BOOL_VEC3:case r.d.BOOL_VEC4:_=E.b;break;case r.d.FLOAT_MAT2:case r.d.FLOAT_MAT3:case r.d.FLOAT_MAT4:case r.d.FLOAT_MAT2x3:case r.d.FLOAT_MAT2x4:case r.d.FLOAT_MAT3x2:case r.d.FLOAT_MAT3x4:case r.d.FLOAT_MAT4x2:case r.d.FLOAT_MAT4x3:_=E.a;break;default:console.error("Unrecognized type for uniform ",e.name)}_&&(this.uniforms[e.name]=new _(this.gl,s,h,a))}let i=this.gl.getProgramParameter(this.program,r.d.ACTIVE_UNIFORM_BLOCKS);for(let t=0;t=this.dirtyEnd)return this;let t=this.data.subarray(this.dirtyStart,this.dirtyEnd),e=4*this.dirtyStart;return this.gl.bindBuffer(this.gl.UNIFORM_BUFFER,this.buffer),this.gl.bufferSubData(this.gl.UNIFORM_BUFFER,e,t),this.gl.bindBuffer(this.gl.UNIFORM_BUFFER,null),this.dirtyStart=this.size,this.dirtyEnd=0,this}delete(){return this.buffer&&(this.gl.deleteBuffer(this.buffer),this.buffer=null,-1!==this.currentBase&&this.appState.uniformBuffers[this.currentBase]===this&&(this.appState.uniformBuffers[this.currentBase]=null)),this}bind(t){let e=this.appState.uniformBuffers[t];return e!==this&&(e&&(e.currentBase=-1),-1!==this.currentBase&&(this.appState.uniformBuffers[this.currentBase]=null),this.gl.bindBufferBase(this.gl.UNIFORM_BUFFER,t,this.buffer),this.appState.uniformBuffers[t]=this,this.currentBase=t),this}}},function(t,e,i){"use strict";var r=i(0);e.a=class{constructor(t,e){this.gl=t,this.appState=e,this.vertexArray=null,this.indexType=null,this.indexed=!1,this.numElements=0,this.numInstances=1,this.offsets=0,this.numDraws=1}restore(){return this.appState.vertexArray===this&&(this.appState.vertexArray=null),null!==this.vertexArray&&(this.vertexArray=this.gl.createVertexArray()),this}vertexAttributeBuffer(t,e,i=r.b){return this.attributeBuffer(t,e,i,!1),this}instanceAttributeBuffer(t,e,i=r.b){return this.attributeBuffer(t,e,i,!0),this}indexBuffer(t){return null===this.vertexArray&&(this.vertexArray=this.gl.createVertexArray()),this.bind(),this.gl.bindBuffer(r.d.ELEMENT_ARRAY_BUFFER,t.buffer),this.numElements=3*t.numItems,this.indexType=t.type,this.indexed=!0,this}delete(){return this.vertexArray&&(this.gl.deleteVertexArray(this.vertexArray),this.vertexArray=null,this.appState.vertexArray===this&&(this.gl.bindVertexArray(null),this.appState.vertexArray=null)),this}bind(){return this.appState.vertexArray!==this&&(this.gl.bindVertexArray(this.vertexArray),this.appState.vertexArray=this),this}attributeBuffer(t,e,i={},s){null===this.vertexArray&&(this.vertexArray=this.gl.createVertexArray()),this.bind(),this.gl.bindBuffer(r.d.ARRAY_BUFFER,e.buffer);let{type:E=e.type,size:_=e.itemSize,stride:h=0,offset:a=0,normalized:R=!1,integer:T=Boolean(e.integer&&!R)}=i,A=e.numColumns;0===h&&(h=A*_*r.g[E]);for(let e=0;e1,this.levels=this.mipmaps?Math.floor(Math.log2(Math.min(this.width,this.height)))+1:1,this.restore(i)}restore(t=R){this.texture=this.gl.createTexture(),-1!==this.currentUnit&&(this.appState.textures[this.currentUnit]=null),this.bind(0),this.gl.pixelStorei(r.UNPACK_FLIP_Y_WEBGL,this.flipY),this.gl.pixelStorei(r.UNPACK_PREMULTIPLY_ALPHA_WEBGL,this.premultiplyAlpha),this.gl.texParameteri(r.TEXTURE_CUBE_MAP,r.TEXTURE_MAG_FILTER,this.magFilter),this.gl.texParameteri(r.TEXTURE_CUBE_MAP,r.TEXTURE_MIN_FILTER,this.minFilter),this.gl.texParameteri(r.TEXTURE_CUBE_MAP,r.TEXTURE_WRAP_S,this.wrapS),this.gl.texParameteri(r.TEXTURE_CUBE_MAP,r.TEXTURE_WRAP_T,this.wrapT),this.gl.texParameteri(r.TEXTURE_CUBE_MAP,r.TEXTURE_COMPARE_FUNC,this.compareFunc),this.gl.texParameteri(r.TEXTURE_CUBE_MAP,r.TEXTURE_COMPARE_MODE,this.compareMode),null!==this.baseLevel&&this.gl.texParameteri(r.TEXTURE_CUBE_MAP,r.TEXTURE_BASE_LEVEL,this.baseLevel),null!==this.maxLevel&&this.gl.texParameteri(r.TEXTURE_CUBE_MAP,r.TEXTURE_MAX_LEVEL,this.maxLevel),null!==this.minLOD&&this.gl.texParameteri(r.TEXTURE_CUBE_MAP,r.TEXTURE_MIN_LOD,this.minLOD),null!==this.maxLOD&&this.gl.texParameteri(r.TEXTURE_CUBE_MAP,r.TEXTURE_MAX_LOD,this.maxLOD),this.maxAnisotropy>1&&this.gl.texParameteri(r.TEXTURE_CUBE_MAP,r.TEXTURE_MAX_ANISOTROPY_EXT,this.maxAnisotropy),this.gl.texStorage2D(r.TEXTURE_CUBE_MAP,this.levels,this.internalFormat,this.width,this.height);let{negX:e,posX:i,negY:s,posY:E,negZ:_,posZ:h}=t;return e&&(this.faceData(r.TEXTURE_CUBE_MAP_NEGATIVE_X,e),this.faceData(r.TEXTURE_CUBE_MAP_POSITIVE_X,i),this.faceData(r.TEXTURE_CUBE_MAP_NEGATIVE_Y,s),this.faceData(r.TEXTURE_CUBE_MAP_POSITIVE_Y,E),this.faceData(r.TEXTURE_CUBE_MAP_NEGATIVE_Z,_),this.faceData(r.TEXTURE_CUBE_MAP_POSITIVE_Z,h)),this.mipmaps&&!this.miplevelsProvided&&this.gl.generateMipmap(r.TEXTURE_CUBE_MAP),this}delete(){return this.texture&&(this.gl.deleteTexture(this.texture),this.texture=null,this.appState.textures[this.currentUnit]=null,this.currentUnit=-1),this}faceData(t,e){Array.isArray(e)||(T[0]=e,e=T);let i,r=this.mipmaps?e.length:1,s=this.width,E=this.height;if(this.compressed)for(i=0;i>1,1),E=Math.max(E>>1,1);else for(i=0;i>1,1),E=Math.max(E>>1,1);return this}bind(t){let e=this.appState.textures[t];return e!==this&&(e&&(e.currentUnit=-1),-1!==this.currentUnit&&(this.appState.textures[this.currentUnit]=null),this.gl.activeTexture(r.TEXTURE0+t),this.gl.bindTexture(r.TEXTURE_CUBE_MAP,this.texture),this.appState.textures[t]=this,this.currentUnit=t),this}}class n{constructor(t,e,i,s=null,E){this.gl=t,this.currentProgram=i,this.drawPrimitive=r.TRIANGLES,this.currentVertexArray=s,this.currentTransformFeedback=null,this.appState=e,this.uniformIndices={},this.uniformNames=new Array(h.MAX_UNIFORMS),this.uniformValues=new Array(h.MAX_UNIFORMS),this.uniformCount=0,this.uniformBuffers=new Array(h.MAX_UNIFORM_BUFFERS),this.uniformBlockNames=new Array(h.MAX_UNIFORM_BUFFERS),this.uniformBlockCount=0,this.textures=new Array(h.MAX_TEXTURE_UNITS),this.textureCount=0,this.offsets=new Int32Array(1),this.numElements=new Int32Array(1),this.numInstances=new Int32Array(1),this.currentVertexArray&&(this.numElements[0]=this.currentVertexArray.numElements,this.numInstances[0]=this.currentVertexArray.numInstances),this.numDraws=1,void 0!==E&&(console.warn("Primitive argument to 'App.createDrawCall' is deprecated and will be removed. Use 'DrawCall.primitive' instead."),this.primitive(E))}primitive(t){return this.drawPrimitive=t,this}transformFeedback(t){return this.currentTransformFeedback=t,this}uniform(t,e){let i=this.uniformIndices[t];return void 0===i&&(i=this.uniformCount++,this.uniformIndices[t]=i,this.uniformNames[i]=t),this.uniformValues[i]=e,this}texture(t,e){let i=this.currentProgram.samplers[t];return this.textures[i]=e,this}uniformBlock(t,e){let i=this.currentProgram.uniformBlocks[t];return this.uniformBuffers[i]=e,this}drawRanges(...t){this.numDraws=t.length,this.offsets.length1&&this.gl.texParameteri(this.binding,r.TEXTURE_MAX_ANISOTROPY_EXT,this.maxAnisotropy),this.is3D?(s=this.mipmaps?Math.floor(Math.log2(Math.max(Math.max(this.width,this.height),this.depth)))+1:1,this.gl.texStorage3D(this.binding,s,this.internalFormat,this.width,this.height,this.depth)):(s=this.mipmaps?Math.floor(Math.log2(Math.max(this.width,this.height)))+1:1,this.gl.texStorage2D(this.binding,s,this.internalFormat,this.width,this.height)),this}data(t){Array.isArray(t)||(T[0]=t,t=T);let e,i=this.mipmaps?t.length:1,r=this.width,s=this.height,E=this.depth,_=this.mipmaps&&1===t.length;if(this.bind(Math.max(this.currentUnit,0)),this.compressed)if(this.is3D)for(e=0;e>1,1),s=Math.max(s>>1,1),E=Math.max(E>>1,1);else for(e=0;e>1,1),s=Math.max(s>>1,1);else if(this.is3D)for(e=0;e>1,1),s=Math.max(s>>1,1),E=Math.max(E>>1,1);else for(e=0;e>1,1),s=Math.max(s>>1,1);return _&&this.gl.generateMipmap(this.binding),this}delete(){return this.texture&&(this.gl.deleteTexture(this.texture),this.texture=null,-1!==this.currentUnit&&this.appState.textures[this.currentUnit]===this&&(this.appState.textures[this.currentUnit]=null,this.currentUnit=-1)),this}bind(t){let e=this.appState.textures[t];return e!==this&&(e&&(e.currentUnit=-1),-1!==this.currentUnit&&(this.appState.textures[this.currentUnit]=null),this.gl.activeTexture(r.TEXTURE0+t),this.gl.bindTexture(this.binding,this.texture),this.appState.textures[t]=this,this.currentUnit=t),this}}class l{constructor(t,e,i,r,s=0){this.gl=t,this.renderbuffer=null,this.width=e,this.height=i,this.internalFormat=r,this.samples=s,this.restore()}restore(){return this.renderbuffer=this.gl.createRenderbuffer(),this.resize(this.width,this.height),this}resize(t,e){return this.width=t,this.height=e,this.gl.bindRenderbuffer(r.RENDERBUFFER,this.renderbuffer),this.gl.renderbufferStorageMultisample(r.RENDERBUFFER,this.samples,this.internalFormat,this.width,this.height),this.gl.bindRenderbuffer(r.RENDERBUFFER,null),this}delete(){return this.gl.deleteRenderbuffer(this.renderbuffer),this.renderbuffer=null,this}}class N{constructor(t,e){this.gl=t,this.framebuffer=null,this.appState=e,this.numColorTargets=0,this.colorAttachments=[],this.colorAttachmentEnums=[],this.colorAttachmentTargets=[],this.depthAttachment=null,this.depthAttachmentTarget=null,this.width=0,this.height=0,this.restore()}restore(){return this.appState.drawFramebuffer===this&&(this.appState.drawFramebuffer=null),this.appState.readFramebuffer===this&&(this.appState.readFramebuffer=null),this.framebuffer=this.gl.createFramebuffer(),this}colorTarget(t,e,i=(e.is3D?0:r.TEXTURE_2D)){if(t>=this.numColorTargets){let e=t+1;this.colorAttachmentEnums.length=e,this.colorAttachments.length=e,this.colorAttachmentTargets.length=e;for(let t=this.numColorTargets;t1?O:M;break;case r.BOOL:E=h>1?m:M;break;case r.FLOAT_VEC2:case r.INT_VEC2:case r.UNSIGNED_INT_VEC2:case r.FLOAT_VEC3:case r.INT_VEC3:case r.UNSIGNED_INT_VEC3:case r.FLOAT_VEC4:case r.INT_VEC4:case r.UNSIGNED_INT_VEC4:E=O;break;case r.BOOL_VEC2:case r.BOOL_VEC3:case r.BOOL_VEC4:E=m;break;case r.FLOAT_MAT2:case r.FLOAT_MAT3:case r.FLOAT_MAT4:case r.FLOAT_MAT2x3:case r.FLOAT_MAT2x4:case r.FLOAT_MAT3x2:case r.FLOAT_MAT3x4:case r.FLOAT_MAT4x2:case r.FLOAT_MAT4x3:E=C;break;default:console.error("Unrecognized type for uniform ",e.name)}E&&(this.uniforms[e.name]=new E(this.gl,s,_,h))}let i=this.gl.getProgramParameter(this.program,r.ACTIVE_UNIFORM_BLOCKS);for(let t=0;t=this.dirtyEnd)return this;let t=this.data.subarray(this.dirtyStart,this.dirtyEnd),e=4*this.dirtyStart;return this.gl.bindBuffer(this.gl.UNIFORM_BUFFER,this.buffer),this.gl.bufferSubData(this.gl.UNIFORM_BUFFER,e,t),this.gl.bindBuffer(this.gl.UNIFORM_BUFFER,null),this.dirtyStart=this.size,this.dirtyEnd=0,this}delete(){return this.buffer&&(this.gl.deleteBuffer(this.buffer),this.buffer=null,-1!==this.currentBase&&this.appState.uniformBuffers[this.currentBase]===this&&(this.appState.uniformBuffers[this.currentBase]=null)),this}bind(t){let e=this.appState.uniformBuffers[t];return e!==this&&(e&&(e.currentBase=-1),-1!==this.currentBase&&(this.appState.uniformBuffers[this.currentBase]=null),this.gl.bindBufferBase(this.gl.UNIFORM_BUFFER,t,this.buffer),this.appState.uniformBuffers[t]=this,this.currentBase=t),this}}class L{constructor(t,e){this.gl=t,this.appState=e,this.vertexArray=null,this.indexType=null,this.indexed=!1,this.numElements=0,this.numInstances=1,this.offsets=0,this.numDraws=1}restore(){return this.appState.vertexArray===this&&(this.appState.vertexArray=null),null!==this.vertexArray&&(this.vertexArray=this.gl.createVertexArray()),this}vertexAttributeBuffer(t,e,i=R){return this.attributeBuffer(t,e,i,!1),this}instanceAttributeBuffer(t,e,i=R){return this.attributeBuffer(t,e,i,!0),this}indexBuffer(t){return null===this.vertexArray&&(this.vertexArray=this.gl.createVertexArray()),this.bind(),this.gl.bindBuffer(r.ELEMENT_ARRAY_BUFFER,t.buffer),this.numElements=3*t.numItems,this.indexType=t.type,this.indexed=!0,this}delete(){return this.vertexArray&&(this.gl.deleteVertexArray(this.vertexArray),this.vertexArray=null,this.appState.vertexArray===this&&(this.gl.bindVertexArray(null),this.appState.vertexArray=null)),this}bind(){return this.appState.vertexArray!==this&&(this.gl.bindVertexArray(this.vertexArray),this.appState.vertexArray=this),this}attributeBuffer(t,e,i={},E){null===this.vertexArray&&(this.vertexArray=this.gl.createVertexArray()),this.bind(),this.gl.bindBuffer(r.ARRAY_BUFFER,e.buffer);let{type:_=e.type,size:h=e.itemSize,stride:T=0,offset:R=0,normalized:a=!1,integer:A=Boolean(e.integer&&!a)}=i,n=e.numColumns;0===T&&(T=n*h*s[_]);for(let e=0;e{t.preventDefault()}),this.canvas.addEventListener("webglcontextrestored",()=>{this.initExtensions(),this.contextRestoredHandler&&this.contextRestoredHandler()})}loseContext(){return this.contextLostExt&&this.contextLostExt.loseContext(),this}restoreContext(){return this.contextLostExt&&this.contextLostExt.restoreContext(),this}onContextRestored(t){return this.contextRestoredHandler=t,this}colorMask(t,e,i,r){return this.gl.colorMask(t,e,i,r),this}clearColor(t,e,i,r){return this.gl.clearColor(t,e,i,r),this}clearMask(t){return this.clearBits=t,this}clear(){return this.gl.clear(this.clearBits),this}drawFramebuffer(t){return t.bindForDraw(),this}readFramebuffer(t){return t.bindForRead(),this}defaultDrawFramebuffer(){return null!==this.state.drawFramebuffer&&(this.gl.bindFramebuffer(this.gl.DRAW_FRAMEBUFFER,null),this.state.drawFramebuffer=null),this}defaultReadFramebuffer(){return null!==this.state.readFramebuffer&&(this.gl.bindFramebuffer(this.gl.READ_FRAMEBUFFER,null),this.state.readFramebuffer=null),this}blitFramebuffer(t,e=R){let i=this.state.readFramebuffer,s=this.state.drawFramebuffer,E=i?i.width:this.width,_=i?i.height:this.height,h=s?s.width:this.width,T=s?s.height:this.height,{srcStartX:a=0,srcStartY:A=0,srcEndX:n=E,srcEndY:S=_,dstStartX:l=0,dstStartY:N=0,dstEndX:u=h,dstEndY:o=T,filter:I=r.NEAREST}=e;return this.gl.blitFramebuffer(a,A,n,S,l,N,u,o,t,I),this}depthRange(t,e){return this.gl.depthRange(t,e),this}depthTest(){return this.gl.enable(this.gl.DEPTH_TEST),this}noDepthTest(){return this.gl.disable(this.gl.DEPTH_TEST),this}depthMask(t){return this.gl.depthMask(t),this}depthFunc(t){return this.gl.depthFunc(t),this}blend(){return this.gl.enable(this.gl.BLEND),this}noBlend(){return this.gl.disable(this.gl.BLEND),this}blendFunc(t,e){return this.gl.blendFunc(t,e),this}blendFuncSeparate(t,e,i,r){return this.gl.blendFuncSeparate(t,e,i,r),this}stencilTest(){return this.gl.enable(this.gl.STENCIL_TEST),this}noStencilTest(){return this.gl.disable(this.gl.STENCIL_TEST),this}scissorTest(){return this.gl.enable(this.gl.SCISSOR_TEST),this}noScissorTest(){return this.gl.disable(this.gl.SCISSOR_TEST),this}scissor(t,e,i,r){return this.gl.scissor(t,e,i,r),this}stencilMask(t){return this.gl.stencilMask(t),this}stencilMaskSeparate(t,e){return this.gl.stencilMaskSeparate(t,e),this}stencilFunc(t,e,i){return this.gl.stencilFunc(t,e,i),this}stencilFuncSeparate(t,e,i,r){return this.gl.stencilFuncSeparate(t,e,i,r),this}stencilOp(t,e,i){return this.gl.stencilOp(t,e,i),this}stencilOpSeparate(t,e,i,r){return this.gl.stencilOpSeparate(t,e,i,r),this}rasterize(){return this.gl.disable(this.gl.RASTERIZER_DISCARD),this}noRasterize(){return this.gl.enable(this.gl.RASTERIZER_DISCARD),this}cullBackfaces(){return this.gl.enable(this.gl.CULL_FACE),this}drawBackfaces(){return this.gl.disable(this.gl.CULL_FACE),this}readPixel(t,e,i,s=R){let{format:E=r.RGBA,type:_=r.UNSIGNED_BYTE}=s;return this.gl.readPixels(t,e,1,1,E,_,i),this}viewport(t,e,i,r){return this.viewportWidth===i&&this.viewportHeight===r&&this.viewportX===t&&this.viewportY===e||(this.viewportX=t,this.viewportY=e,this.viewportWidth=i,this.viewportHeight=r,this.gl.viewport(t,e,this.viewportWidth,this.viewportHeight)),this}defaultViewport(){return this.viewport(0,0,this.width,this.height),this}resize(t,e){return this.canvas.width=t,this.canvas.height=e,this.width=this.gl.drawingBufferWidth,this.height=this.gl.drawingBufferHeight,this.viewport(0,0,this.width,this.height),this}createProgram(t,e,i){return new D(this.gl,this.state,t,e,i).link().checkLinkage()}createPrograms(...t){return new Promise((e,i)=>{let r=t.length,s=new Array(r),E=new Array(r),_=r;for(let e=0;e{let t=0;for(let e=0;e<_;++e)if(E[e].checkCompletion()){if(E[e].checkLinkage(),!E[e].linked)return void i(new Error("Program linkage failed"));++t}else E[e-t]=E[e];0===(_-=t)?e(s):requestAnimationFrame(h)};h()})}restorePrograms(...t){return new Promise((e,i)=>{let r=t.length,s=t.slice(),E=r;for(let e=0;e{let t=0;for(let e=0;e