Skip to content

Instantly share code, notes, and snippets.

@robertmassman
Created June 16, 2023 22:16
Show Gist options
  • Save robertmassman/80f3379d0d7333ff546681cedf17eac4 to your computer and use it in GitHub Desktop.
Save robertmassman/80f3379d0d7333ff546681cedf17eac4 to your computer and use it in GitHub Desktop.
[Bug]: unexpected 4 and 16 bit image results using electron 21.4.1 and newer #37152
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
<meta http-equiv="Content-Security-Policy" content="default-src *;
img-src * 'self' blob: data: data: https:; script-src 'self' *;
style-src 'self' 'unsafe-inline' *">
<title>Hello World!</title>
</head>
<body>
<div class="Container">
<div class="Menu-Container">
<div class="group" id="Image-Group">
<div class="hold-image">
<div class="image-alignment">
<img src="" id="reference-image" alt="">
</div>
</div>
<label for="selectImageDropdown">Images: </label>
<select class="menuBar" id="selectImageDropdown"></select>
</div>
<div class="group" id="Bit-Depth-Group">
<label for="bitDepthDropdown">Bit Depth: </label>
<select class="menuBar" id="bitDepthDropdown"></select>
</div>
<div id="Sequencer-Container" class="Sequencer-Container">
<div class="slidecontainer" id="slideContainer">
<div class="inputBlock">
<p>Gaussian Blur: <span id="GaussianAmount"></span></p>
<p class="helpTip">Controls the radius of the gaussian blur.</p>
<label for="GaussianSlider">Gaussian Blur: </label>
<input type="range" min="0" max="1000" value="900" class="slider" id="GaussianSlider">
</div>
</div>
</div>
</div>
<div class="Filter-UI-Container">
<div id="webgl-canvas-container" class="IMAGE" >
<canvas id="webgl-canvas"></canvas>
</div>
</div>
</div>
<img id="testImage" src="https://fastly.picsum.photos/id/972/500/500.jpg?hmac=EOMk-JjJvw1q19X1yyDoUZIIUZJFl1wWEKz9z4D5YVk" alt="building" width="500" height="500">
<!-- You can also require other files to run in this process -->
<script src="./renderer.js"></script>
</body>
</html>
// Modules to control application life and create native browser window
const { app, BrowserWindow } = require('electron')
const path = require('path')
function createWindow () {
// Create the browser window.
const mainWindow = new BrowserWindow({
width: 1400,
height: 1000,
webPreferences: {
preload: path.join(__dirname, 'preload.js')
}
})
// and load the index.html of the app.
mainWindow.loadFile('index.html')
// Open the DevTools.
mainWindow.webContents.openDevTools()
}
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.whenReady().then(() => {
createWindow()
app.on('activate', function () {
// On macOS it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
})
// Quit when all windows are closed, except on macOS. There, it's common
// for applications and their menu bar to stay active until the user quits
// explicitly with Cmd + Q.
app.on('window-all-closed', function () {
if (process.platform !== 'darwin') app.quit()
})
// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.
{
"name": "electronFiddle",
"productName": "electronFiddle",
"description": "My Electron application description",
"keywords": [],
"main": "./main.js",
"version": "1.0.0",
"author": "robertmassman",
"scripts": {
"start": "electron ."
},
"dependencies": {},
"devDependencies": {
"electron": "17.4.11"
}
}
/**
* The preload script runs before. It has access to web APIs
* as well as Electron's renderer process modules and some
* polyfilled Node.js functions.
*
* https://www.electronjs.org/docs/latest/tutorial/sandbox
*/
window.addEventListener('DOMContentLoaded', () => {
const replaceText = (selector, text) => {
const element = document.getElementById(selector)
if (element) element.innerText = text
}
for (const type of ['chrome', 'node', 'electron']) {
replaceText(`${type}-version`, process.versions[type])
}
})
(function (factory) {
typeof define === 'function' && define.amd ? define(factory) :
factory();
})((function () { 'use strict';
///////////////////////////////////////////////////////////////////////////////////
// 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
const GL = {
/**
@type {GLenum}
@name PicoGL.DEPTH_BUFFER_BIT
@private
*/
DEPTH_BUFFER_BIT: 0x00000100,
/**
@type {GLenum}
@name PicoGL.STENCIL_BUFFER_BIT
@private
*/
STENCIL_BUFFER_BIT: 0x00000400,
/**
@type {GLenum}
@name PicoGL.COLOR_BUFFER_BIT
@private
*/
COLOR_BUFFER_BIT: 0x00004000,
/**
@type {GLenum}
@name PicoGL.POINTS
@private
*/
POINTS: 0x0000,
/**
@type {GLenum}
@name PicoGL.LINES
@private
*/
LINES: 0x0001,
/**
@type {GLenum}
@name PicoGL.LINE_LOOP
@private
*/
LINE_LOOP: 0x0002,
/**
@type {GLenum}
@name PicoGL.LINE_STRIP
@private
*/
LINE_STRIP: 0x0003,
/**
@type {GLenum}
@name PicoGL.TRIANGLES
@private
*/
TRIANGLES: 0x0004,
/**
@type {GLenum}
@name PicoGL.TRIANGLE_STRIP
@private
*/
TRIANGLE_STRIP: 0x0005,
/**
@type {GLenum}
@name PicoGL.TRIANGLE_FAN
@private
*/
TRIANGLE_FAN: 0x0006,
/**
@type {GLenum}
@name PicoGL.ZERO
@private
*/
ZERO: 0,
/**
@type {GLenum}
@name PicoGL.ONE
@private
*/
ONE: 1,
/**
@type {GLenum}
@name PicoGL.SRC_COLOR
@private
*/
SRC_COLOR: 0x0300,
/**
@type {GLenum}
@name PicoGL.ONE_MINUS_SRC_COLOR
@private
*/
ONE_MINUS_SRC_COLOR: 0x0301,
/**
@type {GLenum}
@name PicoGL.SRC_ALPHA
@private
*/
SRC_ALPHA: 0x0302,
/**
@type {GLenum}
@name PicoGL.ONE_MINUS_SRC_ALPHA
@private
*/
ONE_MINUS_SRC_ALPHA: 0x0303,
/**
@type {GLenum}
@name PicoGL.DST_ALPHA
@private
*/
DST_ALPHA: 0x0304,
/**
@type {GLenum}
@name PicoGL.ONE_MINUS_DST_ALPHA
@private
*/
ONE_MINUS_DST_ALPHA: 0x0305,
/**
@type {GLenum}
@name PicoGL.DST_COLOR
@private
*/
DST_COLOR: 0x0306,
/**
@type {GLenum}
@name PicoGL.ONE_MINUS_DST_COLOR
@private
*/
ONE_MINUS_DST_COLOR: 0x0307,
/**
@type {GLenum}
@name PicoGL.SRC_ALPHA_SATURATE
@private
*/
SRC_ALPHA_SATURATE: 0x0308,
/**
@type {GLenum}
@name PicoGL.FUNC_ADD
@private
*/
FUNC_ADD: 0x8006,
/**
@type {GLenum}
@name PicoGL.BLEND_EQUATION
@private
*/
BLEND_EQUATION: 0x8009,
/**
@type {GLenum}
@name PicoGL.BLEND_EQUATION_RGB
@private
*/
BLEND_EQUATION_RGB: 0x8009,
/**
@type {GLenum}
@name PicoGL.BLEND_EQUATION_ALPHA
@private
*/
BLEND_EQUATION_ALPHA: 0x883D,
/**
@type {GLenum}
@name PicoGL.FUNC_SUBTRACT
@private
*/
FUNC_SUBTRACT: 0x800A,
/**
@type {GLenum}
@name PicoGL.FUNC_REVERSE_SUBTRACT
@private
*/
FUNC_REVERSE_SUBTRACT: 0x800B,
/**
@type {GLenum}
@name PicoGL.BLEND_DST_RGB
@private
*/
BLEND_DST_RGB: 0x80C8,
/**
@type {GLenum}
@name PicoGL.BLEND_SRC_RGB
@private
*/
BLEND_SRC_RGB: 0x80C9,
/**
@type {GLenum}
@name PicoGL.BLEND_DST_ALPHA
@private
*/
BLEND_DST_ALPHA: 0x80CA,
/**
@type {GLenum}
@name PicoGL.BLEND_SRC_ALPHA
@private
*/
BLEND_SRC_ALPHA: 0x80CB,
/**
@type {GLenum}
@name PicoGL.CONSTANT_COLOR
@private
*/
CONSTANT_COLOR: 0x8001,
/**
@type {GLenum}
@name PicoGL.ONE_MINUS_CONSTANT_COLOR
@private
*/
ONE_MINUS_CONSTANT_COLOR: 0x8002,
/**
@type {GLenum}
@name PicoGL.CONSTANT_ALPHA
@private
*/
CONSTANT_ALPHA: 0x8003,
/**
@type {GLenum}
@name PicoGL.ONE_MINUS_CONSTANT_ALPHA
@private
*/
ONE_MINUS_CONSTANT_ALPHA: 0x8004,
/**
@type {GLenum}
@name PicoGL.BLEND_COLOR
@private
*/
BLEND_COLOR: 0x8005,
/**
@type {GLenum}
@name PicoGL.ARRAY_BUFFER
@private
*/
ARRAY_BUFFER: 0x8892,
/**
@type {GLenum}
@name PicoGL.ELEMENT_ARRAY_BUFFER
@private
*/
ELEMENT_ARRAY_BUFFER: 0x8893,
/**
@type {GLenum}
@name PicoGL.ARRAY_BUFFER_BINDING
@private
*/
ARRAY_BUFFER_BINDING: 0x8894,
/**
@type {GLenum}
@name PicoGL.ELEMENT_ARRAY_BUFFER_BINDING
@private
*/
ELEMENT_ARRAY_BUFFER_BINDING: 0x8895,
/**
@type {GLenum}
@name PicoGL.STREAM_DRAW
@private
*/
STREAM_DRAW: 0x88E0,
/**
@type {GLenum}
@name PicoGL.STATIC_DRAW
@private
*/
STATIC_DRAW: 0x88E4,
/**
@type {GLenum}
@name PicoGL.DYNAMIC_DRAW
@private
*/
DYNAMIC_DRAW: 0x88E8,
/**
@type {GLenum}
@name PicoGL.BUFFER_SIZE
@private
*/
BUFFER_SIZE: 0x8764,
/**
@type {GLenum}
@name PicoGL.BUFFER_USAGE
@private
*/
BUFFER_USAGE: 0x8765,
/**
@type {GLenum}
@name PicoGL.CURRENT_VERTEX_ATTRIB
@private
*/
CURRENT_VERTEX_ATTRIB: 0x8626,
/**
@type {GLenum}
@name PicoGL.FRONT
@private
*/
FRONT: 0x0404,
/**
@type {GLenum}
@name PicoGL.BACK
@private
*/
BACK: 0x0405,
/**
@type {GLenum}
@name PicoGL.FRONT_AND_BACK
@private
*/
FRONT_AND_BACK: 0x0408,
/**
@type {GLenum}
@name PicoGL.CULL_FACE
@private
*/
CULL_FACE: 0x0B44,
/**
@type {GLenum}
@name PicoGL.BLEND
@private
*/
BLEND: 0x0BE2,
/**
@type {GLenum}
@name PicoGL.DITHER
@private
*/
DITHER: 0x0BD0,
/**
@type {GLenum}
@name PicoGL.STENCIL_TEST
@private
*/
STENCIL_TEST: 0x0B90,
/**
@type {GLenum}
@name PicoGL.DEPTH_TEST
@private
*/
DEPTH_TEST: 0x0B71,
/**
@type {GLenum}
@name PicoGL.SCISSOR_TEST
@private
*/
SCISSOR_TEST: 0x0C11,
/**
@type {GLenum}
@name PicoGL.POLYGON_OFFSET_FILL
@private
*/
POLYGON_OFFSET_FILL: 0x8037,
/**
@type {GLenum}
@name PicoGL.SAMPLE_ALPHA_TO_COVERAGE
@private
*/
SAMPLE_ALPHA_TO_COVERAGE: 0x809E,
/**
@type {GLenum}
@name PicoGL.SAMPLE_COVERAGE
@private
*/
SAMPLE_COVERAGE: 0x80A0,
/**
@type {GLenum}
@name PicoGL.NO_ERROR
@private
*/
NO_ERROR: 0,
/**
@type {GLenum}
@name PicoGL.INVALID_ENUM
@private
*/
INVALID_ENUM: 0x0500,
/**
@type {GLenum}
@name PicoGL.INVALID_VALUE
@private
*/
INVALID_VALUE: 0x0501,
/**
@type {GLenum}
@name PicoGL.INVALID_OPERATION
@private
*/
INVALID_OPERATION: 0x0502,
/**
@type {GLenum}
@name PicoGL.OUT_OF_MEMORY
@private
*/
OUT_OF_MEMORY: 0x0505,
/**
@type {GLenum}
@name PicoGL.CW
@private
*/
CW: 0x0900,
/**
@type {GLenum}
@name PicoGL.CCW
@private
*/
CCW: 0x0901,
/**
@type {GLenum}
@name PicoGL.LINE_WIDTH
@private
*/
LINE_WIDTH: 0x0B21,
/**
@type {GLenum}
@name PicoGL.ALIASED_POINT_SIZE_RANGE
@private
*/
ALIASED_POINT_SIZE_RANGE: 0x846D,
/**
@type {GLenum}
@name PicoGL.ALIASED_LINE_WIDTH_RANGE
@private
*/
ALIASED_LINE_WIDTH_RANGE: 0x846E,
/**
@type {GLenum}
@name PicoGL.CULL_FACE_MODE
@private
*/
CULL_FACE_MODE: 0x0B45,
/**
@type {GLenum}
@name PicoGL.FRONT_FACE
@private
*/
FRONT_FACE: 0x0B46,
/**
@type {GLenum}
@name PicoGL.DEPTH_RANGE
@private
*/
DEPTH_RANGE: 0x0B70,
/**
@type {GLenum}
@name PicoGL.DEPTH_WRITEMASK
@private
*/
DEPTH_WRITEMASK: 0x0B72,
/**
@type {GLenum}
@name PicoGL.DEPTH_CLEAR_VALUE
@private
*/
DEPTH_CLEAR_VALUE: 0x0B73,
/**
@type {GLenum}
@name PicoGL.DEPTH_FUNC
@private
*/
DEPTH_FUNC: 0x0B74,
/**
@type {GLenum}
@name PicoGL.STENCIL_CLEAR_VALUE
@private
*/
STENCIL_CLEAR_VALUE: 0x0B91,
/**
@type {GLenum}
@name PicoGL.STENCIL_FUNC
@private
*/
STENCIL_FUNC: 0x0B92,
/**
@type {GLenum}
@name PicoGL.STENCIL_FAIL
@private
*/
STENCIL_FAIL: 0x0B94,
/**
@type {GLenum}
@name PicoGL.STENCIL_PASS_DEPTH_FAIL
@private
*/
STENCIL_PASS_DEPTH_FAIL: 0x0B95,
/**
@type {GLenum}
@name PicoGL.STENCIL_PASS_DEPTH_PASS
@private
*/
STENCIL_PASS_DEPTH_PASS: 0x0B96,
/**
@type {GLenum}
@name PicoGL.STENCIL_REF
@private
*/
STENCIL_REF: 0x0B97,
/**
@type {GLenum}
@name PicoGL.STENCIL_VALUE_MASK
@private
*/
STENCIL_VALUE_MASK: 0x0B93,
/**
@type {GLenum}
@name PicoGL.STENCIL_WRITEMASK
@private
*/
STENCIL_WRITEMASK: 0x0B98,
/**
@type {GLenum}
@name PicoGL.STENCIL_BACK_FUNC
@private
*/
STENCIL_BACK_FUNC: 0x8800,
/**
@type {GLenum}
@name PicoGL.STENCIL_BACK_FAIL
@private
*/
STENCIL_BACK_FAIL: 0x8801,
/**
@type {GLenum}
@name PicoGL.STENCIL_BACK_PASS_DEPTH_FAIL
@private
*/
STENCIL_BACK_PASS_DEPTH_FAIL: 0x8802,
/**
@type {GLenum}
@name PicoGL.STENCIL_BACK_PASS_DEPTH_PASS
@private
*/
STENCIL_BACK_PASS_DEPTH_PASS: 0x8803,
/**
@type {GLenum}
@name PicoGL.STENCIL_BACK_REF
@private
*/
STENCIL_BACK_REF: 0x8CA3,
/**
@type {GLenum}
@name PicoGL.STENCIL_BACK_VALUE_MASK
@private
*/
STENCIL_BACK_VALUE_MASK: 0x8CA4,
/**
@type {GLenum}
@name PicoGL.STENCIL_BACK_WRITEMASK
@private
*/
STENCIL_BACK_WRITEMASK: 0x8CA5,
/**
@type {GLenum}
@name PicoGL.VIEWPORT
@private
*/
VIEWPORT: 0x0BA2,
/**
@type {GLenum}
@name PicoGL.SCISSOR_BOX
@private
*/
SCISSOR_BOX: 0x0C10,
/**
@type {GLenum}
@name PicoGL.COLOR_CLEAR_VALUE
@private
*/
COLOR_CLEAR_VALUE: 0x0C22,
/**
@type {GLenum}
@name PicoGL.COLOR_WRITEMASK
@private
*/
COLOR_WRITEMASK: 0x0C23,
/**
@type {GLenum}
@name PicoGL.UNPACK_ALIGNMENT
@private
*/
UNPACK_ALIGNMENT: 0x0CF5,
/**
@type {GLenum}
@name PicoGL.PACK_ALIGNMENT
@private
*/
PACK_ALIGNMENT: 0x0D05,
/**
@type {GLenum}
@name PicoGL.MAX_TEXTURE_SIZE
@private
*/
MAX_TEXTURE_SIZE: 0x0D33,
/**
@type {GLenum}
@name PicoGL.MAX_VIEWPORT_DIMS
@private
*/
MAX_VIEWPORT_DIMS: 0x0D3A,
/**
@type {GLenum}
@name PicoGL.SUBPIXEL_BITS
@private
*/
SUBPIXEL_BITS: 0x0D50,
/**
@type {GLenum}
@name PicoGL.RED_BITS
@private
*/
RED_BITS: 0x0D52,
/**
@type {GLenum}
@name PicoGL.GREEN_BITS
@private
*/
GREEN_BITS: 0x0D53,
/**
@type {GLenum}
@name PicoGL.BLUE_BITS
@private
*/
BLUE_BITS: 0x0D54,
/**
@type {GLenum}
@name PicoGL.ALPHA_BITS
@private
*/
ALPHA_BITS: 0x0D55,
/**
@type {GLenum}
@name PicoGL.DEPTH_BITS
@private
*/
DEPTH_BITS: 0x0D56,
/**
@type {GLenum}
@name PicoGL.STENCIL_BITS
@private
*/
STENCIL_BITS: 0x0D57,
/**
@type {GLenum}
@name PicoGL.POLYGON_OFFSET_UNITS
@private
*/
POLYGON_OFFSET_UNITS: 0x2A00,
/**
@type {GLenum}
@name PicoGL.POLYGON_OFFSET_FACTOR
@private
*/
POLYGON_OFFSET_FACTOR: 0x8038,
/**
@type {GLenum}
@name PicoGL.TEXTURE_BINDING_2D
@private
*/
TEXTURE_BINDING_2D: 0x8069,
/**
@type {GLenum}
@name PicoGL.SAMPLE_BUFFERS
@private
*/
SAMPLE_BUFFERS: 0x80A8,
/**
@type {GLenum}
@name PicoGL.SAMPLES
@private
*/
SAMPLES: 0x80A9,
/**
@type {GLenum}
@name PicoGL.SAMPLE_COVERAGE_VALUE
@private
*/
SAMPLE_COVERAGE_VALUE: 0x80AA,
/**
@type {GLenum}
@name PicoGL.SAMPLE_COVERAGE_INVERT
@private
*/
SAMPLE_COVERAGE_INVERT: 0x80AB,
/**
@type {GLenum}
@name PicoGL.COMPRESSED_TEXTURE_FORMATS
@private
*/
COMPRESSED_TEXTURE_FORMATS: 0x86A3,
/**
@type {GLenum}
@name PicoGL.DONT_CARE
@private
*/
DONT_CARE: 0x1100,
/**
@type {GLenum}
@name PicoGL.FASTEST
@private
*/
FASTEST: 0x1101,
/**
@type {GLenum}
@name PicoGL.NICEST
@private
*/
NICEST: 0x1102,
/**
@type {GLenum}
@name PicoGL.GENERATE_MIPMAP_HINT
@private
*/
GENERATE_MIPMAP_HINT: 0x8192,
/**
@type {GLenum}
@name PicoGL.BYTE
@private
*/
BYTE: 0x1400,
/**
@type {GLenum}
@name PicoGL.UNSIGNED_BYTE
@private
*/
UNSIGNED_BYTE: 0x1401,
/**
@type {GLenum}
@name PicoGL.SHORT
@private
*/
SHORT: 0x1402,
/**
@type {GLenum}
@name PicoGL.UNSIGNED_SHORT
@private
*/
UNSIGNED_SHORT: 0x1403,
/**
@type {GLenum}
@name PicoGL.INT
@private
*/
INT: 0x1404,
/**
@type {GLenum}
@name PicoGL.UNSIGNED_INT
@private
*/
UNSIGNED_INT: 0x1405,
/**
@type {GLenum}
@name PicoGL.FLOAT
@private
*/
FLOAT: 0x1406,
/**
@type {GLenum}
@name PicoGL.DEPTH_COMPONENT
@private
*/
DEPTH_COMPONENT: 0x1902,
/**
@type {GLenum}
@name PicoGL.ALPHA
@private
*/
ALPHA: 0x1906,
/**
@type {GLenum}
@name PicoGL.RGB
@private
*/
RGB: 0x1907,
/**
@type {GLenum}
@name PicoGL.RGBA
@private
*/
RGBA: 0x1908,
/**
@type {GLenum}
@name PicoGL.LUMINANCE
@private
*/
LUMINANCE: 0x1909,
/**
@type {GLenum}
@name PicoGL.LUMINANCE_ALPHA
@private
*/
LUMINANCE_ALPHA: 0x190A,
/**
@type {GLenum}
@name PicoGL.UNSIGNED_SHORT_4_4_4_4
@private
*/
UNSIGNED_SHORT_4_4_4_4: 0x8033,
/**
@type {GLenum}
@name PicoGL.UNSIGNED_SHORT_5_5_5_1
@private
*/
UNSIGNED_SHORT_5_5_5_1: 0x8034,
/**
@type {GLenum}
@name PicoGL.UNSIGNED_SHORT_5_6_5
@private
*/
UNSIGNED_SHORT_5_6_5: 0x8363,
/**
@type {GLenum}
@name PicoGL.FRAGMENT_SHADER
@private
*/
FRAGMENT_SHADER: 0x8B30,
/**
@type {GLenum}
@name PicoGL.VERTEX_SHADER
@private
*/
VERTEX_SHADER: 0x8B31,
/**
@type {GLenum}
@name PicoGL.MAX_VERTEX_ATTRIBS
@private
*/
MAX_VERTEX_ATTRIBS: 0x8869,
/**
@type {GLenum}
@name PicoGL.MAX_VERTEX_UNIFORM_VECTORS
@private
*/
MAX_VERTEX_UNIFORM_VECTORS: 0x8DFB,
/**
@type {GLenum}
@name PicoGL.MAX_VARYING_VECTORS
@private
*/
MAX_VARYING_VECTORS: 0x8DFC,
/**
@type {GLenum}
@name PicoGL.MAX_COMBINED_TEXTURE_IMAGE_UNITS
@private
*/
MAX_COMBINED_TEXTURE_IMAGE_UNITS: 0x8B4D,
/**
@type {GLenum}
@name PicoGL.MAX_VERTEX_TEXTURE_IMAGE_UNITS
@private
*/
MAX_VERTEX_TEXTURE_IMAGE_UNITS: 0x8B4C,
/**
@type {GLenum}
@name PicoGL.MAX_TEXTURE_IMAGE_UNITS
@private
*/
MAX_TEXTURE_IMAGE_UNITS: 0x8872,
/**
@type {GLenum}
@name PicoGL.MAX_FRAGMENT_UNIFORM_VECTORS
@private
*/
MAX_FRAGMENT_UNIFORM_VECTORS: 0x8DFD,
/**
@type {GLenum}
@name PicoGL.SHADER_TYPE
@private
*/
SHADER_TYPE: 0x8B4F,
/**
@type {GLenum}
@name PicoGL.DELETE_STATUS
@private
*/
DELETE_STATUS: 0x8B80,
/**
@type {GLenum}
@name PicoGL.LINK_STATUS
@private
*/
LINK_STATUS: 0x8B82,
/**
@type {GLenum}
@name PicoGL.VALIDATE_STATUS
@private
*/
VALIDATE_STATUS: 0x8B83,
/**
@type {GLenum}
@name PicoGL.ATTACHED_SHADERS
@private
*/
ATTACHED_SHADERS: 0x8B85,
/**
@type {GLenum}
@name PicoGL.ACTIVE_UNIFORMS
@private
*/
ACTIVE_UNIFORMS: 0x8B86,
/**
@type {GLenum}
@name PicoGL.ACTIVE_ATTRIBUTES
@private
*/
ACTIVE_ATTRIBUTES: 0x8B89,
/**
@type {GLenum}
@name PicoGL.SHADING_LANGUAGE_VERSION
@private
*/
SHADING_LANGUAGE_VERSION: 0x8B8C,
/**
@type {GLenum}
@name PicoGL.CURRENT_PROGRAM
@private
*/
CURRENT_PROGRAM: 0x8B8D,
/**
@type {GLenum}
@name PicoGL.NEVER
@private
*/
NEVER: 0x0200,
/**
@type {GLenum}
@name PicoGL.LESS
@private
*/
LESS: 0x0201,
/**
@type {GLenum}
@name PicoGL.EQUAL
@private
*/
EQUAL: 0x0202,
/**
@type {GLenum}
@name PicoGL.LEQUAL
@private
*/
LEQUAL: 0x0203,
/**
@type {GLenum}
@name PicoGL.GREATER
@private
*/
GREATER: 0x0204,
/**
@type {GLenum}
@name PicoGL.NOTEQUAL
@private
*/
NOTEQUAL: 0x0205,
/**
@type {GLenum}
@name PicoGL.GEQUAL
@private
*/
GEQUAL: 0x0206,
/**
@type {GLenum}
@name PicoGL.ALWAYS
@private
*/
ALWAYS: 0x0207,
/**
@type {GLenum}
@name PicoGL.KEEP
@private
*/
KEEP: 0x1E00,
/**
@type {GLenum}
@name PicoGL.REPLACE
@private
*/
REPLACE: 0x1E01,
/**
@type {GLenum}
@name PicoGL.INCR
@private
*/
INCR: 0x1E02,
/**
@type {GLenum}
@name PicoGL.DECR
@private
*/
DECR: 0x1E03,
/**
@type {GLenum}
@name PicoGL.INVERT
@private
*/
INVERT: 0x150A,
/**
@type {GLenum}
@name PicoGL.INCR_WRAP
@private
*/
INCR_WRAP: 0x8507,
/**
@type {GLenum}
@name PicoGL.DECR_WRAP
@private
*/
DECR_WRAP: 0x8508,
/**
@type {GLenum}
@name PicoGL.VENDOR
@private
*/
VENDOR: 0x1F00,
/**
@type {GLenum}
@name PicoGL.RENDERER
@private
*/
RENDERER: 0x1F01,
/**
@type {GLenum}
@name PicoGL.VERSION
@private
*/
VERSION: 0x1F02,
/**
@type {GLenum}
@name PicoGL.NEAREST
@private
*/
NEAREST: 0x2600,
/**
@type {GLenum}
@name PicoGL.LINEAR
@private
*/
LINEAR: 0x2601,
/**
@type {GLenum}
@name PicoGL.NEAREST_MIPMAP_NEAREST
@private
*/
NEAREST_MIPMAP_NEAREST: 0x2700,
/**
@type {GLenum}
@name PicoGL.LINEAR_MIPMAP_NEAREST
@private
*/
LINEAR_MIPMAP_NEAREST: 0x2701,
/**
@type {GLenum}
@name PicoGL.NEAREST_MIPMAP_LINEAR
@private
*/
NEAREST_MIPMAP_LINEAR: 0x2702,
/**
@type {GLenum}
@name PicoGL.LINEAR_MIPMAP_LINEAR
@private
*/
LINEAR_MIPMAP_LINEAR: 0x2703,
/**
@type {GLenum}
@name PicoGL.TEXTURE_MAG_FILTER
@private
*/
TEXTURE_MAG_FILTER: 0x2800,
/**
@type {GLenum}
@name PicoGL.TEXTURE_MIN_FILTER
@private
*/
TEXTURE_MIN_FILTER: 0x2801,
/**
@type {GLenum}
@name PicoGL.TEXTURE_WRAP_S
@private
*/
TEXTURE_WRAP_S: 0x2802,
/**
@type {GLenum}
@name PicoGL.TEXTURE_WRAP_T
@private
*/
TEXTURE_WRAP_T: 0x2803,
/**
@type {GLenum}
@name PicoGL.TEXTURE_2D
@private
*/
TEXTURE_2D: 0x0DE1,
/**
@type {GLenum}
@name PicoGL.TEXTURE
@private
*/
TEXTURE: 0x1702,
/**
@type {GLenum}
@name PicoGL.TEXTURE_CUBE_MAP
@private
*/
TEXTURE_CUBE_MAP: 0x8513,
/**
@type {GLenum}
@name PicoGL.TEXTURE_BINDING_CUBE_MAP
@private
*/
TEXTURE_BINDING_CUBE_MAP: 0x8514,
/**
@type {GLenum}
@name PicoGL.TEXTURE_CUBE_MAP_POSITIVE_X
@private
*/
TEXTURE_CUBE_MAP_POSITIVE_X: 0x8515,
/**
@type {GLenum}
@name PicoGL.TEXTURE_CUBE_MAP_NEGATIVE_X
@private
*/
TEXTURE_CUBE_MAP_NEGATIVE_X: 0x8516,
/**
@type {GLenum}
@name PicoGL.TEXTURE_CUBE_MAP_POSITIVE_Y
@private
*/
TEXTURE_CUBE_MAP_POSITIVE_Y: 0x8517,
/**
@type {GLenum}
@name PicoGL.TEXTURE_CUBE_MAP_NEGATIVE_Y
@private
*/
TEXTURE_CUBE_MAP_NEGATIVE_Y: 0x8518,
/**
@type {GLenum}
@name PicoGL.TEXTURE_CUBE_MAP_POSITIVE_Z
@private
*/
TEXTURE_CUBE_MAP_POSITIVE_Z: 0x8519,
/**
@type {GLenum}
@name PicoGL.TEXTURE_CUBE_MAP_NEGATIVE_Z
@private
*/
TEXTURE_CUBE_MAP_NEGATIVE_Z: 0x851A,
/**
@type {GLenum}
@name PicoGL.MAX_CUBE_MAP_TEXTURE_SIZE
@private
*/
MAX_CUBE_MAP_TEXTURE_SIZE: 0x851C,
/**
@type {GLenum}
@name PicoGL.TEXTURE0
@private
*/
TEXTURE0: 0x84C0,
/**
@type {GLenum}
@name PicoGL.TEXTURE1
@private
*/
TEXTURE1: 0x84C1,
/**
@type {GLenum}
@name PicoGL.TEXTURE2
@private
*/
TEXTURE2: 0x84C2,
/**
@type {GLenum}
@name PicoGL.TEXTURE3
@private
*/
TEXTURE3: 0x84C3,
/**
@type {GLenum}
@name PicoGL.TEXTURE4
@private
*/
TEXTURE4: 0x84C4,
/**
@type {GLenum}
@name PicoGL.TEXTURE5
@private
*/
TEXTURE5: 0x84C5,
/**
@type {GLenum}
@name PicoGL.TEXTURE6
@private
*/
TEXTURE6: 0x84C6,
/**
@type {GLenum}
@name PicoGL.TEXTURE7
@private
*/
TEXTURE7: 0x84C7,
/**
@type {GLenum}
@name PicoGL.TEXTURE8
@private
*/
TEXTURE8: 0x84C8,
/**
@type {GLenum}
@name PicoGL.TEXTURE9
@private
*/
TEXTURE9: 0x84C9,
/**
@type {GLenum}
@name PicoGL.TEXTURE10
@private
*/
TEXTURE10: 0x84CA,
/**
@type {GLenum}
@name PicoGL.TEXTURE11
@private
*/
TEXTURE11: 0x84CB,
/**
@type {GLenum}
@name PicoGL.TEXTURE12
@private
*/
TEXTURE12: 0x84CC,
/**
@type {GLenum}
@name PicoGL.TEXTURE13
@private
*/
TEXTURE13: 0x84CD,
/**
@type {GLenum}
@name PicoGL.TEXTURE14
@private
*/
TEXTURE14: 0x84CE,
/**
@type {GLenum}
@name PicoGL.TEXTURE15
@private
*/
TEXTURE15: 0x84CF,
/**
@type {GLenum}
@name PicoGL.TEXTURE16
@private
*/
TEXTURE16: 0x84D0,
/**
@type {GLenum}
@name PicoGL.TEXTURE17
@private
*/
TEXTURE17: 0x84D1,
/**
@type {GLenum}
@name PicoGL.TEXTURE18
@private
*/
TEXTURE18: 0x84D2,
/**
@type {GLenum}
@name PicoGL.TEXTURE19
@private
*/
TEXTURE19: 0x84D3,
/**
@type {GLenum}
@name PicoGL.TEXTURE20
@private
*/
TEXTURE20: 0x84D4,
/**
@type {GLenum}
@name PicoGL.TEXTURE21
@private
*/
TEXTURE21: 0x84D5,
/**
@type {GLenum}
@name PicoGL.TEXTURE22
@private
*/
TEXTURE22: 0x84D6,
/**
@type {GLenum}
@name PicoGL.TEXTURE23
@private
*/
TEXTURE23: 0x84D7,
/**
@type {GLenum}
@name PicoGL.TEXTURE24
@private
*/
TEXTURE24: 0x84D8,
/**
@type {GLenum}
@name PicoGL.TEXTURE25
@private
*/
TEXTURE25: 0x84D9,
/**
@type {GLenum}
@name PicoGL.TEXTURE26
@private
*/
TEXTURE26: 0x84DA,
/**
@type {GLenum}
@name PicoGL.TEXTURE27
@private
*/
TEXTURE27: 0x84DB,
/**
@type {GLenum}
@name PicoGL.TEXTURE28
@private
*/
TEXTURE28: 0x84DC,
/**
@type {GLenum}
@name PicoGL.TEXTURE29
@private
*/
TEXTURE29: 0x84DD,
/**
@type {GLenum}
@name PicoGL.TEXTURE30
@private
*/
TEXTURE30: 0x84DE,
/**
@type {GLenum}
@name PicoGL.TEXTURE31
@private
*/
TEXTURE31: 0x84DF,
/**
@type {GLenum}
@name PicoGL.ACTIVE_TEXTURE
@private
*/
ACTIVE_TEXTURE: 0x84E0,
/**
@type {GLenum}
@name PicoGL.REPEAT
@private
*/
REPEAT: 0x2901,
/**
@type {GLenum}
@name PicoGL.CLAMP_TO_EDGE
@private
*/
CLAMP_TO_EDGE: 0x812F,
/**
@type {GLenum}
@name PicoGL.MIRRORED_REPEAT
@private
*/
MIRRORED_REPEAT: 0x8370,
/**
@type {GLenum}
@name PicoGL.FLOAT_VEC2
@private
*/
FLOAT_VEC2: 0x8B50,
/**
@type {GLenum}
@name PicoGL.FLOAT_VEC3
@private
*/
FLOAT_VEC3: 0x8B51,
/**
@type {GLenum}
@name PicoGL.FLOAT_VEC4
@private
*/
FLOAT_VEC4: 0x8B52,
/**
@type {GLenum}
@name PicoGL.INT_VEC2
@private
*/
INT_VEC2: 0x8B53,
/**
@type {GLenum}
@name PicoGL.INT_VEC3
@private
*/
INT_VEC3: 0x8B54,
/**
@type {GLenum}
@name PicoGL.INT_VEC4
@private
*/
INT_VEC4: 0x8B55,
/**
@type {GLenum}
@name PicoGL.BOOL
@private
*/
BOOL: 0x8B56,
/**
@type {GLenum}
@name PicoGL.BOOL_VEC2
@private
*/
BOOL_VEC2: 0x8B57,
/**
@type {GLenum}
@name PicoGL.BOOL_VEC3
@private
*/
BOOL_VEC3: 0x8B58,
/**
@type {GLenum}
@name PicoGL.BOOL_VEC4
@private
*/
BOOL_VEC4: 0x8B59,
/**
@type {GLenum}
@name PicoGL.FLOAT_MAT2
@private
*/
FLOAT_MAT2: 0x8B5A,
/**
@type {GLenum}
@name PicoGL.FLOAT_MAT3
@private
*/
FLOAT_MAT3: 0x8B5B,
/**
@type {GLenum}
@name PicoGL.FLOAT_MAT4
@private
*/
FLOAT_MAT4: 0x8B5C,
/**
@type {GLenum}
@name PicoGL.SAMPLER_2D
@private
*/
SAMPLER_2D: 0x8B5E,
/**
@type {GLenum}
@name PicoGL.SAMPLER_CUBE
@private
*/
SAMPLER_CUBE: 0x8B60,
/**
@type {GLenum}
@name PicoGL.VERTEX_ATTRIB_ARRAY_ENABLED
@private
*/
VERTEX_ATTRIB_ARRAY_ENABLED: 0x8622,
/**
@type {GLenum}
@name PicoGL.VERTEX_ATTRIB_ARRAY_SIZE
@private
*/
VERTEX_ATTRIB_ARRAY_SIZE: 0x8623,
/**
@type {GLenum}
@name PicoGL.VERTEX_ATTRIB_ARRAY_STRIDE
@private
*/
VERTEX_ATTRIB_ARRAY_STRIDE: 0x8624,
/**
@type {GLenum}
@name PicoGL.VERTEX_ATTRIB_ARRAY_TYPE
@private
*/
VERTEX_ATTRIB_ARRAY_TYPE: 0x8625,
/**
@type {GLenum}
@name PicoGL.VERTEX_ATTRIB_ARRAY_NORMALIZED
@private
*/
VERTEX_ATTRIB_ARRAY_NORMALIZED: 0x886A,
/**
@type {GLenum}
@name PicoGL.VERTEX_ATTRIB_ARRAY_POINTER
@private
*/
VERTEX_ATTRIB_ARRAY_POINTER: 0x8645,
/**
@type {GLenum}
@name PicoGL.VERTEX_ATTRIB_ARRAY_BUFFER_BINDING
@private
*/
VERTEX_ATTRIB_ARRAY_BUFFER_BINDING: 0x889F,
/**
@type {GLenum}
@name PicoGL.IMPLEMENTATION_COLOR_READ_TYPE
@private
*/
IMPLEMENTATION_COLOR_READ_TYPE: 0x8B9A,
/**
@type {GLenum}
@name PicoGL.IMPLEMENTATION_COLOR_READ_FORMAT
@private
*/
IMPLEMENTATION_COLOR_READ_FORMAT: 0x8B9B,
/**
@type {GLenum}
@name PicoGL.COMPILE_STATUS
@private
*/
COMPILE_STATUS: 0x8B81,
/**
@type {GLenum}
@name PicoGL.LOW_FLOAT
@private
*/
LOW_FLOAT: 0x8DF0,
/**
@type {GLenum}
@name PicoGL.MEDIUM_FLOAT
@private
*/
MEDIUM_FLOAT: 0x8DF1,
/**
@type {GLenum}
@name PicoGL.HIGH_FLOAT
@private
*/
HIGH_FLOAT: 0x8DF2,
/**
@type {GLenum}
@name PicoGL.LOW_INT
@private
*/
LOW_INT: 0x8DF3,
/**
@type {GLenum}
@name PicoGL.MEDIUM_INT
@private
*/
MEDIUM_INT: 0x8DF4,
/**
@type {GLenum}
@name PicoGL.HIGH_INT
@private
*/
HIGH_INT: 0x8DF5,
/**
@type {GLenum}
@name PicoGL.FRAMEBUFFER
@private
*/
FRAMEBUFFER: 0x8D40,
/**
@type {GLenum}
@name PicoGL.RENDERBUFFER
@private
*/
RENDERBUFFER: 0x8D41,
/**
@type {GLenum}
@name PicoGL.RGBA4
@private
*/
RGBA4: 0x8056,
/**
@type {GLenum}
@name PicoGL.RGB5_A1
@private
*/
RGB5_A1: 0x8057,
/**
@type {GLenum}
@name PicoGL.RGB565
@private
*/
RGB565: 0x8D62,
/**
@type {GLenum}
@name PicoGL.DEPTH_COMPONENT16
@private
*/
DEPTH_COMPONENT16: 0x81A5,
/**
@type {GLenum}
@name PicoGL.STENCIL_INDEX
@private
*/
STENCIL_INDEX: 0x1901,
/**
@type {GLenum}
@name PicoGL.STENCIL_INDEX8
@private
*/
STENCIL_INDEX8: 0x8D48,
/**
@type {GLenum}
@name PicoGL.DEPTH_STENCIL
@private
*/
DEPTH_STENCIL: 0x84F9,
/**
@type {GLenum}
@name PicoGL.RENDERBUFFER_WIDTH
@private
*/
RENDERBUFFER_WIDTH: 0x8D42,
/**
@type {GLenum}
@name PicoGL.RENDERBUFFER_HEIGHT
@private
*/
RENDERBUFFER_HEIGHT: 0x8D43,
/**
@type {GLenum}
@name PicoGL.RENDERBUFFER_INTERNAL_FORMAT
@private
*/
RENDERBUFFER_INTERNAL_FORMAT: 0x8D44,
/**
@type {GLenum}
@name PicoGL.RENDERBUFFER_RED_SIZE
@private
*/
RENDERBUFFER_RED_SIZE: 0x8D50,
/**
@type {GLenum}
@name PicoGL.RENDERBUFFER_GREEN_SIZE
@private
*/
RENDERBUFFER_GREEN_SIZE: 0x8D51,
/**
@type {GLenum}
@name PicoGL.RENDERBUFFER_BLUE_SIZE
@private
*/
RENDERBUFFER_BLUE_SIZE: 0x8D52,
/**
@type {GLenum}
@name PicoGL.RENDERBUFFER_ALPHA_SIZE
@private
*/
RENDERBUFFER_ALPHA_SIZE: 0x8D53,
/**
@type {GLenum}
@name PicoGL.RENDERBUFFER_DEPTH_SIZE
@private
*/
RENDERBUFFER_DEPTH_SIZE: 0x8D54,
/**
@type {GLenum}
@name PicoGL.RENDERBUFFER_STENCIL_SIZE
@private
*/
RENDERBUFFER_STENCIL_SIZE: 0x8D55,
/**
@type {GLenum}
@name PicoGL.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE
@private
*/
FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE: 0x8CD0,
/**
@type {GLenum}
@name PicoGL.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME
@private
*/
FRAMEBUFFER_ATTACHMENT_OBJECT_NAME: 0x8CD1,
/**
@type {GLenum}
@name PicoGL.FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL
@private
*/
FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL: 0x8CD2,
/**
@type {GLenum}
@name PicoGL.FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE
@private
*/
FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE: 0x8CD3,
/**
@type {GLenum}
@name PicoGL.COLOR_ATTACHMENT0
@private
*/
COLOR_ATTACHMENT0: 0x8CE0,
/**
@type {GLenum}
@name PicoGL.DEPTH_ATTACHMENT
@private
*/
DEPTH_ATTACHMENT: 0x8D00,
/**
@type {GLenum}
@name PicoGL.STENCIL_ATTACHMENT
@private
*/
STENCIL_ATTACHMENT: 0x8D20,
/**
@type {GLenum}
@name PicoGL.DEPTH_STENCIL_ATTACHMENT
@private
*/
DEPTH_STENCIL_ATTACHMENT: 0x821A,
/**
@type {GLenum}
@name PicoGL.NONE
@private
*/
NONE: 0,
/**
@type {GLenum}
@name PicoGL.FRAMEBUFFER_COMPLETE
@private
*/
FRAMEBUFFER_COMPLETE: 0x8CD5,
/**
@type {GLenum}
@name PicoGL.FRAMEBUFFER_INCOMPLETE_ATTACHMENT
@private
*/
FRAMEBUFFER_INCOMPLETE_ATTACHMENT: 0x8CD6,
/**
@type {GLenum}
@name PicoGL.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT
@private
*/
FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: 0x8CD7,
/**
@type {GLenum}
@name PicoGL.FRAMEBUFFER_INCOMPLETE_DIMENSIONS
@private
*/
FRAMEBUFFER_INCOMPLETE_DIMENSIONS: 0x8CD9,
/**
@type {GLenum}
@name PicoGL.FRAMEBUFFER_UNSUPPORTED
@private
*/
FRAMEBUFFER_UNSUPPORTED: 0x8CDD,
/**
@type {GLenum}
@name PicoGL.FRAMEBUFFER_BINDING
@private
*/
FRAMEBUFFER_BINDING: 0x8CA6,
/**
@type {GLenum}
@name PicoGL.RENDERBUFFER_BINDING
@private
*/
RENDERBUFFER_BINDING: 0x8CA7,
/**
@type {GLenum}
@name PicoGL.MAX_RENDERBUFFER_SIZE
@private
*/
MAX_RENDERBUFFER_SIZE: 0x84E8,
/**
@type {GLenum}
@name PicoGL.INVALID_FRAMEBUFFER_OPERATION
@private
*/
INVALID_FRAMEBUFFER_OPERATION: 0x0506,
/**
@type {GLenum}
@name PicoGL.UNPACK_FLIP_Y_WEBGL
@private
*/
UNPACK_FLIP_Y_WEBGL: 0x9240,
/**
@type {GLenum}
@name PicoGL.UNPACK_PREMULTIPLY_ALPHA_WEBGL
@private
*/
UNPACK_PREMULTIPLY_ALPHA_WEBGL: 0x9241,
/**
@type {GLenum}
@name PicoGL.CONTEXT_LOST_WEBGL
@private
*/
CONTEXT_LOST_WEBGL: 0x9242,
/**
@type {GLenum}
@name PicoGL.UNPACK_COLORSPACE_CONVERSION_WEBGL
@private
*/
UNPACK_COLORSPACE_CONVERSION_WEBGL: 0x9243,
/**
@type {GLenum}
@name PicoGL.BROWSER_DEFAULT_WEBGL
@private
*/
BROWSER_DEFAULT_WEBGL: 0x9244,
/**
@type {GLenum}
@name PicoGL.READ_BUFFER
@private
*/
READ_BUFFER: 0x0C02,
/**
@type {GLenum}
@name PicoGL.UNPACK_ROW_LENGTH
@private
*/
UNPACK_ROW_LENGTH: 0x0CF2,
/**
@type {GLenum}
@name PicoGL.UNPACK_SKIP_ROWS
@private
*/
UNPACK_SKIP_ROWS: 0x0CF3,
/**
@type {GLenum}
@name PicoGL.UNPACK_SKIP_PIXELS
@private
*/
UNPACK_SKIP_PIXELS: 0x0CF4,
/**
@type {GLenum}
@name PicoGL.PACK_ROW_LENGTH
@private
*/
PACK_ROW_LENGTH: 0x0D02,
/**
@type {GLenum}
@name PicoGL.PACK_SKIP_ROWS
@private
*/
PACK_SKIP_ROWS: 0x0D03,
/**
@type {GLenum}
@name PicoGL.PACK_SKIP_PIXELS
@private
*/
PACK_SKIP_PIXELS: 0x0D04,
/**
@type {GLenum}
@name PicoGL.COLOR
@private
*/
COLOR: 0x1800,
/**
@type {GLenum}
@name PicoGL.DEPTH
@private
*/
DEPTH: 0x1801,
/**
@type {GLenum}
@name PicoGL.STENCIL
@private
*/
STENCIL: 0x1802,
/**
@type {GLenum}
@name PicoGL.RED
@private
*/
RED: 0x1903,
/**
@type {GLenum}
@name PicoGL.RGB8
@private
*/
RGB8: 0x8051,
/**
@type {GLenum}
@name PicoGL.RGBA8
@private
*/
RGBA8: 0x8058,
/**
@type {GLenum}
@name PicoGL.RGB10_A2
@private
*/
RGB10_A2: 0x8059,
/**
@type {GLenum}
@name PicoGL.TEXTURE_BINDING_3D
@private
*/
TEXTURE_BINDING_3D: 0x806A,
/**
@type {GLenum}
@name PicoGL.UNPACK_SKIP_IMAGES
@private
*/
UNPACK_SKIP_IMAGES: 0x806D,
/**
@type {GLenum}
@name PicoGL.UNPACK_IMAGE_HEIGHT
@private
*/
UNPACK_IMAGE_HEIGHT: 0x806E,
/**
@type {GLenum}
@name PicoGL.TEXTURE_3D
@private
*/
TEXTURE_3D: 0x806F,
/**
@type {GLenum}
@name PicoGL.TEXTURE_WRAP_R
@private
*/
TEXTURE_WRAP_R: 0x8072,
/**
@type {GLenum}
@name PicoGL.MAX_3D_TEXTURE_SIZE
@private
*/
MAX_3D_TEXTURE_SIZE: 0x8073,
/**
@type {GLenum}
@name PicoGL.UNSIGNED_INT_2_10_10_10_REV
@private
*/
UNSIGNED_INT_2_10_10_10_REV: 0x8368,
/**
@type {GLenum}
@name PicoGL.MAX_ELEMENTS_VERTICES
@private
*/
MAX_ELEMENTS_VERTICES: 0x80E8,
/**
@type {GLenum}
@name PicoGL.MAX_ELEMENTS_INDICES
@private
*/
MAX_ELEMENTS_INDICES: 0x80E9,
/**
@type {GLenum}
@name PicoGL.TEXTURE_MIN_LOD
@private
*/
TEXTURE_MIN_LOD: 0x813A,
/**
@type {GLenum}
@name PicoGL.TEXTURE_MAX_LOD
@private
*/
TEXTURE_MAX_LOD: 0x813B,
/**
@type {GLenum}
@name PicoGL.TEXTURE_BASE_LEVEL
@private
*/
TEXTURE_BASE_LEVEL: 0x813C,
/**
@type {GLenum}
@name PicoGL.TEXTURE_MAX_LEVEL
@private
*/
TEXTURE_MAX_LEVEL: 0x813D,
/**
@type {GLenum}
@name PicoGL.MIN
@private
*/
MIN: 0x8007,
/**
@type {GLenum}
@name PicoGL.MAX
@private
*/
MAX: 0x8008,
/**
@type {GLenum}
@name PicoGL.DEPTH_COMPONENT24
@private
*/
DEPTH_COMPONENT24: 0x81A6,
/**
@type {GLenum}
@name PicoGL.MAX_TEXTURE_LOD_BIAS
@private
*/
MAX_TEXTURE_LOD_BIAS: 0x84FD,
/**
@type {GLenum}
@name PicoGL.TEXTURE_COMPARE_MODE
@private
*/
TEXTURE_COMPARE_MODE: 0x884C,
/**
@type {GLenum}
@name PicoGL.TEXTURE_COMPARE_FUNC
@private
*/
TEXTURE_COMPARE_FUNC: 0x884D,
/**
@type {GLenum}
@name PicoGL.CURRENT_QUERY
@private
*/
CURRENT_QUERY: 0x8865,
/**
@type {GLenum}
@name PicoGL.QUERY_RESULT
@private
*/
QUERY_RESULT: 0x8866,
/**
@type {GLenum}
@name PicoGL.QUERY_RESULT_AVAILABLE
@private
*/
QUERY_RESULT_AVAILABLE: 0x8867,
/**
@type {GLenum}
@name PicoGL.STREAM_READ
@private
*/
STREAM_READ: 0x88E1,
/**
@type {GLenum}
@name PicoGL.STREAM_COPY
@private
*/
STREAM_COPY: 0x88E2,
/**
@type {GLenum}
@name PicoGL.STATIC_READ
@private
*/
STATIC_READ: 0x88E5,
/**
@type {GLenum}
@name PicoGL.STATIC_COPY
@private
*/
STATIC_COPY: 0x88E6,
/**
@type {GLenum}
@name PicoGL.DYNAMIC_READ
@private
*/
DYNAMIC_READ: 0x88E9,
/**
@type {GLenum}
@name PicoGL.DYNAMIC_COPY
@private
*/
DYNAMIC_COPY: 0x88EA,
/**
@type {GLenum}
@name PicoGL.MAX_DRAW_BUFFERS
@private
*/
MAX_DRAW_BUFFERS: 0x8824,
/**
@type {GLenum}
@name PicoGL.DRAW_BUFFER0
@private
*/
DRAW_BUFFER0: 0x8825,
/**
@type {GLenum}
@name PicoGL.DRAW_BUFFER1
@private
*/
DRAW_BUFFER1: 0x8826,
/**
@type {GLenum}
@name PicoGL.DRAW_BUFFER2
@private
*/
DRAW_BUFFER2: 0x8827,
/**
@type {GLenum}
@name PicoGL.DRAW_BUFFER3
@private
*/
DRAW_BUFFER3: 0x8828,
/**
@type {GLenum}
@name PicoGL.DRAW_BUFFER4
@private
*/
DRAW_BUFFER4: 0x8829,
/**
@type {GLenum}
@name PicoGL.DRAW_BUFFER5
@private
*/
DRAW_BUFFER5: 0x882A,
/**
@type {GLenum}
@name PicoGL.DRAW_BUFFER6
@private
*/
DRAW_BUFFER6: 0x882B,
/**
@type {GLenum}
@name PicoGL.DRAW_BUFFER7
@private
*/
DRAW_BUFFER7: 0x882C,
/**
@type {GLenum}
@name PicoGL.DRAW_BUFFER8
@private
*/
DRAW_BUFFER8: 0x882D,
/**
@type {GLenum}
@name PicoGL.DRAW_BUFFER9
@private
*/
DRAW_BUFFER9: 0x882E,
/**
@type {GLenum}
@name PicoGL.DRAW_BUFFER10
@private
*/
DRAW_BUFFER10: 0x882F,
/**
@type {GLenum}
@name PicoGL.DRAW_BUFFER11
@private
*/
DRAW_BUFFER11: 0x8830,
/**
@type {GLenum}
@name PicoGL.DRAW_BUFFER12
@private
*/
DRAW_BUFFER12: 0x8831,
/**
@type {GLenum}
@name PicoGL.DRAW_BUFFER13
@private
*/
DRAW_BUFFER13: 0x8832,
/**
@type {GLenum}
@name PicoGL.DRAW_BUFFER14
@private
*/
DRAW_BUFFER14: 0x8833,
/**
@type {GLenum}
@name PicoGL.DRAW_BUFFER15
@private
*/
DRAW_BUFFER15: 0x8834,
/**
@type {GLenum}
@name PicoGL.MAX_FRAGMENT_UNIFORM_COMPONENTS
@private
*/
MAX_FRAGMENT_UNIFORM_COMPONENTS: 0x8B49,
/**
@type {GLenum}
@name PicoGL.MAX_VERTEX_UNIFORM_COMPONENTS
@private
*/
MAX_VERTEX_UNIFORM_COMPONENTS: 0x8B4A,
/**
@type {GLenum}
@name PicoGL.SAMPLER_3D
@private
*/
SAMPLER_3D: 0x8B5F,
/**
@type {GLenum}
@name PicoGL.SAMPLER_2D_SHADOW
@private
*/
SAMPLER_2D_SHADOW: 0x8B62,
/**
@type {GLenum}
@name PicoGL.FRAGMENT_SHADER_DERIVATIVE_HINT
@private
*/
FRAGMENT_SHADER_DERIVATIVE_HINT: 0x8B8B,
/**
@type {GLenum}
@name PicoGL.PIXEL_PACK_BUFFER
@private
*/
PIXEL_PACK_BUFFER: 0x88EB,
/**
@type {GLenum}
@name PicoGL.PIXEL_UNPACK_BUFFER
@private
*/
PIXEL_UNPACK_BUFFER: 0x88EC,
/**
@type {GLenum}
@name PicoGL.PIXEL_PACK_BUFFER_BINDING
@private
*/
PIXEL_PACK_BUFFER_BINDING: 0x88ED,
/**
@type {GLenum}
@name PicoGL.PIXEL_UNPACK_BUFFER_BINDING
@private
*/
PIXEL_UNPACK_BUFFER_BINDING: 0x88EF,
/**
@type {GLenum}
@name PicoGL.FLOAT_MAT2x3
@private
*/
FLOAT_MAT2x3: 0x8B65,
/**
@type {GLenum}
@name PicoGL.FLOAT_MAT2x4
@private
*/
FLOAT_MAT2x4: 0x8B66,
/**
@type {GLenum}
@name PicoGL.FLOAT_MAT3x2
@private
*/
FLOAT_MAT3x2: 0x8B67,
/**
@type {GLenum}
@name PicoGL.FLOAT_MAT3x4
@private
*/
FLOAT_MAT3x4: 0x8B68,
/**
@type {GLenum}
@name PicoGL.FLOAT_MAT4x2
@private
*/
FLOAT_MAT4x2: 0x8B69,
/**
@type {GLenum}
@name PicoGL.FLOAT_MAT4x3
@private
*/
FLOAT_MAT4x3: 0x8B6A,
/**
@type {GLenum}
@name PicoGL.SRGB
@private
*/
SRGB: 0x8C40,
/**
@type {GLenum}
@name PicoGL.SRGB8
@private
*/
SRGB8: 0x8C41,
/**
@type {GLenum}
@name PicoGL.SRGB8_ALPHA8
@private
*/
SRGB8_ALPHA8: 0x8C43,
/**
@type {GLenum}
@name PicoGL.COMPARE_REF_TO_TEXTURE
@private
*/
COMPARE_REF_TO_TEXTURE: 0x884E,
/**
@type {GLenum}
@name PicoGL.RGBA32F
@private
*/
RGBA32F: 0x8814,
/**
@type {GLenum}
@name PicoGL.RGB32F
@private
*/
RGB32F: 0x8815,
/**
@type {GLenum}
@name PicoGL.RGBA16F
@private
*/
RGBA16F: 0x881A,
/**
@type {GLenum}
@name PicoGL.RGB16F
@private
*/
RGB16F: 0x881B,
/**
@type {GLenum}
@name PicoGL.VERTEX_ATTRIB_ARRAY_INTEGER
@private
*/
VERTEX_ATTRIB_ARRAY_INTEGER: 0x88FD,
/**
@type {GLenum}
@name PicoGL.MAX_ARRAY_TEXTURE_LAYERS
@private
*/
MAX_ARRAY_TEXTURE_LAYERS: 0x88FF,
/**
@type {GLenum}
@name PicoGL.MIN_PROGRAM_TEXEL_OFFSET
@private
*/
MIN_PROGRAM_TEXEL_OFFSET: 0x8904,
/**
@type {GLenum}
@name PicoGL.MAX_PROGRAM_TEXEL_OFFSET
@private
*/
MAX_PROGRAM_TEXEL_OFFSET: 0x8905,
/**
@type {GLenum}
@name PicoGL.MAX_VARYING_COMPONENTS
@private
*/
MAX_VARYING_COMPONENTS: 0x8B4B,
/**
@type {GLenum}
@name PicoGL.TEXTURE_2D_ARRAY
@private
*/
TEXTURE_2D_ARRAY: 0x8C1A,
/**
@type {GLenum}
@name PicoGL.TEXTURE_BINDING_2D_ARRAY
@private
*/
TEXTURE_BINDING_2D_ARRAY: 0x8C1D,
/**
@type {GLenum}
@name PicoGL.R11F_G11F_B10F
@private
*/
R11F_G11F_B10F: 0x8C3A,
/**
@type {GLenum}
@name PicoGL.UNSIGNED_INT_10F_11F_11F_REV
@private
*/
UNSIGNED_INT_10F_11F_11F_REV: 0x8C3B,
/**
@type {GLenum}
@name PicoGL.RGB9_E5
@private
*/
RGB9_E5: 0x8C3D,
/**
@type {GLenum}
@name PicoGL.UNSIGNED_INT_5_9_9_9_REV
@private
*/
UNSIGNED_INT_5_9_9_9_REV: 0x8C3E,
/**
@type {GLenum}
@name PicoGL.TRANSFORM_FEEDBACK_BUFFER_MODE
@private
*/
TRANSFORM_FEEDBACK_BUFFER_MODE: 0x8C7F,
/**
@type {GLenum}
@name PicoGL.MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS
@private
*/
MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS: 0x8C80,
/**
@type {GLenum}
@name PicoGL.TRANSFORM_FEEDBACK_VARYINGS
@private
*/
TRANSFORM_FEEDBACK_VARYINGS: 0x8C83,
/**
@type {GLenum}
@name PicoGL.TRANSFORM_FEEDBACK_BUFFER_START
@private
*/
TRANSFORM_FEEDBACK_BUFFER_START: 0x8C84,
/**
@type {GLenum}
@name PicoGL.TRANSFORM_FEEDBACK_BUFFER_SIZE
@private
*/
TRANSFORM_FEEDBACK_BUFFER_SIZE: 0x8C85,
/**
@type {GLenum}
@name PicoGL.TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN
@private
*/
TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN: 0x8C88,
/**
@type {GLenum}
@name PicoGL.RASTERIZER_DISCARD
@private
*/
RASTERIZER_DISCARD: 0x8C89,
/**
@type {GLenum}
@name PicoGL.MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS
@private
*/
MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS: 0x8C8A,
/**
@type {GLenum}
@name PicoGL.MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS
@private
*/
MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS: 0x8C8B,
/**
@type {GLenum}
@name PicoGL.INTERLEAVED_ATTRIBS
@private
*/
INTERLEAVED_ATTRIBS: 0x8C8C,
/**
@type {GLenum}
@name PicoGL.SEPARATE_ATTRIBS
@private
*/
SEPARATE_ATTRIBS: 0x8C8D,
/**
@type {GLenum}
@name PicoGL.TRANSFORM_FEEDBACK_BUFFER
@private
*/
TRANSFORM_FEEDBACK_BUFFER: 0x8C8E,
/**
@type {GLenum}
@name PicoGL.TRANSFORM_FEEDBACK_BUFFER_BINDING
@private
*/
TRANSFORM_FEEDBACK_BUFFER_BINDING: 0x8C8F,
/**
@type {GLenum}
@name PicoGL.RGBA32UI
@private
*/
RGBA32UI: 0x8D70,
/**
@type {GLenum}
@name PicoGL.RGB32UI
@private
*/
RGB32UI: 0x8D71,
/**
@type {GLenum}
@name PicoGL.RGBA16UI
@private
*/
RGBA16UI: 0x8D76,
/**
@type {GLenum}
@name PicoGL.RGB16UI
@private
*/
RGB16UI: 0x8D77,
/**
@type {GLenum}
@name PicoGL.RGBA8UI
@private
*/
RGBA8UI: 0x8D7C,
/**
@type {GLenum}
@name PicoGL.RGB8UI
@private
*/
RGB8UI: 0x8D7D,
/**
@type {GLenum}
@name PicoGL.RGBA32I
@private
*/
RGBA32I: 0x8D82,
/**
@type {GLenum}
@name PicoGL.RGB32I
@private
*/
RGB32I: 0x8D83,
/**
@type {GLenum}
@name PicoGL.RGBA16I
@private
*/
RGBA16I: 0x8D88,
/**
@type {GLenum}
@name PicoGL.RGB16I
@private
*/
RGB16I: 0x8D89,
/**
@type {GLenum}
@name PicoGL.RGBA8I
@private
*/
RGBA8I: 0x8D8E,
/**
@type {GLenum}
@name PicoGL.RGB8I
@private
*/
RGB8I: 0x8D8F,
/**
@type {GLenum}
@name PicoGL.RED_INTEGER
@private
*/
RED_INTEGER: 0x8D94,
/**
@type {GLenum}
@name PicoGL.RGB_INTEGER
@private
*/
RGB_INTEGER: 0x8D98,
/**
@type {GLenum}
@name PicoGL.RGBA_INTEGER
@private
*/
RGBA_INTEGER: 0x8D99,
/**
@type {GLenum}
@name PicoGL.SAMPLER_2D_ARRAY
@private
*/
SAMPLER_2D_ARRAY: 0x8DC1,
/**
@type {GLenum}
@name PicoGL.SAMPLER_2D_ARRAY_SHADOW
@private
*/
SAMPLER_2D_ARRAY_SHADOW: 0x8DC4,
/**
@type {GLenum}
@name PicoGL.SAMPLER_CUBE_SHADOW
@private
*/
SAMPLER_CUBE_SHADOW: 0x8DC5,
/**
@type {GLenum}
@name PicoGL.UNSIGNED_INT_VEC2
@private
*/
UNSIGNED_INT_VEC2: 0x8DC6,
/**
@type {GLenum}
@name PicoGL.UNSIGNED_INT_VEC3
@private
*/
UNSIGNED_INT_VEC3: 0x8DC7,
/**
@type {GLenum}
@name PicoGL.UNSIGNED_INT_VEC4
@private
*/
UNSIGNED_INT_VEC4: 0x8DC8,
/**
@type {GLenum}
@name PicoGL.INT_SAMPLER_2D
@private
*/
INT_SAMPLER_2D: 0x8DCA,
/**
@type {GLenum}
@name PicoGL.INT_SAMPLER_3D
@private
*/
INT_SAMPLER_3D: 0x8DCB,
/**
@type {GLenum}
@name PicoGL.INT_SAMPLER_CUBE
@private
*/
INT_SAMPLER_CUBE: 0x8DCC,
/**
@type {GLenum}
@name PicoGL.INT_SAMPLER_2D_ARRAY
@private
*/
INT_SAMPLER_2D_ARRAY: 0x8DCF,
/**
@type {GLenum}
@name PicoGL.UNSIGNED_INT_SAMPLER_2D
@private
*/
UNSIGNED_INT_SAMPLER_2D: 0x8DD2,
/**
@type {GLenum}
@name PicoGL.UNSIGNED_INT_SAMPLER_3D
@private
*/
UNSIGNED_INT_SAMPLER_3D: 0x8DD3,
/**
@type {GLenum}
@name PicoGL.UNSIGNED_INT_SAMPLER_CUBE
@private
*/
UNSIGNED_INT_SAMPLER_CUBE: 0x8DD4,
/**
@type {GLenum}
@name PicoGL.UNSIGNED_INT_SAMPLER_2D_ARRAY
@private
*/
UNSIGNED_INT_SAMPLER_2D_ARRAY: 0x8DD7,
/**
@type {GLenum}
@name PicoGL.DEPTH_COMPONENT32F
@private
*/
DEPTH_COMPONENT32F: 0x8CAC,
/**
@type {GLenum}
@name PicoGL.DEPTH32F_STENCIL8
@private
*/
DEPTH32F_STENCIL8: 0x8CAD,
/**
@type {GLenum}
@name PicoGL.FLOAT_32_UNSIGNED_INT_24_8_REV
@private
*/
FLOAT_32_UNSIGNED_INT_24_8_REV: 0x8DAD,
/**
@type {GLenum}
@name PicoGL.FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING
@private
*/
FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING: 0x8210,
/**
@type {GLenum}
@name PicoGL.FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE
@private
*/
FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE: 0x8211,
/**
@type {GLenum}
@name PicoGL.FRAMEBUFFER_ATTACHMENT_RED_SIZE
@private
*/
FRAMEBUFFER_ATTACHMENT_RED_SIZE: 0x8212,
/**
@type {GLenum}
@name PicoGL.FRAMEBUFFER_ATTACHMENT_GREEN_SIZE
@private
*/
FRAMEBUFFER_ATTACHMENT_GREEN_SIZE: 0x8213,
/**
@type {GLenum}
@name PicoGL.FRAMEBUFFER_ATTACHMENT_BLUE_SIZE
@private
*/
FRAMEBUFFER_ATTACHMENT_BLUE_SIZE: 0x8214,
/**
@type {GLenum}
@name PicoGL.FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE
@private
*/
FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE: 0x8215,
/**
@type {GLenum}
@name PicoGL.FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE
@private
*/
FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE: 0x8216,
/**
@type {GLenum}
@name PicoGL.FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE
@private
*/
FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE: 0x8217,
/**
@type {GLenum}
@name PicoGL.FRAMEBUFFER_DEFAULT
@private
*/
FRAMEBUFFER_DEFAULT: 0x8218,
/**
@type {GLenum}
@name PicoGL.UNSIGNED_INT_24_8
@private
*/
UNSIGNED_INT_24_8: 0x84FA,
/**
@type {GLenum}
@name PicoGL.DEPTH24_STENCIL8
@private
*/
DEPTH24_STENCIL8: 0x88F0,
/**
@type {GLenum}
@name PicoGL.UNSIGNED_NORMALIZED
@private
*/
UNSIGNED_NORMALIZED: 0x8C17,
/**
@type {GLenum}
@name PicoGL.DRAW_FRAMEBUFFER_BINDING
@private
*/
DRAW_FRAMEBUFFER_BINDING: 0x8CA6,
/**
@type {GLenum}
@name PicoGL.READ_FRAMEBUFFER
@private
*/
READ_FRAMEBUFFER: 0x8CA8,
/**
@type {GLenum}
@name PicoGL.DRAW_FRAMEBUFFER
@private
*/
DRAW_FRAMEBUFFER: 0x8CA9,
/**
@type {GLenum}
@name PicoGL.READ_FRAMEBUFFER_BINDING
@private
*/
READ_FRAMEBUFFER_BINDING: 0x8CAA,
/**
@type {GLenum}
@name PicoGL.RENDERBUFFER_SAMPLES
@private
*/
RENDERBUFFER_SAMPLES: 0x8CAB,
/**
@type {GLenum}
@name PicoGL.FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER
@private
*/
FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER: 0x8CD4,
/**
@type {GLenum}
@name PicoGL.MAX_COLOR_ATTACHMENTS
@private
*/
MAX_COLOR_ATTACHMENTS: 0x8CDF,
/**
@type {GLenum}
@name PicoGL.COLOR_ATTACHMENT1
@private
*/
COLOR_ATTACHMENT1: 0x8CE1,
/**
@type {GLenum}
@name PicoGL.COLOR_ATTACHMENT2
@private
*/
COLOR_ATTACHMENT2: 0x8CE2,
/**
@type {GLenum}
@name PicoGL.COLOR_ATTACHMENT3
@private
*/
COLOR_ATTACHMENT3: 0x8CE3,
/**
@type {GLenum}
@name PicoGL.COLOR_ATTACHMENT4
@private
*/
COLOR_ATTACHMENT4: 0x8CE4,
/**
@type {GLenum}
@name PicoGL.COLOR_ATTACHMENT5
@private
*/
COLOR_ATTACHMENT5: 0x8CE5,
/**
@type {GLenum}
@name PicoGL.COLOR_ATTACHMENT6
@private
*/
COLOR_ATTACHMENT6: 0x8CE6,
/**
@type {GLenum}
@name PicoGL.COLOR_ATTACHMENT7
@private
*/
COLOR_ATTACHMENT7: 0x8CE7,
/**
@type {GLenum}
@name PicoGL.COLOR_ATTACHMENT8
@private
*/
COLOR_ATTACHMENT8: 0x8CE8,
/**
@type {GLenum}
@name PicoGL.COLOR_ATTACHMENT9
@private
*/
COLOR_ATTACHMENT9: 0x8CE9,
/**
@type {GLenum}
@name PicoGL.COLOR_ATTACHMENT10
@private
*/
COLOR_ATTACHMENT10: 0x8CEA,
/**
@type {GLenum}
@name PicoGL.COLOR_ATTACHMENT11
@private
*/
COLOR_ATTACHMENT11: 0x8CEB,
/**
@type {GLenum}
@name PicoGL.COLOR_ATTACHMENT12
@private
*/
COLOR_ATTACHMENT12: 0x8CEC,
/**
@type {GLenum}
@name PicoGL.COLOR_ATTACHMENT13
@private
*/
COLOR_ATTACHMENT13: 0x8CED,
/**
@type {GLenum}
@name PicoGL.COLOR_ATTACHMENT14
@private
*/
COLOR_ATTACHMENT14: 0x8CEE,
/**
@type {GLenum}
@name PicoGL.COLOR_ATTACHMENT15
@private
*/
COLOR_ATTACHMENT15: 0x8CEF,
/**
@type {GLenum}
@name PicoGL.FRAMEBUFFER_INCOMPLETE_MULTISAMPLE
@private
*/
FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: 0x8D56,
/**
@type {GLenum}
@name PicoGL.MAX_SAMPLES
@private
*/
MAX_SAMPLES: 0x8D57,
/**
@type {GLenum}
@name PicoGL.HALF_FLOAT
@private
*/
HALF_FLOAT: 0x140B,
/**
@type {GLenum}
@name PicoGL.RG
@private
*/
RG: 0x8227,
/**
@type {GLenum}
@name PicoGL.RG_INTEGER
@private
*/
RG_INTEGER: 0x8228,
/**
@type {GLenum}
@name PicoGL.R8
@private
*/
R8: 0x8229,
/**
@type {GLenum}
@name PicoGL.RG8
@private
*/
RG8: 0x822B,
/**
@type {GLenum}
@name PicoGL.R16F
@private
*/
R16F: 0x822D,
/**
@type {GLenum}
@name PicoGL.R32F
@private
*/
R32F: 0x822E,
/**
@type {GLenum}
@name PicoGL.RG16F
@private
*/
RG16F: 0x822F,
/**
@type {GLenum}
@name PicoGL.RG32F
@private
*/
RG32F: 0x8230,
/**
@type {GLenum}
@name PicoGL.R8I
@private
*/
R8I: 0x8231,
/**
@type {GLenum}
@name PicoGL.R8UI
@private
*/
R8UI: 0x8232,
/**
@type {GLenum}
@name PicoGL.R16I
@private
*/
R16I: 0x8233,
/**
@type {GLenum}
@name PicoGL.R16UI
@private
*/
R16UI: 0x8234,
/**
@type {GLenum}
@name PicoGL.R32I
@private
*/
R32I: 0x8235,
/**
@type {GLenum}
@name PicoGL.R32UI
@private
*/
R32UI: 0x8236,
/**
@type {GLenum}
@name PicoGL.RG8I
@private
*/
RG8I: 0x8237,
/**
@type {GLenum}
@name PicoGL.RG8UI
@private
*/
RG8UI: 0x8238,
/**
@type {GLenum}
@name PicoGL.RG16I
@private
*/
RG16I: 0x8239,
/**
@type {GLenum}
@name PicoGL.RG16UI
@private
*/
RG16UI: 0x823A,
/**
@type {GLenum}
@name PicoGL.RG32I
@private
*/
RG32I: 0x823B,
/**
@type {GLenum}
@name PicoGL.RG32UI
@private
*/
RG32UI: 0x823C,
/**
@type {GLenum}
@name PicoGL.VERTEX_ARRAY_BINDING
@private
*/
VERTEX_ARRAY_BINDING: 0x85B5,
/**
@type {GLenum}
@name PicoGL.R8_SNORM
@private
*/
R8_SNORM: 0x8F94,
/**
@type {GLenum}
@name PicoGL.RG8_SNORM
@private
*/
RG8_SNORM: 0x8F95,
/**
@type {GLenum}
@name PicoGL.RGB8_SNORM
@private
*/
RGB8_SNORM: 0x8F96,
/**
@type {GLenum}
@name PicoGL.RGBA8_SNORM
@private
*/
RGBA8_SNORM: 0x8F97,
/**
@type {GLenum}
@name PicoGL.SIGNED_NORMALIZED
@private
*/
SIGNED_NORMALIZED: 0x8F9C,
/**
@type {GLenum}
@name PicoGL.COPY_READ_BUFFER
@private
*/
COPY_READ_BUFFER: 0x8F36,
/**
@type {GLenum}
@name PicoGL.COPY_WRITE_BUFFER
@private
*/
COPY_WRITE_BUFFER: 0x8F37,
/**
@type {GLenum}
@name PicoGL.COPY_READ_BUFFER_BINDING
@private
*/
COPY_READ_BUFFER_BINDING: 0x8F36,
/**
@type {GLenum}
@name PicoGL.COPY_WRITE_BUFFER_BINDING
@private
*/
COPY_WRITE_BUFFER_BINDING: 0x8F37,
/**
@type {GLenum}
@name PicoGL.UNIFORM_BUFFER
@private
*/
UNIFORM_BUFFER: 0x8A11,
/**
@type {GLenum}
@name PicoGL.UNIFORM_BUFFER_BINDING
@private
*/
UNIFORM_BUFFER_BINDING: 0x8A28,
/**
@type {GLenum}
@name PicoGL.UNIFORM_BUFFER_START
@private
*/
UNIFORM_BUFFER_START: 0x8A29,
/**
@type {GLenum}
@name PicoGL.UNIFORM_BUFFER_SIZE
@private
*/
UNIFORM_BUFFER_SIZE: 0x8A2A,
/**
@type {GLenum}
@name PicoGL.MAX_VERTEX_UNIFORM_BLOCKS
@private
*/
MAX_VERTEX_UNIFORM_BLOCKS: 0x8A2B,
/**
@type {GLenum}
@name PicoGL.MAX_FRAGMENT_UNIFORM_BLOCKS
@private
*/
MAX_FRAGMENT_UNIFORM_BLOCKS: 0x8A2D,
/**
@type {GLenum}
@name PicoGL.MAX_COMBINED_UNIFORM_BLOCKS
@private
*/
MAX_COMBINED_UNIFORM_BLOCKS: 0x8A2E,
/**
@type {GLenum}
@name PicoGL.MAX_UNIFORM_BUFFER_BINDINGS
@private
*/
MAX_UNIFORM_BUFFER_BINDINGS: 0x8A2F,
/**
@type {GLenum}
@name PicoGL.MAX_UNIFORM_BLOCK_SIZE
@private
*/
MAX_UNIFORM_BLOCK_SIZE: 0x8A30,
/**
@type {GLenum}
@name PicoGL.MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS
@private
*/
MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS: 0x8A31,
/**
@type {GLenum}
@name PicoGL.MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS
@private
*/
MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS: 0x8A33,
/**
@type {GLenum}
@name PicoGL.UNIFORM_BUFFER_OFFSET_ALIGNMENT
@private
*/
UNIFORM_BUFFER_OFFSET_ALIGNMENT: 0x8A34,
/**
@type {GLenum}
@name PicoGL.ACTIVE_UNIFORM_BLOCKS
@private
*/
ACTIVE_UNIFORM_BLOCKS: 0x8A36,
/**
@type {GLenum}
@name PicoGL.UNIFORM_TYPE
@private
*/
UNIFORM_TYPE: 0x8A37,
/**
@type {GLenum}
@name PicoGL.UNIFORM_SIZE
@private
*/
UNIFORM_SIZE: 0x8A38,
/**
@type {GLenum}
@name PicoGL.UNIFORM_BLOCK_INDEX
@private
*/
UNIFORM_BLOCK_INDEX: 0x8A3A,
/**
@type {GLenum}
@name PicoGL.UNIFORM_OFFSET
@private
*/
UNIFORM_OFFSET: 0x8A3B,
/**
@type {GLenum}
@name PicoGL.UNIFORM_ARRAY_STRIDE
@private
*/
UNIFORM_ARRAY_STRIDE: 0x8A3C,
/**
@type {GLenum}
@name PicoGL.UNIFORM_MATRIX_STRIDE
@private
*/
UNIFORM_MATRIX_STRIDE: 0x8A3D,
/**
@type {GLenum}
@name PicoGL.UNIFORM_IS_ROW_MAJOR
@private
*/
UNIFORM_IS_ROW_MAJOR: 0x8A3E,
/**
@type {GLenum}
@name PicoGL.UNIFORM_BLOCK_BINDING
@private
*/
UNIFORM_BLOCK_BINDING: 0x8A3F,
/**
@type {GLenum}
@name PicoGL.UNIFORM_BLOCK_DATA_SIZE
@private
*/
UNIFORM_BLOCK_DATA_SIZE: 0x8A40,
/**
@type {GLenum}
@name PicoGL.UNIFORM_BLOCK_ACTIVE_UNIFORMS
@private
*/
UNIFORM_BLOCK_ACTIVE_UNIFORMS: 0x8A42,
/**
@type {GLenum}
@name PicoGL.UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES
@private
*/
UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES: 0x8A43,
/**
@type {GLenum}
@name PicoGL.UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER
@private
*/
UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER: 0x8A44,
/**
@type {GLenum}
@name PicoGL.UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER
@private
*/
UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER: 0x8A46,
/**
@type {GLenum}
@name PicoGL.INVALID_INDEX
@private
*/
INVALID_INDEX: 0xFFFFFFFF,
/**
@type {GLenum}
@name PicoGL.MAX_VERTEX_OUTPUT_COMPONENTS
@private
*/
MAX_VERTEX_OUTPUT_COMPONENTS: 0x9122,
/**
@type {GLenum}
@name PicoGL.MAX_FRAGMENT_INPUT_COMPONENTS
@private
*/
MAX_FRAGMENT_INPUT_COMPONENTS: 0x9125,
/**
@type {GLenum}
@name PicoGL.MAX_SERVER_WAIT_TIMEOUT
@private
*/
MAX_SERVER_WAIT_TIMEOUT: 0x9111,
/**
@type {GLenum}
@name PicoGL.OBJECT_TYPE
@private
*/
OBJECT_TYPE: 0x9112,
/**
@type {GLenum}
@name PicoGL.SYNC_CONDITION
@private
*/
SYNC_CONDITION: 0x9113,
/**
@type {GLenum}
@name PicoGL.SYNC_STATUS
@private
*/
SYNC_STATUS: 0x9114,
/**
@type {GLenum}
@name PicoGL.SYNC_FLAGS
@private
*/
SYNC_FLAGS: 0x9115,
/**
@type {GLenum}
@name PicoGL.SYNC_FENCE
@private
*/
SYNC_FENCE: 0x9116,
/**
@type {GLenum}
@name PicoGL.SYNC_GPU_COMMANDS_COMPLETE
@private
*/
SYNC_GPU_COMMANDS_COMPLETE: 0x9117,
/**
@type {GLenum}
@name PicoGL.UNSIGNALED
@private
*/
UNSIGNALED: 0x9118,
/**
@type {GLenum}
@name PicoGL.SIGNALED
@private
*/
SIGNALED: 0x9119,
/**
@type {GLenum}
@name PicoGL.ALREADY_SIGNALED
@private
*/
ALREADY_SIGNALED: 0x911A,
/**
@type {GLenum}
@name PicoGL.TIMEOUT_EXPIRED
@private
*/
TIMEOUT_EXPIRED: 0x911B,
/**
@type {GLenum}
@name PicoGL.CONDITION_SATISFIED
@private
*/
CONDITION_SATISFIED: 0x911C,
/**
@type {GLenum}
@name PicoGL.WAIT_FAILED
@private
*/
WAIT_FAILED: 0x911D,
/**
@type {GLenum}
@name PicoGL.SYNC_FLUSH_COMMANDS_BIT
@private
*/
SYNC_FLUSH_COMMANDS_BIT: 0x00000001,
/**
@type {GLenum}
@name PicoGL.VERTEX_ATTRIB_ARRAY_DIVISOR
@private
*/
VERTEX_ATTRIB_ARRAY_DIVISOR: 0x88FE,
/**
@type {GLenum}
@name PicoGL.ANY_SAMPLES_PASSED
@private
*/
ANY_SAMPLES_PASSED: 0x8C2F,
/**
@type {GLenum}
@name PicoGL.ANY_SAMPLES_PASSED_CONSERVATIVE
@private
*/
ANY_SAMPLES_PASSED_CONSERVATIVE: 0x8D6A,
/**
@type {GLenum}
@name PicoGL.SAMPLER_BINDING
@private
*/
SAMPLER_BINDING: 0x8919,
/**
@type {GLenum}
@name PicoGL.RGB10_A2UI
@private
*/
RGB10_A2UI: 0x906F,
/**
@type {GLenum}
@name PicoGL.INT_2_10_10_10_REV
@private
*/
INT_2_10_10_10_REV: 0x8D9F,
/**
@type {GLenum}
@name PicoGL.TRANSFORM_FEEDBACK
@private
*/
TRANSFORM_FEEDBACK: 0x8E22,
/**
@type {GLenum}
@name PicoGL.TRANSFORM_FEEDBACK_PAUSED
@private
*/
TRANSFORM_FEEDBACK_PAUSED: 0x8E23,
/**
@type {GLenum}
@name PicoGL.TRANSFORM_FEEDBACK_ACTIVE
@private
*/
TRANSFORM_FEEDBACK_ACTIVE: 0x8E24,
/**
@type {GLenum}
@name PicoGL.TRANSFORM_FEEDBACK_BINDING
@private
*/
TRANSFORM_FEEDBACK_BINDING: 0x8E25,
/**
@type {GLenum}
@name PicoGL.TEXTURE_IMMUTABLE_FORMAT
@private
*/
TEXTURE_IMMUTABLE_FORMAT: 0x912F,
/**
@type {GLenum}
@name PicoGL.MAX_ELEMENT_INDEX
@private
*/
MAX_ELEMENT_INDEX: 0x8D6B,
/**
@type {GLenum}
@name PicoGL.TEXTURE_IMMUTABLE_LEVELS
@private
*/
TEXTURE_IMMUTABLE_LEVELS: 0x82DF,
/**
@type {GLenum}
@name PicoGL.TIMEOUT_IGNORED
@private
*/
TIMEOUT_IGNORED: -1,
/**
@type {GLenum}
@name PicoGL.MAX_CLIENT_WAIT_TIMEOUT_WEBGL
@private
*/
MAX_CLIENT_WAIT_TIMEOUT_WEBGL: 0x9247,
// https://www.khronos.org/registry/webgl/extensions/EXT_disjoint_timer_query_webgl2/
/**
@type {GLenum}
@name PicoGL.QUERY_COUNTER_BITS_EXT
@private
*/
QUERY_COUNTER_BITS_EXT: 0x8864,
/**
@type {GLenum}
@name PicoGL.TIME_ELAPSED_EXT
@private
*/
TIME_ELAPSED_EXT: 0x88BF,
/**
@type {GLenum}
@name PicoGL.TIMESTAMP_EXT
@private
*/
TIMESTAMP_EXT: 0x8E28,
/**
@type {GLenum}
@name PicoGL.GPU_DISJOINT_EXT
@private
*/
GPU_DISJOINT_EXT: 0x8FBB,
// https://www.khronos.org/registry/webgl/extensions/EXT_texture_filter_anisotropic/
/**
@type {GLenum}
@name PicoGL.TEXTURE_MAX_ANISOTROPY_EXT
@private
*/
TEXTURE_MAX_ANISOTROPY_EXT: 0x84FE,
/**
@type {GLenum}
@name PicoGL.MAX_TEXTURE_MAX_ANISOTROPY_EXT
@private
*/
MAX_TEXTURE_MAX_ANISOTROPY_EXT: 0x84FF,
// https://www.khronos.org/registry/webgl/extensions/WEBGL_debug_renderer_info/
/**
@type {GLenum}
@name PicoGL.UNMASKED_VENDOR_WEBGL
@private
*/
UNMASKED_VENDOR_WEBGL: 0x9245,
/**
@type {GLenum}
@name PicoGL.UNMASKED_RENDERER_WEBGL
@private
*/
UNMASKED_RENDERER_WEBGL: 0x9246,
// https://www.khronos.org/registry/webgl/extensions/KHR_parallel_shader_compile/
/**
@type {GLenum}
@name PicoGL.COMPLETION_STATUS_KHR
@private
*/
COMPLETION_STATUS_KHR: 0x91B1,
// https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_s3tc/
/**
@type {GLenum}
@name PicoGL.COMPRESSED_RGB_S3TC_DXT1_EXT
@private
*/
COMPRESSED_RGB_S3TC_DXT1_EXT: 0x83F0,
/**
@type {GLenum}
@name PicoGL.COMPRESSED_RGBA_S3TC_DXT1_EXT
@private
*/
COMPRESSED_RGBA_S3TC_DXT1_EXT: 0x83F1,
/**
@type {GLenum}
@name PicoGL.COMPRESSED_RGBA_S3TC_DXT3_EXT
@private
*/
COMPRESSED_RGBA_S3TC_DXT3_EXT: 0x83F2,
/**
@type {GLenum}
@name PicoGL.COMPRESSED_RGBA_S3TC_DXT5_EXT
@private
*/
COMPRESSED_RGBA_S3TC_DXT5_EXT: 0x83F3,
// https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_s3tc_srgb/
/**
@type {GLenum}
@name PicoGL.COMPRESSED_SRGB_S3TC_DXT1_EXT
@private
*/
COMPRESSED_SRGB_S3TC_DXT1_EXT: 0x8C4C,
/**
@type {GLenum}
@name PicoGL.COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT
@private
*/
COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT: 0x8C4D,
/**
@type {GLenum}
@name PicoGL.COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT
@private
*/
COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT: 0x8C4E,
/**
@type {GLenum}
@name PicoGL.COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT
@private
*/
COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT: 0x8C4F,
// https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_etc/
/**
@type {GLenum}
@name PicoGL.COMPRESSED_R11_EAC
@private
*/
COMPRESSED_R11_EAC: 0x9270,
/**
@type {GLenum}
@name PicoGL.COMPRESSED_SIGNED_R11_EAC
@private
*/
COMPRESSED_SIGNED_R11_EAC: 0x9271,
/**
@type {GLenum}
@name PicoGL.COMPRESSED_RG11_EAC
@private
*/
COMPRESSED_RG11_EAC: 0x9272,
/**
@type {GLenum}
@name PicoGL.COMPRESSED_SIGNED_RG11_EAC
@private
*/
COMPRESSED_SIGNED_RG11_EAC: 0x9273,
/**
@type {GLenum}
@name PicoGL.COMPRESSED_RGB8_ETC2
@private
*/
COMPRESSED_RGB8_ETC2: 0x9274,
/**
@type {GLenum}
@name PicoGL.COMPRESSED_SRGB8_ETC2
@private
*/
COMPRESSED_SRGB8_ETC2: 0x9275,
/**
@type {GLenum}
@name PicoGL.COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2
@private
*/
COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2: 0x9276,
/**
@type {GLenum}
@name PicoGL.COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2
@private
*/
COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2: 0x9277,
/**
@type {GLenum}
@name PicoGL.COMPRESSED_RGBA8_ETC2_EAC
@private
*/
COMPRESSED_RGBA8_ETC2_EAC: 0x9278,
/**
@type {GLenum}
@name PicoGL.COMPRESSED_SRGB8_ALPHA8_ETC2_EAC
@private
*/
COMPRESSED_SRGB8_ALPHA8_ETC2_EAC: 0x9279,
// https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_pvrtc/
/**
@type {GLenum}
@name PicoGL.COMPRESSED_RGB_PVRTC_4BPPV1_IMG
@private
*/
COMPRESSED_RGB_PVRTC_4BPPV1_IMG: 0x8C00,
/**
@type {GLenum}
@name PicoGL.COMPRESSED_RGB_PVRTC_2BPPV1_IMG
@private
*/
COMPRESSED_RGB_PVRTC_2BPPV1_IMG: 0x8C01,
/**
@type {GLenum}
@name PicoGL.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG
@private
*/
COMPRESSED_RGBA_PVRTC_4BPPV1_IMG: 0x8C02,
/**
@type {GLenum}
@name PicoGL.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG
@private
*/
COMPRESSED_RGBA_PVRTC_2BPPV1_IMG: 0x8C03,
// https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_astc/
/**
@type {GLenum}
@name PicoGL.COMPRESSED_RGBA_ASTC_4x4_KHR
@private
*/
COMPRESSED_RGBA_ASTC_4x4_KHR: 0x93B0,
/**
@type {GLenum}
@name PicoGL.COMPRESSED_RGBA_ASTC_5x4_KHR
@private
*/
COMPRESSED_RGBA_ASTC_5x4_KHR: 0x93B1,
/**
@type {GLenum}
@name PicoGL.COMPRESSED_RGBA_ASTC_5x5_KHR
@private
*/
COMPRESSED_RGBA_ASTC_5x5_KHR: 0x93B2,
/**
@type {GLenum}
@name PicoGL.COMPRESSED_RGBA_ASTC_6x5_KHR
@private
*/
COMPRESSED_RGBA_ASTC_6x5_KHR: 0x93B3,
/**
@type {GLenum}
@name PicoGL.COMPRESSED_RGBA_ASTC_6x6_KHR
@private
*/
COMPRESSED_RGBA_ASTC_6x6_KHR: 0x93B4,
/**
@type {GLenum}
@name PicoGL.COMPRESSED_RGBA_ASTC_8x5_KHR
@private
*/
COMPRESSED_RGBA_ASTC_8x5_KHR: 0x93B5,
/**
@type {GLenum}
@name PicoGL.COMPRESSED_RGBA_ASTC_8x6_KHR
@private
*/
COMPRESSED_RGBA_ASTC_8x6_KHR: 0x93B6,
/**
@type {GLenum}
@name PicoGL.COMPRESSED_RGBA_ASTC_8x8_KHR
@private
*/
COMPRESSED_RGBA_ASTC_8x8_KHR: 0x93B7,
/**
@type {GLenum}
@name PicoGL.COMPRESSED_RGBA_ASTC_10x5_KHR
@private
*/
COMPRESSED_RGBA_ASTC_10x5_KHR: 0x93B8,
/**
@type {GLenum}
@name PicoGL.COMPRESSED_RGBA_ASTC_10x6_KHR
@private
*/
COMPRESSED_RGBA_ASTC_10x6_KHR: 0x93B9,
/**
@type {GLenum}
@name PicoGL.COMPRESSED_RGBA_ASTC_10x8_KHR
@private
*/
COMPRESSED_RGBA_ASTC_10x8_KHR: 0x93BA,
/**
@type {GLenum}
@name PicoGL.COMPRESSED_RGBA_ASTC_10x10_KHR
@private
*/
COMPRESSED_RGBA_ASTC_10x10_KHR: 0x93BB,
/**
@type {GLenum}
@name PicoGL.COMPRESSED_RGBA_ASTC_12x10_KHR
@private
*/
COMPRESSED_RGBA_ASTC_12x10_KHR: 0x93BC,
/**
@type {GLenum}
@name PicoGL.COMPRESSED_RGBA_ASTC_12x12_KHR
@private
*/
COMPRESSED_RGBA_ASTC_12x12_KHR: 0x93BD,
/**
@type {GLenum}
@name PicoGL.COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR
@private
*/
COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR: 0x93D0,
/**
@type {GLenum}
@name PicoGL.COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR
@private
*/
COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR: 0x93D1,
/**
@type {GLenum}
@name PicoGL.COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR
@private
*/
COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR: 0x93D2,
/**
@type {GLenum}
@name PicoGL.COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR
@private
*/
COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR: 0x93D3,
/**
@type {GLenum}
@name PicoGL.COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR
@private
*/
COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR: 0x93D4,
/**
@type {GLenum}
@name PicoGL.COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR
@private
*/
COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR: 0x93D5,
/**
@type {GLenum}
@name PicoGL.COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR
@private
*/
COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR: 0x93D6,
/**
@type {GLenum}
@name PicoGL.COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR
@private
*/
COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR: 0x93D7,
/**
@type {GLenum}
@name PicoGL.COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR
@private
*/
COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR: 0x93D8,
/**
@type {GLenum}
@name PicoGL.COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR
@private
*/
COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR: 0x93D9,
/**
@type {GLenum}
@name PicoGL.COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR
@private
*/
COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR: 0x93DA,
/**
@type {GLenum}
@name PicoGL.COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR
@private
*/
COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR: 0x93DB,
/**
@type {GLenum}
@name PicoGL.COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR
@private
*/
COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR: 0x93DC,
/**
@type {GLenum}
@name PicoGL.COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR
@private
*/
COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR: 0x93DD
};
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]
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 ]
};
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
};
const WEBGL_INFO = {};
const DUMMY_UNIT_ARRAY = new Array(1);
const DUMMY_OBJECT = {};
// DEPRECATED
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
}
};
///////////////////////////////////////////////////////////////////////////////////
/**
Cubemap for environment mapping.
@class Cubemap
@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.
*/
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 {HTMLElement|ArrayBufferView} [options.negX] The image data for the negative X direction.
Can be any format that would be accepted by texImage2D.
@param {HTMLElement|ArrayBufferView} [options.posX] The image data for the positive X direction.
Can be any format that would be accepted by texImage2D.
@param {HTMLElement|ArrayBufferView} [options.negY] The image data for the negative Y direction.
Can be any format that would be accepted by texImage2D.
@param {HTMLElement|ArrayBufferView} [options.posY] The image data for the positive Y direction.
Can be any format that would be accepted by texImage2D.
@param {HTMLElement|ArrayBufferView} [options.negZ] The image data for the negative Z direction.
Can be any format that would be accepted by texImage2D.
@param {HTMLElement|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.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;
this.gl.pixelStorei(GL.UNPACK_FLIP_Y_WEBGL, this.flipY);
this.gl.pixelStorei(GL.UNPACK_PREMULTIPLY_ALPHA_WEBGL, this.premultiplyAlpha);
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 (this.appState.activeTexture !== unit) {
this.gl.activeTexture(GL.TEXTURE0 + unit);
this.appState.activeTexture = unit;
}
if (currentTexture !== this) {
if (currentTexture) {
currentTexture.currentUnit = -1;
}
if (this.currentUnit !== -1) {
this.appState.textures[this.currentUnit] = null;
}
this.gl.bindTexture(GL.TEXTURE_CUBE_MAP, this.texture);
this.appState.textures[unit] = this;
this.currentUnit = unit;
}
return this;
}
}
///////////////////////////////////////////////////////////////////////////////////
/**
A DrawCall represents the program and values of associated
attributes, uniforms and textures for a single draw call.
@class DrawCall
@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 {Object} appState Tracked GL state.
@prop {GLsizei} numElements The number of element to draw.
@prop {GLsizei} numInstances The number of instances to draw.
*/
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);
this.numDraws = 1;
this.drawCountsFromVertexArray = true;
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. Note that after this method is called, draw counts will no longer
automatically be pulled from the VertexArray.
@method
@param {...Array} counts Variable number of 2 or 3 element arrays, each containing:
<ul>
<li> (Number) Number of elements to skip at the start of the array.
<li> (Number) Number of elements to draw.
<li> (Number - optional) Number of instances to draw of the given range.
</ul>
@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;
}
this.drawCountsFromVertexArray = false;
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;
if (this.drawCountsFromVertexArray) {
this.numElements[0] = this.currentVertexArray.numElements;
this.numInstances[0] = this.currentVertexArray.numInstances;
}
}
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;
}
}
///////////////////////////////////////////////////////////////////////////////////
/**
General-purpose texture.
@class Texture
@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.
*/
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 {HTMLElement|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);
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 {HTMLImageElement|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));
this.gl.pixelStorei(GL.UNPACK_FLIP_Y_WEBGL, this.flipY);
this.gl.pixelStorei(GL.UNPACK_PREMULTIPLY_ALPHA_WEBGL, this.premultiplyAlpha);
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 (this.appState.activeTexture !== unit) {
this.gl.activeTexture(GL.TEXTURE0 + unit);
this.appState.activeTexture = unit;
}
if (currentTexture !== this) {
if (currentTexture) {
currentTexture.currentUnit = -1;
}
if (this.currentUnit !== -1) {
this.appState.textures[this.currentUnit] = null;
}
this.gl.bindTexture(this.binding, this.texture);
this.appState.textures[unit] = this;
this.currentUnit = unit;
}
return this;
}
}
///////////////////////////////////////////////////////////////////////////////////
/**
Offscreen drawing attachment.
@class Renderbuffer
@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.
*/
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;
}
}
///////////////////////////////////////////////////////////////////////////////////
/**
Offscreen drawing surface.
@class Framebuffer
@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.
*/
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() {
let currentFramebuffers = this.appState.framebuffers;
for (let binding in currentFramebuffers) {
if (currentFramebuffers[binding] === this) {
currentFramebuffers[binding] = 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();
let binding = this.appState.drawFramebufferBinding;
if (attachment instanceof Renderbuffer) {
this.gl.framebufferRenderbuffer(binding, this.colorAttachmentEnums[index], GL.RENDERBUFFER, attachment.renderbuffer);
} else if (attachment.is3D) {
this.gl.framebufferTextureLayer(binding, this.colorAttachmentEnums[index], attachment.texture, 0, target);
} else {
this.gl.framebufferTexture2D(binding, 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();
let binding = this.appState.drawFramebufferBinding;
this.depthAttachment = attachment;
this.depthAttachmentTarget = target;
if (attachment instanceof Renderbuffer) {
this.gl.framebufferRenderbuffer(binding, GL.DEPTH_ATTACHMENT, GL.RENDERBUFFER, attachment.renderbuffer);
} else if (attachment.is3D) {
this.gl.framebufferTextureLayer(binding, GL.DEPTH_ATTACHMENT, attachment.texture, 0, target);
} else {
this.gl.framebufferTexture2D(binding, 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();
let binding = this.appState.drawFramebufferBinding;
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(binding, this.colorAttachmentEnums[i], attachment.texture, 0, this.colorAttachmentTargets[i]);
} else {
this.gl.framebufferTexture2D(binding, 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(binding, GL.DEPTH_ATTACHMENT, this.depthAttachment.texture, 0, this.depthAttachmentTarget);
} else {
this.gl.framebufferTexture2D(binding, 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;
let currentFramebuffers = this.appState.framebuffers;
for (let binding in currentFramebuffers) {
if (currentFramebuffers[binding] === this) {
this.gl.bindFramebuffer(binding, null);
currentFramebuffers[binding] = 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 binding = this.appState.drawFramebufferBinding;
let status = this.gl.checkFramebufferStatus(binding);
this.restoreState(currentFramebuffer);
return status;
}
/**
Bind as the draw framebuffer
@method
@ignore
@return {Framebuffer} The Framebuffer object.
*/
bindForDraw() {
let binding = this.appState.drawFramebufferBinding;
let currentFramebuffers = this.appState.framebuffers;
if (currentFramebuffers[binding] !== this) {
this.gl.bindFramebuffer(binding, this.framebuffer);
currentFramebuffers[binding] = this;
}
return this;
}
/**
Bind as the read framebuffer
@method
@ignore
@return {Framebuffer} The Framebuffer object.
*/
bindForRead() {
let binding = this.appState.readFramebufferBinding;
let currentFramebuffers = this.appState.framebuffers;
if (currentFramebuffers[binding] !== this) {
this.gl.bindFramebuffer(binding, this.framebuffer);
currentFramebuffers[binding] = 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 binding = this.appState.drawFramebufferBinding;
let currentFramebuffer = this.appState.framebuffers[binding];
if (currentFramebuffer !== this) {
this.gl.bindFramebuffer(binding, this.framebuffer);
}
return currentFramebuffer;
}
/**
Bind restore previous binding after state update
@method
@ignore
@return {Framebuffer} The Framebuffer object.
*/
restoreState(framebuffer) {
if (framebuffer !== this) {
let binding = this.appState.drawFramebufferBinding;
this.gl.bindFramebuffer(binding, 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;
}
}
///////////////////////////////////////////////////////////////////////////////////
/**
WebGL shader.
@class Shader
@prop {WebGLRenderingContext} gl The WebGL context.
@prop {WebGLShader} shader The shader.
*/
class Shader {
constructor(gl, appState, type, source) {
this.gl = gl;
this.appState = appState;
this.shader = null;
this.type = type;
this.source = source.trim();
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;
}
}
///////////////////////////////////////////////////////////////////////////////////
// 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;
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;
}
}
}
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;
}
}
}
}
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;
}
}
}
}
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;
}
}
}
}
///////////////////////////////////////////////////////////////////////////////////
/**
WebGL program consisting of compiled and linked vertex and fragment
shaders.
@class Program
@prop {WebGLRenderingContext} gl The WebGL context.
@prop {WebGLProgram} program The WebGL program.
@prop {array} transformFeedbackVaryings Names of transform feedback varyings, if any.
@prop {GLenum} transformFeedbackMode Capture mode of the transform feedback.
@prop {Object.<string, number>} attributeLocations Map of user-provided attribute names to indices, if any.
@prop {Object} uniforms Map of uniform names to handles.
@prop {Object} appState Tracked GL state.
*/
class Program {
constructor(gl, appState, vsSource, fsSource, xformFeebackVars, attributeLocations, transformFeedbackMode) {
this.gl = gl;
this.appState = appState;
this.program = null;
this.transformFeedbackVaryings = xformFeebackVars || null;
this.transformFeedbackMode = transformFeedbackMode || GL.SEPARATE_ATTRIBS;
this.attributeLocations = attributeLocations || 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. <b>App.restorePrograms</b>
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, this.transformFeedbackMode);
}
if (this.attributeLocations) {
for (let name in this.attributeLocations) {
this.gl.bindAttribLocation(this.program, this.attributeLocations[name], name);
}
}
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;
}
}
///////////////////////////////////////////////////////////////////////////////////
/**
Generic query object.
@class Query
@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).
*/
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;
}
}
///////////////////////////////////////////////////////////////////////////////////
/**
Rendering timer.
@class Timer
@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.
*/
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;
}
}
///////////////////////////////////////////////////////////////////////////////////
/**
Tranform feedback object.
@class TransformFeedback
@prop {WebGLRenderingContext} gl The WebGL context.
@prop {WebGLTransformFeedback} transformFeedback Transform feedback object.
@prop {Object} appState Tracked GL state.
*/
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;
}
}
///////////////////////////////////////////////////////////////////////////////////
/**
Storage for uniform data. Data is stored in std140 layout.
@class UniformBuffer
@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.
*/
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;
}
this.currentBase = -1;
}
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;
}
}
// Copyright (c) 2017 Tarek Sherif
/**
Organizes vertex buffer and attribute state.
@class VertexArray
@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.
*/
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 {boolean} [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];
}
let numItems = Math.ceil((vertexBuffer.byteLength - offset) / stride);
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 = numItems;
} else if (!this.indexed) {
this.numElements = numItems;
}
}
this.gl.bindBuffer(GL.ARRAY_BUFFER, null);
return this;
}
}
///////////////////////////////////////////////////////////////////////////////////
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 VertexBuffer
@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.
*/
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 {ArrayBufferView} data Data to store in the buffer.
@param {number} [offset=0] Byte offset into the buffer at which to start writing.
@return {VertexBuffer} The VertexBuffer object.
*/
data(data, offset = 0) {
// 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, offset, 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;
}
}
///////////////////////////////////////////////////////////////////////////////////
/**
Primary entry point to PicoGL. An app will store all parts of the WebGL
state.
@class App
@param {WebGLRenderingContext} gl
@prop {HTMLElement} 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 {Object} state.drawFramebufferBinding=GL.DRAW_FRAMEBUFFER Binding point to bind framebuffers to for draw. Should be set before any binding occurs. Should only have values GL.DRAW_FRAMEBUFFER or GL.FRAMEBUFFER (the latter with state.readFramebufferBinding set to the same).
@prop {Object} state.readFramebufferBinding=GL.READ_FRAMEBUFFER Binding point to bind framebuffers to for read. Should be set before any binding occurs. Should only have values GL.READ_FRAMEBUFFER or GL.FRAMEBUFFER (the latter with state.drawFramebufferBinding set to the same).
@prop {GLenum} clearBits Current clear mask to use with clear().
*/
class App {
constructor(gl) {
this.gl = gl;
this.canvas = gl.canvas;
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: [],
framebuffers: {},
drawFramebufferBinding: GL.DRAW_FRAMEBUFFER,
readFramebufferBinding: GL.READ_FRAMEBUFFER,
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.contextLostListener = null;
this.contextRestoredListener = null;
this.contextRestoredHandler = null;
this.initExtensions();
}
/**
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;
this.initContextListeners();
return this;
}
/**
Enable WebGL capability (e.g. depth testing, blending, etc.).
@method
@param {GLenum} cap Capability to enable.
@return {App} The App object.
*/
enable(cap) {
this.gl.enable(cap);
return this;
}
/**
Disable WebGL capability (e.g. depth testing, blending, etc.).
@method
@param {GLenum} cap Capability to disable.
@return {App} The App object.
*/
disable(cap) {
this.gl.disable(cap);
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() {
let binding = this.state.drawFramebufferBinding;
if (this.state.framebuffers[binding] !== null) {
this.gl.bindFramebuffer(binding, null);
this.state.framebuffers[binding] = null;
}
return this;
}
/**
Switch back to the default framebuffer for reading (i.e. read from the screen).
@method
@return {App} The App object.
*/
defaultReadFramebuffer() {
let binding = this.state.readFramebufferBinding;
if (this.state.framebuffers[binding] !== null) {
this.gl.bindFramebuffer(binding, null);
this.state.framebuffers[binding] = 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 readBinding = this.state.readFramebufferBinding;
let drawBinding = this.state.drawFramebufferBinding;
let readFramebuffer = this.state.framebuffers[readBinding];
let drawFramebuffer = this.state.framebuffers[drawBinding];
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 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;
}
/**
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;
}
/**
Set the blend equation. E.g. app.blendEquation(PicoGL.MIN).
@method
@param {GLenum} mode The operation to use in combining source and destination channels.
@return {App} The App object.
*/
blendEquation(mode) {
this.gl.blendEquation(mode);
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;
}
/**
Define the scissor box.
@method
@param {Number} x Horizontal position of the scissor box.
@param {Number} y Vertical position of the scissor box.
@param {Number} width Width of the scissor box.
@param {Number} height Height of the scissor box.
@return {App} The App object.
*/
scissor(x, y, width, height) {
this.gl.scissor(x, y, width, height);
return this;
}
/**
Set the scale and units used.
@method
@param {Number} factor Scale factor used to create a variable depth offset for each polygon.
@param {Number} units Constant depth offset.
@return {App} The App object.
*/
polygonOffset(factor, units) {
this.gl.polygonOffset(factor, units);
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 <b>createPrograms</b> 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 {Object} [options] Texture options.
@param {Object} [options.attributeLocations] Map of attribute names to locations (useful when using GLSL 1).
@param {Array} [options.transformFeedbackVaryings] Array of varying names used for transform feedback output.
@param {GLenum} [options.transformFeedbackMode] Capture mode of the transform feedback. (Default: PicoGL.SEPARATE_ATTRIBS).
@return {Program} New Program object.
*/
createProgram(vsSource, fsSource, opts = {}) {
let {transformFeedbackVaryings, attributeLocations, transformFeedbackMode} = opts;
return new Program(this.gl, this.state, vsSource, fsSource, transformFeedbackVaryings, attributeLocations, transformFeedbackMode)
.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:
<ul>
<li> (Shader|string) Vertex shader object or source code.
<li> (Shader|string) Fragment shader object or source code.
<li> (Object - optional) Optional program parameters.
<ul>
<li>(Object - optional) <strong><code>attributeLocations</code></strong> Map of attribute names to locations (useful when using GLSL 1).
<li>(Array - optional) <strong><code>transformFeedbackVaryings</code></strong> Array of varying names used for transform feedback output.
<li>(GLenum - optional) <strong><code>transformFeedbackMode</code></strong> Capture mode of the transform feedback. (Default: PicoGL.SEPARATE_ATTRIBS).
</ul>
</ul>
</ul>
@return {Promise<Program[]>} 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 opts = source[2] || {};
let {transformFeedbackVaryings, attributeLocations, transformFeedbackMode} = opts;
programs[i] = new Program(this.gl, this.state, vsSource, fsSource, transformFeedbackVaryings, attributeLocations, transformFeedbackMode);
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<void>} 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. If the `itemSize` is not specified, it defaults to 3
@method
@variation 1
@param {GLenum} type The data type stored in the index buffer.
@param {ArrayBufferView} data Index buffer data.
@param {GLenum} [usage=STATIC_DRAW] Buffer usage.
@return {VertexBuffer} New VertexBuffer object.
*//**
Create an index buffer.
@method
@variation 2
@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) {
if (ArrayBuffer.isView(itemSize)) {
usage = data;
data = itemSize;
itemSize = 3;
}
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 empty 2D texture.
@method
@variation 1
@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
<b>internalFormat</b>.
@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.
*//**
Create a 2D texture from a DOM image element.
@method
@variation 2
@param {HTMLImageElement|HTMLImageElement[]} 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 {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
<b>intrnalFormat</b>.
@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.
*//**
Create 2D texture from a typed array.
@method
@variation 3
@param {ArrayBufferView|ArrayBufferView[]} 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
<b>internalFormat</b>.
@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
<b>internalFormat</b>.
@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
<b>internalFormat</b>.
@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 {HTMLElement|ArrayBufferView} [options.negX] The image data for the negative X direction.
Can be any format that would be accepted by texImage2D.
@param {HTMLElement|ArrayBufferView} [options.posX] The image data for the positive X direction.
Can be any format that would be accepted by texImage2D.
@param {HTMLElement|ArrayBufferView} [options.negY] The image data for the negative Y direction.
Can be any format that would be accepted by texImage2D.
@param {HTMLElement|ArrayBufferView} [options.posY] The image data for the positive Y direction.
Can be any format that would be accepted by texImage2D.
@param {HTMLElement|ArrayBufferView} [options.negZ] The image data for the negative Z direction.
Can be any format that would be accepted by texImage2D.
@param {HTMLElement|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
<b>internalFormat</b>.
@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");
}
initContextListeners() {
if (this.contextRestoredHandler) {
this.contextLostListener = (e) => {
e.preventDefault();
};
this.contextRestoredListener = () => {
this.initExtensions();
this.contextRestoredHandler();
};
this.canvas.addEventListener("webglcontextlost", this.contextLostListener);
this.canvas.addEventListener("webglcontextrestored", this.contextRestoredListener);
} else {
this.canvas.removeEventListener("webglcontextlost", this.contextLostListener);
this.canvas.removeEventListener("webglcontextrestored", this.contextRestoredListener);
this.contextLostListener = null;
this.contextRestoredListener = null;
}
}
// DEPRECATED
depthTest() {
console.warn("App.depthTest is deprecated. Use App.enable(PicoGL.DEPTH_TEST) instead.");
this.enable(GL.DEPTH_TEST);
return this;
}
noDepthTest() {
console.warn("App.noDepthTest is deprecated. Use App.disable(PicoGL.DEPTH_TEST) instead.");
this.disable(GL.DEPTH_TEST);
return this;
}
blend() {
console.warn("App.blend is deprecated. Use App.enable(PicoGL.BLEND) instead.");
this.enable(GL.BLEND);
return this;
}
noBlend() {
console.warn("App.noBlend is deprecated. Use App.disable(PicoGL.BLEND) instead.");
this.disable(GL.BLEND);
return this;
}
stencilTest() {
console.warn("App.stencilTest is deprecated. Use App.enable(PicoGL.STENCIL_TEST) instead.");
this.enable(GL.STENCIL_TEST);
return this;
}
noStencilTest() {
console.warn("App.noStencilTest is deprecated. Use App.disable(PicoGL.STENCIL_TEST) instead.");
this.disable(GL.STENCIL_TEST);
return this;
}
scissorTest() {
console.warn("App.scissorTest is deprecated. Use App.enable(PicoGL.SCISSOR_TEST) instead.");
this.enable(GL.SCISSOR_TEST);
return this;
}
noScissorTest() {
console.warn("App.noScissorTest is deprecated. Use App.disable(PicoGL.SCISSOR_TEST) instead.");
this.disable(GL.SCISSOR_TEST);
return this;
}
rasterize() {
console.warn("App.noRasterize is deprecated. Use App.disable(PicoGL.RASTERIZER_DISCARD) instead.");
this.disable(GL.RASTERIZER_DISCARD);
return this;
}
noRasterize() {
console.warn("App.rasterize is deprecated. Use App.enable(PicoGL.RASTERIZER_DISCARD) instead.");
this.enable(GL.RASTERIZER_DISCARD);
return this;
}
cullBackfaces() {
console.warn("App.cullBackfaces is deprecated. Use App.enable(PicoGL.CULL_FACE) instead.");
this.enable(GL.CULL_FACE);
return this;
}
drawBackfaces() {
console.warn("App.drawBackfaces is deprecated. Use App.disable(PicoGL.CULL_FACE) instead.");
this.disable(GL.CULL_FACE);
return this;
}
}
///////////////////////////////////////////////////////////////////////////////////
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
*/
const PicoGL = Object.assign({
/**
The version of PicoGL
@type {string}
@name PicoGL.version
@private
*/
version: "0.17.9",
/**
WebGL information about the current system
@type {Object.<string, *>}
@name PicoGL.WEBGL_INFO
@private
*/
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 {HTMLElement} 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(gl, contextAttributes) {
// Support providing a canvas and getting a WebGL 2 context
if (gl.tagName === "CANVAS") {
gl = gl.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);
}
}, GL);
let canvas = document.getElementById( 'webgl-canvas' );
let gl = canvas.getContext( 'webgl2' );
if ( !gl ) {
document.body.innerHTML = 'WebGL2 is not supported in this browser. Please try again with Firefox or Chrome';
}
const app = PicoGL.createApp( canvas );
app.canvas = canvas;
app.defaultMenuBar = {
/**
* @param {object} dropdownIndex holds menu bar dropdown index values
* @param {number} bitDepth the bit depth index used by the WebGL filter
*/
dropdownIndex:{
bitDepth: 0,
},
};
app.filterParams = {
/**
* @param {array} Gaussian create the gaussian uniform buffer
* @param {vec4} PicoGL.FLOAT_VEC4 step (inverse of viewport [ inverted image width, 0.0, 0.0, inverted image height ] )
* @param {float} PicoGL.FLOAT alpha radius
* @param {float} PicoGL.FLOAT beta radius
* @param {float} PicoGL.FLOAT ratio
* @param {float} PicoGL.FLOAT gaussian window size
*/
Gaussian: app.createUniformBuffer([
PicoGL.FLOAT_VEC4,
PicoGL.FLOAT,
PicoGL.FLOAT,
PicoGL.FLOAT,
PicoGL.FLOAT,
]),
};
/**
Set the default values for the WebGL filters uniform buffer
*/
// GAUSSIAN
app.filterParams.Gaussian.set( 0, [ 0.0, 0.0, 0.0, 0.0 ] ).update(); // step (inverse of viewport [ inverted image width, 0.0, 0.0, inverted image height ] )
app.filterParams.Gaussian.set( 1, 445.72 ).update(); // alpha radius
app.filterParams.Gaussian.set( 2, 3.29 ).update(); // beta radius
app.filterParams.Gaussian.set( 3, 1.0 ).update(); // ratio based on image scale ratio value
app.filterParams.Gaussian.set( 4, 3072.0 ).update(); // gaussian window size
/**
* @param {object} app.initial that contains the initial inputs for HTML and Layer creation
*/
app.initial = {
/**
* @param {object} zIndex that contains z indexes for HTML and Canvas elements
*/
zIndex: {
canvasWebGL: 600,
canvasUI: 100,
canvasUIActive: 200,
canvasUIDetection: 500,
canvasUITrasnparent: 199,
menu: 1000,
},
/**
* @param {array} bitDepths of objects that holds the WebGL bitdepth internal format
* used when building filters and loading new images
*/
bitDepths: [
{
name:"4 bit",
internalFormat: PicoGL.RGBA4
},{
name:"8 bit",
internalFormat: PicoGL.RGBA8
},{
name:"16 bit",
internalFormat: PicoGL.RGBA16F
},//{
//name:"32 bit",
//internalFormat: PicoGL.RGBA32F
//},
],
};
/**
* @param {boolean} app.needsRender set this to "true" whenever we want to trigger a render
*/
app.needsRender = false;
/**
* @param {boolean} app.renderAccepted "true" when WebGL render is possible
*/
app.renderAccepted = false;
/**
* @param {object} app.function that contains the funcitons used by the app
* @param {object} menuBar funcitons used and created in menu bar js
* @param {object} layerWindow funcitons used and created in layer window js
* @param {object} frequencyUI funcitons used and created in frequency js
* @param {object} sequencer funcitons used and created in sequencer js
* @param {object} webGL funcitons used and created in app js
*/
app.function = {
menuBar: null,
layerWindow: null,
frequencyUI: null,
sequencer: null,
webGL: null,
};
app.texture = null; // the texture to be used by the WebGL filter
app.textureArray = []; // the array of textures used by the WebGL filter. Selected from the dropdown in the menubar
app.imageArray = []; // the array of image URLs/Blobs used to create textures. Selected from the dropdown in the menubar. Also contains loaded images names
app.recipeArray = []; // the array of recipes loaded. Selected from the dropdown in the menubar
app.frequencyUI = null; // the object that contains the frequency UI parameters
app.loadJSON = false;
app.newWindow = {}; // the object that holds the new window
/**
* @param {array} app.commandPatternUndo an array that holds the applications input commands
* allowing for the applications undo states.
*/
app.commandPatternUndo = [];
/**
* @param {array} app.commandPatternRedo an array that holds the applications undone commands
* allowing for the applications redo its undone states.
*/
app.commandPatternRedo = [];
/**
* @param {object} app.layerCollection that contains the apps layers states
*/
app.layerCollection = {
/**
* @param {array} layers holds all layer states
*/
layers:[], // holds all layer states
/**
* @param {array} sampledTextures holds arrays of object layers
* holds the layer states of the sampled texture
* used by the program to create the sampled textures
*/
sampledTextures: [],
/**
* @param {object} background holds canvas background
* Populated when initial image is loaded from menu-bar.js
* function "handleUploadedImages()"
*/
background:{
width: null,
height: null,
maxWidth: null,
maxHeight: null,
},
/**
* @param {number} activeIndex The index of the active layer in the layer window
*/
activeIndex: undefined,
/**
* @param {number} renderIndex The index of the layers data to be used by the WebGL filter
*/
renderIndex: undefined,
/**
* @param {object} offset The object that holds the mouses to layers offset relationship
* @param {number} x clicked layer windows offsetLeft - mouse event "x"
* @param {number} y clicked layer windows offsetTop - mouse event "y"
* assigned when the mouse clicks on a layer
*/
offset: {
x: null,
y: null,
},
/**
* @param {canvas} webglCanvas The WebGL canvas to draw on
*/
webglCanvas: null,
/**
* @param {context} webglCTX The WebGL context to draw in
*/
webglCTX: null,
/**
* @param {canvas} uiCanvas The frequency UI canvas to draw on
*/
uiCanvas: null,
/**
* @param {context} uiCTX The frequency UI context to draw in
*/
uiCTX: null, // the frequency UI context to draw in
/**
* @param {object} menuBar holds menu bar states for recipe export
*/
menuBar: JSON.parse( JSON.stringify( app.defaultMenuBar ) ),
/**
* @param {object} mouseEvent holds mouse interaciton boolean values with layers
* @param {boolean} sliders if true active layers sliders are being updated
* @param {boolean} frequency if true frequency UI is being updated
* @param {boolean} sequencer if true sequncer is active
* @param {boolean} keys if true there is a key being pressed
* @param {boolean} dialog if true the layers rename dialog is open
*/
mouseEvent:{
sliders: false,
frequency: false,
sequencer: false,
keys: false,
dialog: false,
},
};
let animate;
/**
* @param {object} app.function.webGL holds functions that calls the WebGL filter
* draw the results of that filter to the active layer
*/
app.function.webGL = {
/**
* Resolves at the end so that calling the await function can be move forward
*
* @param {object} drawCanvas draw the results of that filter to the active layer
* @param {object} p holds the position and scale used when drawing the canvas
*/
drawCanvas: ( p ) => {
return new Promise( async ( resolve ) => {
let destCtx = app.layerCollection.webglCTX; // get the context for WebGL output us
// update the active canvas with the WebGL filter output
destCtx.clearRect(0, 0, app.texture.width, app.texture.height);
destCtx.save();
destCtx.transform( p._layerScale, 0, 0, p._layerScale, p._x , p._y );
destCtx.drawImage( app.canvas, 0, 0, app.texture.width, app.texture.height );
destCtx.restore();
resolve();
});
},
/**
* Checks to see if conditions for updating the active layers canvas are meet.
* If all conditions are meet the active layers canvas will be updated with the WebGls filter output.
*
* @param {object} draw draw the results of that filter to the active layer
*/
draw: () => {
/**
* @param {boolean} app.needsRender is true
* @param {object} app.activeFilter is there a valid Pico WebGL program to be used
* @param {object} app.activeFilter.drawCall is there a valid the Pico WebGL filter to be used
* @param {object} app.texture is there a valid image texture to be used by the WebGL filter
* @param {number} app.layerCollection.renderIndex active layers index whoes canvas will be updated
* @param {number} layer.dropdownIndex.selectFrom if greater than 0 a select from dropdown item is selected
* @param {number} layer.dropdownIndex.fillWith if greater than 0 a will with dropdown item is selected
* @param {boolean} layer.switches.visible is true
*/
if( app.needsRender && app.activeFilter &&
app.activeFilter.drawCall && app.texture ){
app.renderAccepted = true; // update WebGL filter
}else {
app.needsRender = false; // reset back to false
app.renderAccepted = false; // prevent WebGL filter update
}
// if the render was accepted by the above logic
// call the WebGL filter to be animated
if( app.renderAccepted ){
//let position = layer.position; // get layers position object
let position ={
"imageScale": 0.5,
"radius": 1,
"radiusScale": 0.5,
"w": 1600,
"h": 1200,
"x": 0,
"y": 0,
"_layerScale": 1,
"_imageScale": 1,
"_w": 800,
"_h": 600,
"_x": 0,
"_y": 0,
"x_Offset": 0,
"y_Offset": 0
};
// because the mouse is not moving the image does not need to be scaled
// set the gaussian radius ratio back to normal
app.filterParams.Gaussian.set( 3, 1.0 ).update();
app.resize( app.texture.width, app.texture.height ); // reset the image to its original size
// update the gaussian filters scale based on the images width and height
// the image scale changes size based on weather or not the UI is active
app.filterParams.Gaussian.set( 0, [ 1 / app.width, 0, 0, 1 / app.height ] ).update();
app.activeFilter.drawCall.draw(); // represents the webgl functional object to be called now.
//await app.function.webGL.drawCanvas( position );
app.function.webGL.drawCanvas( position );
// Set app render states back to their default states
app.needsRender = false; // set to "false" so that another render is not triggered
app.renderAccepted = false; // set to "false" so that the app can check to see if a render is needed
animate = requestAnimationFrame( app.function.webGL.draw ); // start frame animation
}else {
// else there is nothing to be rendered
// so cancel the animation
// Set app render states back to their default states of false
app.needsRender = false; // set to "false" so that another render is not triggered
app.renderAccepted = false; // set to "false" so that the app can check to see if a render is needed
cancelAnimationFrame( animate ); // stop the stop asking for draw commands
}
},
};
// update CSS styles
let windowStyle = document.createElement('style');
( () => {
document.head.appendChild(windowStyle);
windowStyle.sheet.insertRule(` body {
display: block;
top: 0;
left: 0;
padding: 0;
margin: 0;
background-repeat: no-repeat;
font-family: Arial, Helvetica, sans-serif;
font-style: normal;
font-weight: normal;
font-size: 12px;
min-width: 1200px !important; /* set the min width or the window */
background-color: rgba( 235, 235, 235, 1.0 ); /* grey */
} `);
/* prevent user from being able to select the webpage text with mouse moves across is */
windowStyle.sheet.insertRule(` div {
user-select: none;
} `);
/* prevent highlighting of dropdowns */
windowStyle.sheet.insertRule(` select {
user-select: none;
outline: none;
} `);
windowStyle.sheet.insertRule(` button {
outline: none;
} `);
windowStyle.sheet.insertRule(` .background-Color2 {
background-color: rgba( 235, 25, 235, 1.0 );
} `);
windowStyle.sheet.insertRule(` .hold-image {
display: inline-block;
vertical-align: middle;
width: 36px;
height: 36px;
} `);
windowStyle.sheet.insertRule(` .image-alignment {
display: flex;
justify-content: center;
align-items: center;
width: 36px;
height: 36px;
} `);
windowStyle.sheet.insertRule(` .image-alignment:hover {
display: inline-block;
text-align: left;
} `);
windowStyle.sheet.insertRule(` .image-alignment:hover img {
float: left;
display: block;
position: absolute;
max-width: none;
max-height: none;
width: 400px;
height: auto;
z-index: ${ app.initial.zIndex.menu };
text-align: left;
} `);
windowStyle.sheet.insertRule(` img {
max-width: 36px;
max-height: 36px;
} `);
windowStyle.sheet.insertRule(` .Container{
display: grid;
grid-template-rows: auto auto auto;
/*justify-content: center;*/
grid-gap: 15px;
align-items: center;
justify-items: center;
width: 100%;
height: 100%;
} `);
windowStyle.sheet.insertRule(` .Container > *{
/*background-color: rgba(20, 250, 230, 0.2);*/
} `);
/* SPACER */
windowStyle.sheet.insertRule(` .Menu-Container {
display: flex;
flex-flow: row wrap;
justify-content: center;
margin: 4px 10px;
min-height: 120px;
align-items: center;
/*margin-bottom: 15px;
height: 100%;*/
/*background-color: rgba(20, 230, 230, 0.3);*/
} `);
windowStyle.sheet.insertRule(` .Menu-Container > * {
/*background-color: rgba(230, 20, 230, 0.1);*/
} `);
windowStyle.sheet.insertRule(` .group {
/*margin-top: 15px;*/
margin: 15px 4px 0 4px;
padding: 0px 12px;
/*background-color: rgba(230, 20, 230, 0.2);*/
} `);
windowStyle.sheet.insertRule(` .menuBar {
background-color: rgba(240, 240, 240, 1.0);
border-radius: 10px;
display: inline-block;
cursor: pointer;
color: #000000;
font-family: Arial, Helvetica, sans-serif;
font-size: 12px;
font-weight: 500;
padding: 6px 11px;
margin: 0 4px;
text-decoration: none;
border: none;
filter: drop-shadow( 0px 1px 1px rgba( 180, 180, 180, 0.6 ) );
} `);
windowStyle.sheet.insertRule(` .menuBar:hover {
background-color: rgba(220, 220, 220, 1.0);
} `);
windowStyle.sheet.insertRule(` .menuBar:active {
position:relative;
top:1px;
} `);
windowStyle.sheet.insertRule(` .menuBar:focus {
outline: none;
box-shadow: none;
} `);
windowStyle.sheet.insertRule(` .menuBar.notActive {
/*display: none;*/
} `);
windowStyle.sheet.insertRule(` #selectRecipeDropdown{
/*display: none;*/
overflow: hidden;
text-overflow: ellipsis;
word-wrap: break-word;
max-width: 200px;
} `);
windowStyle.sheet.insertRule(` .menubar-checkbox{
padding: 0 8px;
display: inline-block;
} `);
windowStyle.sheet.insertRule(` .checkbox-title{
display: block;
font-size: 10px;
text-align: center;
padding-bottom: 2px;
} `);
windowStyle.sheet.insertRule(` .switch {
position: relative;
display: inline-block;
width: 28px;
height: 14px;
} `);
windowStyle.sheet.insertRule(` .switch input {
opacity: 0;
width: 0;
height: 0;
} `);
windowStyle.sheet.insertRule(` .switch .Switch-Help{
font-size: 10px;
position: absolute;
width: 240px;
top: 110%;
left: 110%;
background-color: rgba(255, 255, 255, 0.9);
color: rgba(0, 0, 0, 1.0);
text-align: left;
border-radius: 6px;
padding: 10px 15px;
filter: drop-shadow( 0px 4px 5px rgba( 120, 120, 120, 0.8 ) );
z-index: ${ app.initial.zIndex.help };
visibility: hidden;
transition-property: visibility;
transition-delay: 0.7s;
opacity: 0.0;
} `);
windowStyle.sheet.insertRule(` .switch:hover .Switch-Help.active{
visibility: visible;
opacity: 1.0;
} `);
windowStyle.sheet.insertRule(` .switchAction {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba( 150, 150, 150, 1.0 ); /* "red-ish" color */
-webkit-transition: .4s;
transition: .4s;
} `);
windowStyle.sheet.insertRule(` .switchAction.active {
color: rgba( 150, 150, 150, 1.0 );
} `);
windowStyle.sheet.insertRule(` .switchAction:before {
position: absolute;
content: "";
height: 10px;
width: 10px;
left: 2px;
bottom: 2px;
background-color: white;
-webkit-transition: .4s;
transition: .4s;
} `);
windowStyle.sheet.insertRule(` input:checked + .switchAction {
background-color: rgba( 150, 150, 150, 1.0 ); /* "green-ish" color */
} `);
windowStyle.sheet.insertRule(` input:checked + .switchAction.active {
background-color: rgba( 70, 70, 70, 1.0 ); /* "green-ish" color */
} `);
windowStyle.sheet.insertRule(` input:focus + .switchAction {
box-shadow: 0 0 1px #21f37c;
} `);
windowStyle.sheet.insertRule(` input:checked + .switchAction:before {
-webkit-transform: translateX(14px);
-ms-transform: translateX(14px);
transform: translateX(14px);
} `);
windowStyle.sheet.insertRule(` .switchAction.round {
border-radius: 10px;
} `);
windowStyle.sheet.insertRule(` .switchAction.round:before {
border-radius: 50%;
} `);
/* SPACER */
windowStyle.sheet.insertRule(` .Sequencer-Menu {
display: flex;
margin: 0 auto;
filter: drop-shadow( 0px 1px 1px rgba( 180, 180, 180, 0.6 ) );
align-items: center;
} `);
windowStyle.sheet.insertRule(` .Sequencer-Menu > * {
vertical-align: middle;
} `);
windowStyle.sheet.insertRule(` .Sequencer-Settings {
display: inline-block;
vertical-align: top;
} `);
windowStyle.sheet.insertRule(` .Sequencer-Settings > * {
display: inline-block;
vertical-align: middle;
} `);
windowStyle.sheet.insertRule(` .sequencer-Menu-group > * {
display: block;
margin: 4px 10px;
color: #000000;
font-family: Arial, Helvetica, sans-serif;
font-size: 10px;
font-weight: 500;
} `);
windowStyle.sheet.insertRule(` .sequencer-Menu-Button, .sequencer-Menu-input{
font-size: 10px;
font-weight: 500;
padding: 2px 7px;
border: none;
border-radius: 3px;
background-color: rgba(240, 240, 240, 1.0);
filter: drop-shadow( 0px 1px 1px rgba( 180, 180, 180, 0.6 ) );
} `);
windowStyle.sheet.insertRule(` .sequencer-Menu-Button:hover {
background-color: rgba(210, 210, 210, 1.0);
} `);
windowStyle.sheet.insertRule(` .sequencer-Menu-input::-webkit-outer-spin-button,
.sequencer-Menu-input::-webkit-inner-spin-button {
-webkit-appearance: none;
} `);
windowStyle.sheet.insertRule(` #measureCounter, #TimeSignatureA, #TimeSignatureB {
margin: 0 10px;
} `);
windowStyle.sheet.insertRule(` .sequencer-switch{
padding: 0 8px;
display: inline-block;
} `);
windowStyle.sheet.insertRule(` .sequencer-title{
display: block;
font-size: 8px;
text-align: center;
padding-bottom: 2px;
} `);
windowStyle.sheet.insertRule(` .Sequencer-Start-Stop {
display: inline-block;
} `);
windowStyle.sheet.insertRule(` .Sequencer-Help {
display: inline-block;
} `);
windowStyle.sheet.insertRule(` .sequencer {
background-color: rgba(246, 246, 246, 1.0);
border-radius: 5px;
display: block;
cursor: pointer;
color: #000000;
font-family: Arial, Helvetica, sans-serif;
font-size: 10px;
font-weight: 500;
padding: 4px 12px;
margin: 0 4px;
text-decoration: none;
border: none;
filter: drop-shadow( 0px 1px 1px rgba( 180, 180, 180, 0.6 ) );
} `);
windowStyle.sheet.insertRule(` .sequencer:hover {
} `);
windowStyle.sheet.insertRule(` .sequencer.active {
background-color: rgba(215, 215, 215, 1.0);
} `);
windowStyle.sheet.insertRule(` .sequencer:focus {
outline: none;
box-shadow: none;
} `);
/* SPACER */
windowStyle.sheet.insertRule(` .play-container {
margin: auto 1em ;
display: grid;
grid-auto-flow: column;
justify-content: start;
gap: .3em;
} `);
windowStyle.sheet.insertRule(` .btn {
display: grid;
grid-auto-flow: column;
grid-template-columns: .5em .5em .5em .5em;
align-content: center;
justify-items: center;
width: 2em;
height: 2em;
cursor: pointer;
border: none;
border-radius: 3px;
background-color: rgba(240, 240, 240, 1.0);
filter: drop-shadow( 0px 1px 1px rgba( 180, 180, 180, 0.6 ) );
} `);
windowStyle.sheet.insertRule(` .btn:hover {
background-color: rgba(210, 210, 210, 1.0);
} `);
windowStyle.sheet.insertRule(` .btn:active {
transform: scale(0.95, 0.95);
} `);
windowStyle.sheet.insertRule(` .btn:focus {
transform: scale(0.95, 0.95);
} `);
windowStyle.sheet.insertRule(` .btn > div {
position: relative;
border-style: solid;
opacity: .6;
} `);
windowStyle.sheet.insertRule(` .btn:hover > div {
opacity: 1;
} `);
windowStyle.sheet.insertRule(` .step-forward-step {
grid-column: 2;
border-width: .35em 0 .35em .6em;
border-color: transparent transparent transparent #000;
} `);
windowStyle.sheet.insertRule(` .step-forward-to {
grid-column: 3;
border-width: .35em 0 .35em .6em;
border-color: transparent transparent transparent #000;
} `);
windowStyle.sheet.insertRule(` .step-backward-to {
grid-column: 2;
border-width: .35em .6em .35em 0;
border-color: transparent #000 transparent transparent;
} `);
windowStyle.sheet.insertRule(` .step-backward-step {
grid-column: 3;
border-width: .35em .6em .35em 0;
border-color: transparent #000 transparent transparent;
} `);
windowStyle.sheet.insertRule(` .previous-measure-to {
grid-column: 2;
border-width: 0 .22em 0 0;
border-color: #000 #000 #000 transparent;
} `);
windowStyle.sheet.insertRule(` .previous-measure-step {
grid-column: 3;
border-width: .35em .6em .35em 0;
border-color: transparent #000 transparent transparent;
} `);
windowStyle.sheet.insertRule(` .next-measure-step {
grid-column: 2;
border-width: .35em 0 .35em .6em;
border-color: transparent transparent transparent #000;
} `);
windowStyle.sheet.insertRule(` .next-measure-to {
grid-column: 3;
border-width: 0 .22em .0 0;
border-color: #000 #000 #000 transparent;
} `);
windowStyle.sheet.insertRule(` .play-left{
grid-column: 2 / 4;
border-width: .5em 0 .5em .9em;
border-color: transparent transparent transparent #000;
} `);
windowStyle.sheet.insertRule(` .play-right{
visibility: hidden;
} `);
windowStyle.sheet.insertRule(` .play-left.active{
opacity: 1;
border-color: transparent transparent transparent #008000;
} `);
windowStyle.sheet.insertRule(` .btn:hover .play-left.active{
grid-column: 2;
width: 0;
height: .9em;
border-width: 0 0 0 .3em;
border-color: transparent transparent transparent #FFF;
} `);
windowStyle.sheet.insertRule(` .btn:hover .play-right.active{
visibility: visible;
grid-column: 3;
width: 0;
height: .9em;
border-width: 0 0 0 .3em;
border-color: transparent transparent transparent #FFF;
} `);
windowStyle.sheet.insertRule(` .play-left.active-pause{
grid-column: 2;
width: 0;
height: .9em;
border-width: 0 0 0 .3em;
border-color: transparent transparent transparent #000;
opacity: 1;
} `);
windowStyle.sheet.insertRule(` .play-right.active-pause{
visibility: visible;
grid-column: 3;
width: 0;
height: .9em;
border-width: 0 0 0 .3em;
border-color: transparent transparent transparent #000;
opacity: 1;
} `);
windowStyle.sheet.insertRule(` .btn:hover .play-left.active-pause{
grid-column: 2 / 4;
width: 0;
height: 0;
border-width: .5em 0 .5em .9em;
border-color: transparent transparent transparent #000;
opacity: 1;
} `);
windowStyle.sheet.insertRule(` .btn:hover .play-right.active-pause{
visibility: hidden;
grid-column: 0;
width: 0;
height: 0;
border-width: 0 0 0 .3em;
border-color: transparent transparent transparent #000;
} `);
windowStyle.sheet.insertRule(` .stop-left {
grid-column: 2 / 4;
height: .9em;
border-width: 0 0 0 .9em;
border-color: transparent transparent transparent #000;
} `);
windowStyle.sheet.insertRule(` .stop-right{
visibility: hidden;
} `);
windowStyle.sheet.insertRule(` .stop-left.active{
opacity: 1;
border-color: transparent transparent transparent rgba( 0, 0, 0, 1.0 );
} `);
windowStyle.sheet.insertRule(` .record{
grid-column: 2 / 4;
height: 1em;
border-width: 0 0 0 1em;
border-color: transparent transparent transparent #000;
border-radius: .5em;
} `);
windowStyle.sheet.insertRule(` .record.active{
border-color: transparent transparent transparent #FF0000;
} `);
/* SPACER */
windowStyle.sheet.insertRule(` .Sequencer-Container {
display: flex;
flex-flow: row wrap;
justify-content: center;
align-items: center;
padding-top:20px;
min-height: 40px;
min-width: 1200px; /* set the min width or the window */
/*background-color: rgba(246, 26, 26, 0.2);*/ /* for testing */
overflow: auto;
} `);
windowStyle.sheet.insertRule(` .Measure-Container{
margin: 0 auto;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: fit-content;
block-size: fit-content;
padding: 2px 6px;
border-radius: 5px;
background-color: rgba(246, 246, 246, 1.0);
margin: 0 3px;
/*margin-bottom: 15px;*/
} `);
windowStyle.sheet.insertRule(` .Measure-Container.active{
background-color: rgba(215, 215, 215, 1.0);
} `);
windowStyle.sheet.insertRule(` .Measure-Title{
margin: 0 auto;
} `);
windowStyle.sheet.insertRule(` .Measure-Title-Lable{
display: block;
font-size: 10px;
text-align: center;
padding-bottom: 0px;
} `);
windowStyle.sheet.insertRule(` .Measure {
display: flex;
flex-wrap: nowrap;
flex-direction: row;
justify-content: center;
align-items: center;
width: 100%;
border-radius: 5px;
padding-bottom: 2px;
} `);
windowStyle.sheet.insertRule(` .Measure-Start-Stop{
display: inline-flex;
border-radius: 2px;
background-color: rgba(100, 100, 100, 1.0);
width: 100%;
min-width: 10px;
max-width: 15px;
height: 20px;
margin: 0 2px;
} `);
windowStyle.sheet.insertRule(` .Measure-Start-Stop.update{
background-color: rgba(232, 186, 77, 1.0); /* light orange */
} `);
windowStyle.sheet.insertRule(` .Measure-steps-container{
display: flex;
flex-wrap: nowrap;
flex-direction: row;
justify-content: center;
width: 100%;
} `);
windowStyle.sheet.insertRule(` .Beat {
display: flex;
flex-wrap: nowrap;
flex-direction: row;
justify-content: center;
border-radius: 2px;
/*background-color: rgba(255, 255, 255, 1.0);*/ /* for testing */
width: 100%;
height: 20px;
margin: 0 1px;
} `);
windowStyle.sheet.insertRule(` .Step {
display: flex;
flex-wrap: nowrap;
flex-direction: row;
justify-content: center;
border-radius: 2px;
background-color: rgba(235, 235, 235, 1.0); /* light grey */
width: 100%;
min-width: 8px;
max-width: 10px;
border-radius: 2px;
margin: 1px 1px;
margin-top: 4px;
} `);
windowStyle.sheet.insertRule(` .Step.active {
background-color: rgba(140, 140, 140, 1.0); /* grey */
} `);
windowStyle.sheet.insertRule(` .Step.play {
background-color: rgba(77, 232, 170, 1.0); /* light green */
} `);
windowStyle.sheet.insertRule(` .Start-help-tip {
visibility: hidden;
width: 280px;
position: absolute;
margin-top: 30px;
background-color: rgba(255, 255, 255, 0.7);
color: rgba(0, 0, 0, 1.0);
text-align: center;
border-radius: 6px;
padding: 5px;
opacity: 0;
filter: drop-shadow( 0px 4px 5px rgba( 120, 120, 120, 0.8 ) );
transition: opacity 0.3s;
} `);
windowStyle.sheet.insertRule(` .Measure-Start-Stop .Start-help-tip::after {
content: "";
position: absolute;
border-width: 5px;
border-style: solid;
border-color: transparent transparent transparent rgba(255, 255, 255, 0.7);
} `);
windowStyle.sheet.insertRule(` .Measure-Start-Stop:hover .Start-help-tip.active{
visibility: visible;
opacity: 1;
} `);
windowStyle.sheet.insertRule(` .Stop-help-tip {
visibility: hidden;
width: 280px;
position: absolute;
margin-top: 30px;
background-color: rgba(255, 255, 255, 0.7);
color: rgba(0, 0, 0, 1.0);
text-align: center;
border-radius: 6px;
padding: 5px;
opacity: 0;
filter: drop-shadow( 0px 4px 5px rgba( 120, 120, 120, 0.8 ) );
transition: opacity 0.3s;
} `);
windowStyle.sheet.insertRule(` .Measure-Start-Stop .Stop-help-tip::after {
content: "";
position: absolute;
border-width: 5px;
border-style: solid;
border-color: transparent rgba(255, 255, 255, 0.7) transparent transparent;
} `);
windowStyle.sheet.insertRule(` .Measure-Start-Stop:hover .Stop-help-tip.active{
visibility: visible;
opacity: 1;
} `);
/* SPACER */
windowStyle.sheet.insertRule(` .Filter-UI-Container {
display: grid;
/*grid-gap: 0.52%;*/
grid-template-columns: auto;
/*grid-template-columns: minmax(154px, 240px) minmax(530px, 840px) minmax(505px, 800px);*/
justify-content: center;
/*align-items: start;*/
align-items: center;
width: 100%;
height: 100%;
} `);
windowStyle.sheet.insertRule(` .Filter-UI-Container > *{
/*background-color: rgba(10, 120, 250, 0.2);*/
} `);
windowStyle.sheet.insertRule(` .Layer-Container{
aspect-ratio: 1 / 1;
display: grid;
grid-row: 1;
grid-column: 1;
max-width: 240px;
max-height: 860px;
/* background-color: rgba(120, 120, 250, 0.2); */
} `);
windowStyle.sheet.insertRule(` .Layer-Window {
display: grid;
grid-gap: 0px;
grid-template-rows: 30px auto 30px;
align-items: center;
justify-content: stretch;
} `);
windowStyle.sheet.insertRule(` .Layer-Window > * {
/* background-color: rgba(250, 20, 20, 0.2); */
} `);
windowStyle.sheet.insertRule(` .LAYERS::-webkit-scrollbar {
display: none;
} `);
windowStyle.sheet.insertRule(` .LAYERS {
aspect-ratio: 1 / 3.33;
overflow-y: scroll;
display: grid;
position: relative;
max-height: 860px;
max-width: 240px;
background-color: rgba(250, 250, 250, 1.0);
box-shadow: inset 0px 2px 4px rgba( 40, 40, 40, 0.8 );
} `);
windowStyle.sheet.insertRule(` #layer-container{
/*background-color: rgba(120, 120, 250, 0.4);*/
} `);
windowStyle.sheet.insertRule(` .Layer-Buttons{
display: grid;
grid-gap: 8%;
grid-template-columns: auto auto auto;
grid-template-rows: auto;
align-items: center;
justify-content: center;
} `);
windowStyle.sheet.insertRule(` .Layer-Buttons > *{
/* background-color: rgba(80, 180, 250, 0.9); */
} `);
windowStyle.sheet.insertRule(` .Add-Remove-Button{
width: 2.2em;
height: 1.2em;
font-weight: normal;
text-align: center;
line-height: 1em;
/*padding: 2px 5px;*/
/*margin: auto 20px;*/
border: none;
border-radius: 5px;
/* background-color: rgba(240, 240, 240, 1.0); */
filter: drop-shadow( 0px 1px 1px rgba( 180, 180, 180, 0.6 ) );
} `);
windowStyle.sheet.insertRule(` .Add-Remove-Button:hover {
/* background-color: rgba(210, 210, 210, 1.0); */
} `);
windowStyle.sheet.insertRule(` .Background-Picker{
display: grid;
grid-gap: 5px;
grid-template-columns: 58px auto;
/*grid-template-columns: 58px 68%;*/
align-items: center;
justify-items: start;
} `);
windowStyle.sheet.insertRule(` .Background-Picker > *{
/* background-color: rgba(0, 250, 250, 0.2); */
} `);
windowStyle.sheet.insertRule(` .Background-Picker-block{
padding-left: 5px;
font-size: 10px;
white-space: wrap;
} `);
windowStyle.sheet.insertRule(` .radio-button-container{
display: grid;
grid-column: 2;
place-self: stretch;
align-items: center;
} `);
windowStyle.sheet.insertRule(` .radio-button-collection{
display: grid;
grid-gap: 4px;
grid-template-columns: repeat(6, 12%);
align-items: center;
justify-content: end;
padding-right: 5px;
/*justify-content: center;*/
} `);
windowStyle.sheet.insertRule(` .radio-button{
display: grid;
/*justify-items: center;*/
/*align-items: center;*/
max-width: 18px;
max-height: 18px;
/*aspect-ratio: 1 / 1;*/
/* background-color: rgba( 30, 130, 270, 0.5 ); */
} `);
windowStyle.sheet.insertRule(` .radio-button input {
-webkit-appearance: none;
appearance: none;
display: none;
} `);
windowStyle.sheet.insertRule(` .checkmark {
aspect-ratio: 1 / 1;
/*max-width: 15px;
max-height: 15px;*/
cursor: pointer;
border-radius: 50%;
filter: drop-shadow( 0px 2px 1px rgba( 100, 100, 100, 0.0 ) );
} `);
windowStyle.sheet.insertRule(` .radio-button input:checked ~ .checkmark {
filter: drop-shadow( 0px 2px 1px rgba( 100, 100, 100, 1.0 ) );
} `);
windowStyle.sheet.insertRule(` .checkmark:after {
content: "";
position: absolute;
display: none;
/*visibility: hidden;*/
} `);
/* SPACER */
windowStyle.sheet.insertRule(` .Frequency-Container {
aspect-ratio: 1 / 1;
display: grid;
grid-row: 1;
grid-column: 2;
align-items: center;
justify-items: center;
max-width: 860px;
/*max-width: 840px;*/
/*max-width: 832px;*/
max-height: 860px;
/*background-color: rgba(250, 0, 0, 0.2);*/
} `);
windowStyle.sheet.insertRule(` .Frequency-Window {
display: grid;
grid-gap: 0px;
grid-template-columns: 30px auto 30px;
/*grid-template-columns: 20px auto 20px;*/
/*grid-template-columns: 16px auto 16px;*/
grid-template-rows: 30px auto 30px;
align-items: center;
width: 100%;
height: 100%;
/*justify-items: center;*/
} `);
windowStyle.sheet.insertRule(` .Frequency-Window > * {
display: grid;
justify-items: center;
/*background-color: rgba(0, 250, 0, 0.2);*/
} `);
windowStyle.sheet.insertRule(` .UI {
aspect-ratio: 1 / 1;
display: grid;
align-items: center;
justify-items: center;
grid-column: 2;
grid-row: 2;
grid-template-columns: 100%;
grid-template-rows: 100%;
max-width: 800px;
max-height: 800px;
background-color: rgba( 248, 248, 248, 1.0 );
/*filter: drop-shadow( 0px 4px 3px rgba( 160, 160, 160, 0.6 ) );*/
} `);
windowStyle.sheet.insertRule(` .UI > * {
grid-column: 1;
grid-row: 1;
width: 100%;
height: 100%;
object-fit: contain;
} `);
windowStyle.sheet.insertRule(` #f-mod-canvas-trasnparentWindow {
background-color: rgba( 248, 248, 248, 0.8 );
z-index: ${ app.initial.zIndex.canvasUITrasnparent };
} `);
windowStyle.sheet.insertRule(` .rotate0Counter {
content: "";
white-space: nowrap;
transform: rotate(0deg);
} `);
windowStyle.sheet.insertRule(` .rotate90Counter {
content: "";
white-space: nowrap;
transform: rotate(-90deg);
} `);
windowStyle.sheet.insertRule(` .f-top{
padding-top: 5px;
} `);
windowStyle.sheet.insertRule(` .f-bottom{
padding-bottom: 5px;
} `);
windowStyle.sheet.insertRule(` .f-left{
padding-top: 5px;
} `);
windowStyle.sheet.insertRule(` .f-right{
padding-bottom: 5px;
zoom: 0.99; /* to make the text crisp */
} `);
/* SPACER */
windowStyle.sheet.insertRule(` .Image-Out-Container {
aspect-ratio: 1 / 1;
display: grid;
grid-row: 1;
grid-column: 3;
align-items: center;
justify-items: center;
max-width: 800px;
max-height: 860px;
/* background-color: rgba(250, 120, 10, 0.2); */
} `);
windowStyle.sheet.insertRule(` .Image-Window {
display: grid;
grid-gap: 0px;
width: 100%;
height: 100%;
grid-template-rows: 30px auto 30px;
align-items: center;
} `);
windowStyle.sheet.insertRule(` .Image-Window > * {
/* background-color: rgba(250, 20, 20, 0.2); */
} `);
windowStyle.sheet.insertRule(` .IMAGE {
aspect-ratio: 1 / 1;
display: grid;
align-items: center;
justify-items: center;
grid-template-columns: 100%;
grid-template-rows: 100%;
max-width: 800px;
max-height: 800px;
z-index: ${ app.initial.zIndex.canvasWebGL };
background-color: rgba( 248, 248, 248, 1.0 );
filter: drop-shadow( 0px 4px 3px rgba( 160, 160, 160, 0.6 ) );
} `);
windowStyle.sheet.insertRule(` .IMAGE > * {
grid-column: 1;
grid-row: 1;
width: 100%;
height: 100%;
object-fit: contain;
} `);
windowStyle.sheet.insertRule(` .center-webgl-row {
font-size: .85em;
font-style: regular;
text-align: left;
margin: auto 0;
padding-left: 10px;
/*padding-top: 26px;*/
padding-top: 8px;
} `);
windowStyle.sheet.insertRule(` #webglMouseInfo{
display: none;
} `);
windowStyle.sheet.insertRule(` .imageMouseInteractions{
margin-top: 3px;
} `);
windowStyle.sheet.insertRule(` .keyComands{
font-weight: bold;
} `);
/* SPACER */
windowStyle.sheet.insertRule(` #picker-Pre-Size{
/*visibility: hidden;*/
background-color: rgba(0, 250, 0, 0.2);
} `);
windowStyle.sheet.insertRule(` #picker-Container{
/*display: none;
background-color: rgba(245, 245, 245, 0.95);*/
} `);
/*
windowStyle.sheet.insertRule(` .ui-picker-container{
display: grid;
align-items: center;
justify-items: center;
background-color: rgba( 248, 248, 248, 0.9 );
z-index: 100;
} `);
windowStyle.sheet.insertRule(` .ui-picker-collection {
display: grid;
grid-gap: 2%;
grid-template-rows: 90% auto;
width: 90%;
height: 90%;
background-color: rgba( 248, 28, 28, 0.9 );
z-index: 101;
} `);
*/
windowStyle.sheet.insertRule(` .botton-item{
/*display: grid;
justify-items: center;*/
} `);
windowStyle.sheet.insertRule(` #button-container{
filter: drop-shadow( 0px 4px 3px rgba( 160, 160, 160, 0.6 ) );
} `);
windowStyle.sheet.insertRule(` .button-container{
display: grid;
grid-column: 1;
grid-row: 3;
grid-gap: 2%;
align-items: center;
justify-content: center;
grid-template-columns: 20% 10% 20%;
} `);
windowStyle.sheet.insertRule(` .button-container > * {
display: grid;
align-items: center;
/*background-color: rgba(25, 110, 110, 0.4);*/
} `);
windowStyle.sheet.insertRule(` .color-picker-button{
font-size: 14px;
text-align: center;
text-decoration: none;
color: rgba(0, 0, 0, 1.0);
white-space: nowrap;
border-radius: 12px;
background-color: rgba(255, 255, 255, 1.0);
border: none;
padding: 3% 10%;
} `);
/*windowStyle.sheet.insertRule(` #myModal{
display: none;
} `);*/
windowStyle.sheet.insertRule(` #picker-Container-Outer{
display: none;
} `);
windowStyle.sheet.insertRule(` #webgl-canvas{
width:800px;
height:800px;
max-width: 800px;
max-height: 800px;
visibility: hidden;
z-index: 1;
/*background-color: rgba(255, 25, 255, 1.0); */
} `);
windowStyle.sheet.insertRule(` .modal {
display: none;
position: fixed; /* Stay in place */
z-index: ${ app.initial.zIndex.dialog };
padding-top: 300px;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgba(245, 245, 245, 0.8);
} `);
windowStyle.sheet.insertRule(` .modal-content {
background-color: #fefefe;
margin: auto;
padding: 20px 40px 20px 40px;
border: 0px solid #888;
width: 25%;
min-height: 100px;
border-radius: 10px;
filter: drop-shadow( 0px 1px 1px rgba( 180, 180, 180, 0.6 ) );
} `);
windowStyle.sheet.insertRule(` .close {
color: #aaaaaa;
float: right;
font-size: 28px;
font-weight: normal;
} `);
windowStyle.sheet.insertRule(` .renameTitle{
border-radius: 5px;
border: none;
padding: 3px 8px;
background-color: rgba( 248, 248, 248, 0.8 );
filter: drop-shadow( 0px 1px 1px rgba( 180, 180, 180, 0.6 ) );
} `);
windowStyle.sheet.insertRule(` .renameButton{
float: right;
font-weight: normal;
margin-left: 10px;
color: black;
text-align: center;
text-decoration: none;
display: inline-block;
padding: 2px 10px 3px 10px;
border: none;
border-radius: 5px;
background-color: rgba(240, 240, 240, 1.0);
filter: drop-shadow( 0px 1px 1px rgba( 180, 180, 180, 0.6 ) );
} `);
windowStyle.sheet.insertRule(` .close:hover,.close:focus {
color: #000;
text-decoration: none;
cursor: pointer;
} `);
windowStyle.sheet.insertRule(` .TEST-Background-COlor{
/*background-color: rgba(0, 250, 0, 0.2);*/
} `);
/* SPACE */
windowStyle.sheet.insertRule(` .total-Mask{
position: fixed; /* Stay in place */
z-index: ${ app.initial.zIndex.modal };
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgba(248, 248, 248, 0.9);
} `);
windowStyle.sheet.insertRule(` .picker-container{
z-index: ${ app.initial.zIndex.colorPicker };
/*display: grid;*/
grid-column: 2;
grid-row: 2;
align-items: center;
justify-items: center;
width: 100%;
height: 100%;
/*background-color: rgba(248, 248, 248, 0.9);*/
} `);
windowStyle.sheet.insertRule(` .picker-container > *{
width: 100%;
height: 100%;
/*background-color: rgba(245, 245, 25, 0.3);*/
} `);
windowStyle.sheet.insertRule(` .picker-window{
display: grid;
align-items: center;
justify-items: center;
grid-template-rows: 100%;
z-index: ${ app.initial.zIndex.colorPicker };
width: 100%;
height: 100%;
} `);
windowStyle.sheet.insertRule(` .picker-window > *{
width: 100%;
height: 100%;
/*background-color: rgba(245, 25, 245, 0.3);*/
} `);
windowStyle.sheet.insertRule(` .picker-area{
z-index: ${ app.initial.zIndex.colorPicker };
display: grid;
align-items: center;
justify-items: center;
} `);
windowStyle.sheet.insertRule(` .picker-area > *{
z-index: ${ app.initial.zIndex.colorPicker };
grid-column: 1;
grid-row: 1;
/*background-color: rgba(245, 25, 25, 0.3);*/
} `);
windowStyle.sheet.insertRule(` #pickerBackground{
visibility: hidden;
} `);
windowStyle.sheet.insertRule(` #pickerLayer{
visibility: hidden;
} `);
windowStyle.sheet.insertRule(` input[type=range] {
width: 100%;
margin: 4px 0;
background-color: transparent;
-webkit-appearance: none;
} `);
windowStyle.sheet.insertRule(` input[type=range]:focus {
outline: none;
} `);
windowStyle.sheet.insertRule(` input[type=range]::-webkit-slider-runnable-track {
background-color: rgba( 100, 100, 100, 1.0 ); /* "red-ish" color */
border: 0;
width: 100%;
height: 4px;
cursor: pointer;
} `);
windowStyle.sheet.insertRule(` input[type=range]::-webkit-slider-thumb {
margin-top: -4px;
width: 12px;
height: 12px;
background-color: rgba( 100, 100, 100, 1.0 ); /* "red-ish" color */
border: 0;
border-radius: 50px;
cursor: pointer;
-webkit-appearance: none;
} `);
windowStyle.sheet.insertRule(` input[type=range]:focus::-webkit-slider-runnable-track {
background-color: rgba( 80, 80, 80, 1.0 ); /* "green-ish" color */
} `);
windowStyle.sheet.insertRule(` input[type=range]:disabled::-webkit-slider-runnable-track {
background-color: rgba( 180, 180, 180, 0.5 ); /* "red-ish" color */
} `);
windowStyle.sheet.insertRule(` input[type=range]:disabled::-webkit-slider-thumb {
background-color: rgba( 180, 180, 180, 0.9 ); /* "red-ish" color */
} `);
/*document.getElementById( 'body' ).style = null; // remove the style from the html body element*/
})();
//loadWindowStyleCSS();
// update CSS styles
let layer_webGL_Style = document.createElement('style');
async function loadwebGLCSS(){
document.head.appendChild( layer_webGL_Style );
}
loadwebGLCSS();
///////////////////////////////////////////////////////////////////////////////////
// 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.
///////////////////////////////////////////////////////////////////////////////////
let translateMat;
let rotateXMat;
let rotateYMat;
let rotateZMat;
let scaleMat;
if (window.mat4) {
translateMat = mat4.create();
rotateXMat = mat4.create();
rotateYMat = mat4.create();
rotateZMat = mat4.create();
scaleMat = mat4.create();
}
let zeros = [0, 0, 0];
let ones = [1, 1, 1];
const NUM_TIMING_SAMPLES = 10;
let cpuTimeSum = 0;
let gpuTimeSum = 0;
let timeSampleCount = NUM_TIMING_SAMPLES - 1;
let randS = 1;
let randC = 1;
// https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_pvrtc/
function pvrtc2bppSize(width, height) {
width = Math.max(width, 16);
height = Math.max(height, 8);
return width * height / 4;
}
// https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_pvrtc/
function pvrtc4bppSize(width, height) {
width = Math.max(width, 8);
height = Math.max(height, 8);
return width * height / 2;
}
// https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_s3tc/
// https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_etc/
// Size for:
// COMPRESSED_RGB_S3TC_DXT1_EXT
// COMPRESSED_R11_EAC
// COMPRESSED_SIGNED_R11_EAC
// COMPRESSED_RGB8_ETC2
// COMPRESSED_SRGB8_ETC2
// COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2
// COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2
function dxtEtcSmallSize(width, height) {
return Math.floor((width + 3) / 4) * Math.floor((height + 3) / 4) * 8;
}
// https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_s3tc/
// https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_etc/
// https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_astc/
// Size for:
// COMPRESSED_RGBA_S3TC_DXT3_EXT
// COMPRESSED_RGBA_S3TC_DXT5_EXT
// COMPRESSED_RG11_EAC
// COMPRESSED_SIGNED_RG11_EAC
// COMPRESSED_RGBA8_ETC2_EAC
// COMPRESSED_SRGB8_ALPHA8_ETC2_EAC
// COMPRESSED_RGBA_ASTC_4x4_KHR
function dxtEtcAstcBigSize(width, height) {
return Math.floor((width + 3) / 4) * Math.floor((height + 3) / 4) * 16;
}
// https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_astc/
function atc5x4Size(width, height) {
return Math.floor((width + 4) / 5) * Math.floor((height + 3) / 4) * 16;
}
// https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_astc/
function atc5x5Size(width, height) {
return Math.floor((width + 4) / 5) * Math.floor((height + 4) / 5) * 16;
}
// https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_astc/
function atc6x5Size(width, height) {
return Math.floor((width + 5) / 6) * Math.floor((height + 4) / 5) * 16;
}
// https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_astc/
function atc6x6Size(width, height) {
return Math.floor((width + 5) / 6) * Math.floor((height + 5) / 6) * 16;
}
// https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_astc/
function atc8x5Size(width, height) {
return Math.floor((width + 7) / 8) * Math.floor((height + 4) / 5) * 16;
}
// https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_astc/
function atc8x6Size(width, height) {
return Math.floor((width + 7) / 8) * Math.floor((height + 5) / 6) * 16;
}
// https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_astc/
function atc8x8Size(width, height) {
return Math.floor((width + 7) / 8) * Math.floor((height + 7) / 8) * 16;
}
// https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_astc/
function atc10x5Size(width, height) {
return Math.floor((width + 9) / 10) * Math.floor((height + 4) / 5) * 16;
}
// https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_astc/
function atc10x6Size(width, height) {
return Math.floor((width + 9) / 10) * Math.floor((height + 5) / 6) * 16;
}
// https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_astc/
function atc10x8Size(width, height) {
return Math.floor((width + 9) / 10) * Math.floor((height + 7) / 8) * 16;
}
// https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_astc/
function atc10x10Size(width, height) {
return Math.floor((width + 9) / 10) * Math.floor((height + 9) / 10) * 16;
}
// https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_astc/
function atc12x10Size(width, height) {
return Math.floor((width + 11) / 12) * Math.floor((height + 9) / 10) * 16;
}
// https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_astc/
function atc12x12Size(width, height) {
return Math.floor((width + 11) / 12) * Math.floor((height + 11) / 12) * 16;
}
const PVR_CONSTANTS = {
MAGIC_NUMBER: 0x03525650,
HEADER_LENGTH: 13,
HEADER_SIZE: 52,
MAGIC_NUMBER_INDEX: 0,
PIXEL_FORMAT_INDEX: 2,
HEIGHT_INDEX: 6,
WIDTH_INDEX: 7,
MIPMAPCOUNT_INDEX: 11,
METADATA_SIZE_INDEX: 12,
FORMATS: {
0: "COMPRESSED_RGB_PVRTC_2BPPV1_IMG",
1: "COMPRESSED_RGBA_PVRTC_2BPPV1_IMG",
2: "COMPRESSED_RGB_PVRTC_4BPPV1_IMG",
3: "COMPRESSED_RGBA_PVRTC_4BPPV1_IMG",
6: "COMPRESSED_RGB8_ETC2",
7: "COMPRESSED_RGB_S3TC_DXT1_EXT",
9: "COMPRESSED_RGBA_S3TC_DXT3_EXT",
11: "COMPRESSED_RGBA_S3TC_DXT5_EXT",
22: "COMPRESSED_RGB8_ETC2",
23: "COMPRESSED_RGBA8_ETC2_EAC",
24: "COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2",
25: "COMPRESSED_R11_EAC",
26: "COMPRESSED_RG11_EAC",
27: "COMPRESSED_RGBA_ASTC_4x4_KHR",
28: "COMPRESSED_RGBA_ASTC_5x4_KHR",
29: "COMPRESSED_RGBA_ASTC_5x5_KHR",
30: "COMPRESSED_RGBA_ASTC_6x5_KHR",
31: "COMPRESSED_RGBA_ASTC_6x6_KHR",
32: "COMPRESSED_RGBA_ASTC_8x5_KHR",
33: "COMPRESSED_RGBA_ASTC_8x6_KHR",
34: "COMPRESSED_RGBA_ASTC_8x8_KHR",
35: "COMPRESSED_RGBA_ASTC_10x5_KHR",
36: "COMPRESSED_RGBA_ASTC_10x6_KHR",
37: "COMPRESSED_RGBA_ASTC_10x8_KHR",
38: "COMPRESSED_RGBA_ASTC_10x10_KHR",
39: "COMPRESSED_RGBA_ASTC_12x10_KHR",
40: "COMPRESSED_RGBA_ASTC_12x12_KHR"
},
SIZE_FUNCTIONS: {
0: pvrtc2bppSize,
1: pvrtc2bppSize,
2: pvrtc4bppSize,
3: pvrtc4bppSize,
6: dxtEtcSmallSize,
7: dxtEtcSmallSize,
9: dxtEtcAstcBigSize,
11: dxtEtcAstcBigSize,
22: dxtEtcSmallSize,
23: dxtEtcAstcBigSize,
24: dxtEtcSmallSize,
25: dxtEtcSmallSize,
26: dxtEtcAstcBigSize,
27: dxtEtcAstcBigSize,
28: atc5x4Size,
29: atc5x5Size,
30: atc6x5Size,
31: atc6x6Size,
32: atc8x5Size,
33: atc8x6Size,
34: atc8x8Size,
35: atc10x5Size,
36: atc10x6Size,
37: atc10x8Size,
38: atc10x10Size,
39: atc12x10Size,
40: atc12x12Size
}
};
const utils = {
random() {
randS = Math.sin(randC * 18.42);
randC = Math.cos(randS * 984.21);
let n = Math.abs(randS * randC) * 4532.3454;
return n - Math.floor(n);
},
instrumentAnimationFrame(numFrames = 1) {
if (window.glcheck_setRAFCount) {
window.glcheck_setRAFCount(numFrames);
}
},
xformMatrix(xform, translate, rotate, scale) {
translate = translate || zeros;
rotate = rotate || zeros;
scale = scale || ones;
mat4.fromTranslation(translateMat, translate);
mat4.fromXRotation(rotateXMat, rotate[0]);
mat4.fromYRotation(rotateYMat, rotate[1]);
mat4.fromZRotation(rotateZMat, rotate[2]);
mat4.fromScaling(scaleMat, scale);
mat4.multiply(xform, rotateXMat, scaleMat);
mat4.multiply(xform, rotateYMat, xform);
mat4.multiply(xform, rotateZMat, xform);
mat4.multiply(xform, translateMat, xform);
},
loadImages(urls) {
return new Promise((resolve) => {
let numImages = urls.length;
let images = new Array(numImages);
function onload() {
if (--numImages === 0) {
resolve(images);
}
}
for (let i = 0; i < numImages; ++i) {
images[i] = new Image();
images[i].onload = onload;
images[i].src = urls[i];
}
});
},
loadImageArray(urls) {
return this.loadImages(urls).then((images) => {
let canvas = document.createElement("canvas");
let ctx = canvas.getContext("2d");
let width = images[0].width;
let height = images[0].height;
canvas.width = width;
canvas.height = height * images.length;
for (let i = 0, len = images.length; i < len; ++i) {
ctx.drawImage(images[i], 0, i * height);
}
return new Promise((resolve) => {
let image = new Image();
image.onload = () => {
resolve({
data: image,
width: width,
height: height,
length: images.length
});
};
image.src = canvas.toDataURL();
});
});
},
createBox(options) {
options = options || {};
let dimensions = options.dimensions || [1, 1, 1];
let position = options.position || [-dimensions[0] / 2, -dimensions[1] / 2, -dimensions[2] / 2];
let x = position[0];
let y = position[1];
let z = position[2];
let width = dimensions[0];
let height = dimensions[1];
let depth = dimensions[2];
let fbl = {x: x, y: y, z: z + depth};
let fbr = {x: x + width, y: y, z: z + depth};
let ftl = {x: x, y: y + height, z: z + depth};
let ftr = {x: x + width, y: y + height, z: z + depth};
let bbl = {x: x, y: y, z: z };
let bbr = {x: x + width, y: y, z: z };
let btl = {x: x, y: y + height, z: z };
let btr = {x: x + width, y: y + height, z: z };
let positions = new Float32Array([
//front
fbl.x, fbl.y, fbl.z,
fbr.x, fbr.y, fbr.z,
ftl.x, ftl.y, ftl.z,
ftl.x, ftl.y, ftl.z,
fbr.x, fbr.y, fbr.z,
ftr.x, ftr.y, ftr.z,
//right
fbr.x, fbr.y, fbr.z,
bbr.x, bbr.y, bbr.z,
ftr.x, ftr.y, ftr.z,
ftr.x, ftr.y, ftr.z,
bbr.x, bbr.y, bbr.z,
btr.x, btr.y, btr.z,
//back
fbr.x, bbr.y, bbr.z,
bbl.x, bbl.y, bbl.z,
btr.x, btr.y, btr.z,
btr.x, btr.y, btr.z,
bbl.x, bbl.y, bbl.z,
btl.x, btl.y, btl.z,
//left
bbl.x, bbl.y, bbl.z,
fbl.x, fbl.y, fbl.z,
btl.x, btl.y, btl.z,
btl.x, btl.y, btl.z,
fbl.x, fbl.y, fbl.z,
ftl.x, ftl.y, ftl.z,
//top
ftl.x, ftl.y, ftl.z,
ftr.x, ftr.y, ftr.z,
btl.x, btl.y, btl.z,
btl.x, btl.y, btl.z,
ftr.x, ftr.y, ftr.z,
btr.x, btr.y, btr.z,
//bottom
bbl.x, bbl.y, bbl.z,
bbr.x, bbr.y, bbr.z,
fbl.x, fbl.y, fbl.z,
fbl.x, fbl.y, fbl.z,
bbr.x, bbr.y, bbr.z,
fbr.x, fbr.y, fbr.z
]);
let uvs = new Float32Array([
//front
0, 0,
1, 0,
0, 1,
0, 1,
1, 0,
1, 1,
//right
0, 0,
1, 0,
0, 1,
0, 1,
1, 0,
1, 1,
//back
0, 0,
1, 0,
0, 1,
0, 1,
1, 0,
1, 1,
//left
0, 0,
1, 0,
0, 1,
0, 1,
1, 0,
1, 1,
//top
0, 0,
1, 0,
0, 1,
0, 1,
1, 0,
1, 1,
//bottom
0, 0,
1, 0,
0, 1,
0, 1,
1, 0,
1, 1
]);
let normals = new Float32Array([
// front
0, 0, 1,
0, 0, 1,
0, 0, 1,
0, 0, 1,
0, 0, 1,
0, 0, 1,
// right
1, 0, 0,
1, 0, 0,
1, 0, 0,
1, 0, 0,
1, 0, 0,
1, 0, 0,
// back
0, 0, -1,
0, 0, -1,
0, 0, -1,
0, 0, -1,
0, 0, -1,
0, 0, -1,
// left
-1, 0, 0,
-1, 0, 0,
-1, 0, 0,
-1, 0, 0,
-1, 0, 0,
-1, 0, 0,
// top
0, 1, 0,
0, 1, 0,
0, 1, 0,
0, 1, 0,
0, 1, 0,
0, 1, 0,
// bottom
0, -1, 0,
0, -1, 0,
0, -1, 0,
0, -1, 0,
0, -1, 0,
0, -1, 0
]);
return {
positions: positions,
normals: normals,
uvs: uvs
};
},
createSphere(options) {
options = options || {};
let longBands = options.longBands || 32;
let latBands = options.latBands || 32;
let radius = options.radius || 1;
let lat_step = Math.PI / latBands;
let long_step = 2 * Math.PI / longBands;
let num_positions = longBands * latBands * 4;
let num_indices = longBands * latBands * 6;
let lat_angle, long_angle;
let positions = new Float32Array(num_positions * 3);
let normals = new Float32Array(num_positions * 3);
let uvs = new Float32Array(num_positions * 2);
let indices = new Uint16Array(num_indices);
let x1, x2, x3, x4,
y1, y2,
z1, z2, z3, z4,
u1, u2,
v1, v2;
let i, j;
let k = 0, l = 0;
let vi, ti;
for (i = 0; i < latBands; i++) {
lat_angle = i * lat_step;
y1 = Math.cos(lat_angle);
y2 = Math.cos(lat_angle + lat_step);
for (j = 0; j < longBands; j++) {
long_angle = j * long_step;
x1 = Math.sin(lat_angle) * Math.cos(long_angle);
x2 = Math.sin(lat_angle) * Math.cos(long_angle + long_step);
x3 = Math.sin(lat_angle + lat_step) * Math.cos(long_angle);
x4 = Math.sin(lat_angle + lat_step) * Math.cos(long_angle + long_step);
z1 = Math.sin(lat_angle) * Math.sin(long_angle);
z2 = Math.sin(lat_angle) * Math.sin(long_angle + long_step);
z3 = Math.sin(lat_angle + lat_step) * Math.sin(long_angle);
z4 = Math.sin(lat_angle + lat_step) * Math.sin(long_angle + long_step);
u1 = 1 - j / longBands;
u2 = 1 - (j + 1) / longBands;
v1 = 1 - i / latBands;
v2 = 1 - (i + 1) / latBands;
vi = k * 3;
ti = k * 2;
positions[vi] = x1 * radius;
positions[vi+1] = y1 * radius;
positions[vi+2] = z1 * radius; //v0
positions[vi+3] = x2 * radius;
positions[vi+4] = y1 * radius;
positions[vi+5] = z2 * radius; //v1
positions[vi+6] = x3 * radius;
positions[vi+7] = y2 * radius;
positions[vi+8] = z3 * radius; // v2
positions[vi+9] = x4 * radius;
positions[vi+10] = y2 * radius;
positions[vi+11] = z4 * radius; // v3
normals[vi] = x1;
normals[vi+1] = y1;
normals[vi+2] = z1;
normals[vi+3] = x2;
normals[vi+4] = y1;
normals[vi+5] = z2;
normals[vi+6] = x3;
normals[vi+7] = y2;
normals[vi+8] = z3;
normals[vi+9] = x4;
normals[vi+10] = y2;
normals[vi+11] = z4;
uvs[ti] = u1;
uvs[ti+1] = v1;
uvs[ti+2] = u2;
uvs[ti+3] = v1;
uvs[ti+4] = u1;
uvs[ti+5] = v2;
uvs[ti+6] = u2;
uvs[ti+7] = v2;
indices[l ] = k;
indices[l + 1] = k + 1;
indices[l + 2] = k + 2;
indices[l + 3] = k + 2;
indices[l + 4] = k + 1;
indices[l + 5] = k + 3;
k += 4;
l += 6;
}
}
return {
positions: positions,
normals: normals,
uvs: uvs,
indices: indices
};
},
computeBoundingBox(position, options) {
options = options || {};
let buildGeometry = options.buildGeometry || false;
let boundary = {
min: vec3.create(),
max: vec3.create()
};
vec3.set(boundary.min, Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY);
vec3.set(boundary.max, Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY);
for (let i = 0, len = position.length; i < len; i += 3) {
boundary.min[0] = Math.min(position[i], boundary.min[0]);
boundary.max[0] = Math.max(position[i], boundary.max[0]);
boundary.min[1] = Math.min(position[i + 1], boundary.min[1]);
boundary.max[1] = Math.max(position[i + 1], boundary.max[1]);
boundary.min[2] = Math.min(position[i + 2], boundary.min[2]);
boundary.max[2] = Math.max(position[i + 2], boundary.max[2]);
}
if (buildGeometry) {
let size = vec3.create();
vec3.subtract(size, boundary.max, boundary.min);
boundary.geometry = utils.createBox({
position: boundary.min,
dimensions: size
});
}
return boundary;
},
addTimerElement() {
this.timerDiv = document.createElement("div");
this.timerDiv.id = "timer";
this.cpuTimeElement = document.createElement("div");
this.gpuTimeElement = document.createElement("div");
this.timerDiv.appendChild(this.cpuTimeElement);
this.timerDiv.appendChild(this.gpuTimeElement);
document.body.appendChild(this.timerDiv);
},
updateTimerElement(cpuTime, gpuTime) {
cpuTimeSum += cpuTime;
gpuTimeSum += gpuTime;
++timeSampleCount;
if (timeSampleCount === NUM_TIMING_SAMPLES) {
let cpuTimeAve = cpuTimeSum / NUM_TIMING_SAMPLES;
let gpuTimeAve = gpuTimeSum / NUM_TIMING_SAMPLES;
this.cpuTimeElement.innerText = "CPU time: " + cpuTimeAve.toFixed(3) + "ms";
if (gpuTimeAve > 0) {
this.gpuTimeElement.innerText = "GPU time: " + gpuTimeAve.toFixed(3) + "ms";
} else {
this.gpuTimeElement.innerText = "GPU time: (Unavailable)";
}
cpuTimeSum = 0;
gpuTimeSum = 0;
timeSampleCount = 0;
}
},
loadBinary(url) {
return new Promise((resolve) => {
let xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.responseType = "arraybuffer";
xhr.onload = function() {
resolve(xhr.response);
};
xhr.send(null);
});
},
loadBinaries(urls) {
return Promise.all(urls.map(url => this.loadBinary(url)));
},
// http://cdn.imgtec.com/sdk-documentation/PVR+File+Format.Specification.pdf
parsePVR(data) {
let header = new Uint32Array(data, 0, PVR_CONSTANTS.HEADER_LENGTH);
let pvrFormat = header[PVR_CONSTANTS.PIXEL_FORMAT_INDEX];
let formatEnum = PVR_CONSTANTS.FORMATS[pvrFormat];
let sizeFunction = PVR_CONSTANTS.SIZE_FUNCTIONS[pvrFormat];
let mipMapLevels = header[PVR_CONSTANTS.MIPMAPCOUNT_INDEX];
let width = header[PVR_CONSTANTS.WIDTH_INDEX];
let height = header[PVR_CONSTANTS.HEIGHT_INDEX];
let dataOffset = PVR_CONSTANTS.HEADER_SIZE + header[PVR_CONSTANTS.METADATA_SIZE_INDEX];
let image = new Uint8Array(data, dataOffset);
let levels = new Array(mipMapLevels);
let levelWidth = width;
let levelHeight = height;
let offset = 0;
for (let i = 0; i < mipMapLevels; ++i) {
let levelSize = sizeFunction(levelWidth, levelHeight);
levels[i] = new Uint8Array(image.buffer, image.byteOffset + offset, levelSize);
levelWidth = Math.max(1, levelWidth >> 1);
levelHeight = Math.max(1, levelHeight >> 1);
offset += levelSize;
}
return {
data: levels,
width: width,
height: height,
format: formatEnum
}
}
};
// Fullscreen triangle with positions and uvs
// (will actually be clipped to quad with uvs in range 0-1)
const positions = app.createVertexBuffer( PicoGL.FLOAT, 4,
new Float32Array( [
-1, -1, 0, 0, // x, y, uvx, uvy
3, -1, 2, 0, // x, y, uvx, uvy
-1, 3, 0, 2 // x, y, uvx, uvy
] )
);
const vertexArray = app.createVertexArray()
.vertexAttributeBuffer( 0, positions );
var vertexShaderSource = "#version 300 es\n\nlayout(std140, column_major) uniform;\n\nlayout(location=0) in vec4 position; // xy: position, zw: uv\n\nout vec2 vUV;\n\nvoid main() {\n vUV = position.zw;\n gl_Position = vec4( position.xy, 0.0, 1.0 );\n}\n";
var fragmentShaderGaussianHorizontalAlpha = "#version 300 es\nprecision highp float;\n\nlayout(std140, column_major) uniform;\n\nuniform sampler2D tex;\n\nuniform FilterParams {\n vec4 step;\n float alphaRadius;\n float betaRadius;\n float ratio;\n float gaussianWindowSize;\n};\n\nin vec2 vUV;\n\n//layout(location=0) out vec4 alphaSeparable;\nlayout(location=0) out vec4 finalColor;\n\nvoid main() {\n // scale the blur radius based weather or not the UI is active\n // if not the ratio is 1.0 so no blur scaling is applied\n float scaleRadius = ratio * alphaRadius;\n\n float N = min( pow( 2.0, ceil( log2( 3.0 * scaleRadius ) ) ), ratio * gaussianWindowSize );\n // Find closet square pixel\n // Do this by taking the scaled radius and multiplying it by 3\n // Then multiply that by 0.25 to derive 1/4 of the radius\n // Floor that value to get the closest integer\n // Then multiply that value by 4 to get the closest square pixel\n // if that value is below 2.0, 2.0 will be the minimum value provided\n\n // Then get the gaussian windows size and multiply it by the ratio to get the correct scale\n // Assign the smallest of this value to N\n //float N = min( max( floor( (3.0 * scaleRadius) * 0.25 ) * 4.0, 2.0 ), ratio * gaussianWindowSize );\n\n float scaler = -1.0 / ( 2.0 * scaleRadius * scaleRadius );\n\n vec4 baseColor_A = texture( tex, vUV ).rgba;\n baseColor_A.rgb *= baseColor_A.a;\n vec4 totalColor_A = baseColor_A;\n float totalWeight_A = 1.0;\n vec2 delta = vec2( step.x, step.y );\n\nfor ( float s = 1.0; s <= N; s += 2.0 ) {\n // Sample from left and right at same time to re-use weight\n vec2 p = vec2( s, s + 1.0 );\n\n vec2 weights_A = exp( p * p * scaler );\n float weight_A = dot( vec2( 1.0 ), weights_A) ;\n float offset_A = dot( p, weights_A) / weight_A;\n\n // baseColor_A = texture( tex, vUV + delta * offset_A ).rgba;\n baseColor_A = texture( tex, vUV + delta * offset_A ).rgba;\n baseColor_A.rgb *= baseColor_A.a;\n totalColor_A += baseColor_A * weight_A;\n\n // baseColor_A = texture( tex, vUV - delta * offset_A ).rgba;\n baseColor_A = texture( tex, vUV - delta * offset_A ).rgba;\n baseColor_A.rgb *= baseColor_A.a;\n totalColor_A += baseColor_A * weight_A;\n\n totalWeight_A += 2.0 * weight_A;\n\n }\n\n //alphaSeparable = vec4( totalColor_A / totalWeight_A );\n finalColor = totalColor_A / totalWeight_A;\n}\n";
var fragmentShaderGaussianVerticalAlpha = "#version 300 es\nprecision highp float;\n\nlayout(std140, column_major) uniform;\n\nuniform sampler2D alphaSeparableHorizontal;\n\nuniform FilterParams {\n vec4 step;\n float alphaRadius;\n float betaRadius;\n float ratio;\n float gaussianWindowSize;\n};\n\nin vec2 vUV;\n\n//layout(location=0) out vec4 alpha;\nlayout(location=0) out vec4 finalColor;\n\n\n\nvoid main() {\n // scale the blur radius based weather or not the UI is active\n // if not the ratio is 1.0 so no blur scaling is applied\n float scaleRadius = ratio * alphaRadius;\n\n float N = min( pow( 2.0, ceil( log2( 3.0 * scaleRadius ) ) ), ratio * gaussianWindowSize );\n // Find closet square pixel\n // Do this by taking the scaled radius and multiplying it by 3\n // Then multiply that by 0.25 to derive 1/4 of the radius\n // Floor that value to get the closest integer\n // Then multiply that value by 4 to get the closest square pixel\n // if that value is below 2.0, 2.0 will be the minimum value provided\n\n // Then get the gaussian windows size and multiply it by the ratio to get the correct scale\n // Assign the smallest of this value to N\n //float N = min( max( floor( (3.0 * scaleRadius) * 0.25 ) * 4.0, 2.0 ), ratio * gaussianWindowSize );\n\n float scaler = -1.0 / ( 2.0 * scaleRadius * scaleRadius );\n\n vec4 baseColor_A = texture( alphaSeparableHorizontal, vUV ).rgba;\n baseColor_A.rgb *= baseColor_A.a;\n vec4 totalColor_A = baseColor_A;\n float totalWeight_A = 1.0;\n vec2 delta = vec2( step.z, step.w );\n\n for ( float s = 1.0; s <= N; s += 2.0 ) {\n // Sample from left and right at same time to re-use weight\n vec2 p = vec2( s, s + 1.0 );\n\n vec2 weights_A = exp( p * p * scaler );\n float weight_A = dot( vec2( 1.0 ), weights_A) ;\n float offset_A = dot( p, weights_A) / weight_A;\n\n // baseColor_A = texture( alphaSeparableBuffer, vUV + delta * offset_A ).rgba;\n baseColor_A = texture( alphaSeparableHorizontal, vUV + delta * offset_A ).rgba;\n baseColor_A.rgb *= baseColor_A.a;\n totalColor_A += weight_A * baseColor_A;\n\n // baseColor_A = texture( alphaSeparableBuffer, vUV - delta * offset_A ).rgba;\n baseColor_A = texture( alphaSeparableHorizontal, vUV - delta * offset_A ).rgba;\n baseColor_A.rgb *= baseColor_A.a;\n totalColor_A += weight_A * baseColor_A;\n\n totalWeight_A += 2.0 * weight_A;\n\n }\n\n /*vec4 finalColor = clamp( totalColor_A / totalWeight_A, 0.0, 1.0 );\n alpha = vec4( finalColor );*/\n\n //finalColor = clamp( totalColor_A / totalWeight_A, 0.0, 1.0 );\n finalColor = totalColor_A / totalWeight_A;\n\n}\n";
var fragmentShaderImageThroughput = "#version 300 es\nprecision highp float;\n\nlayout(std140, column_major) uniform;\n\nuniform sampler2D throughputBuffer;\n\nin vec2 vUV;\n\nlayout(location=0) out vec4 outColor;\n\n\nvoid main() {\n\nvec4 base = texture( throughputBuffer, vUV ).rgba;\n\noutColor = base;\n\n}\n";
const filter = {
name: 'Test Electron 4 bit - 16 bit', drawCall: null
};
const shaders = [
[ vertexShaderSource, fragmentShaderGaussianHorizontalAlpha ],
[ vertexShaderSource, fragmentShaderGaussianVerticalAlpha ],
[ vertexShaderSource, fragmentShaderImageThroughput ],
];
app.createPrograms( ...shaders ).then( ( programs ) => {
let drawCall_GaussianHorizontalAlpha = app.createDrawCall( programs[ 0 ], vertexArray );
drawCall_GaussianHorizontalAlpha.uniformBlock( 'FilterParams', app.filterParams.Gaussian );
let drawCall_GaussianVerticalAlpha = app.createDrawCall( programs[ 1 ], vertexArray );
drawCall_GaussianVerticalAlpha.uniformBlock( 'FilterParams', app.filterParams.Gaussian );
let drawCall_ImageThroughPut = app.createDrawCall( programs[ 2 ], vertexArray );
let buffer = [];
for( let i = 0; i < app.initial.bitDepths.length; i++ ){
// for each bit-depths located in the "app.initial.bitDepths" array index
let temp = []; // clear the temp frame buffer array
for( let j = 0; j < 1; j++ ){
// create 5 temp frame buffers
// push each frame buffer to the temp array
temp.push(
app.createFramebuffer()
.colorTarget( 0, app.createTexture2D(
app.width, app.height, {
internalFormat: app.initial.bitDepths[ i ].internalFormat,
flipY: true,
maxAnisotropy: PicoGL.WEBGL_INFO.MAX_TEXTURE_ANISOTROPY,
wrapS: PicoGL.CLAMP_TO_EDGE,
wrapT: PicoGL.CLAMP_TO_EDGE
} )
)
);
}
let result = []; // clear the result frame buffer array
for( let k = 0; k < 1; k++ ){
// create 2 result frame buffers
// push each frame buffer to the result array
result.push(
app.createFramebuffer()
.colorTarget( 0, app.createTexture2D(
app.width, app.height, {
internalFormat: app.initial.bitDepths[ i ].internalFormat,
flipY: true,
maxAnisotropy: PicoGL.WEBGL_INFO.MAX_TEXTURE_ANISOTROPY,
wrapS: PicoGL.CLAMP_TO_EDGE,
wrapT: PicoGL.CLAMP_TO_EDGE
} )
)
);
}
// once both the temp and result arrays are filled
// push those arrays to the buffer array
// at the index of its associated bit-depth
buffer.push( [ temp, result ] );
}
console.log(buffer);
console.assert(
buffer[ app.layerCollection.menuBar.dropdownIndex.bitDepth ][ 0 ][ 0 ].getStatus() === PicoGL.FRAMEBUFFER_COMPLETE,
buffer[ app.layerCollection.menuBar.dropdownIndex.bitDepth ][ 1 ][ 0 ].getStatus() === PicoGL.FRAMEBUFFER_COMPLETE,
'G-buffer framebuffer is not complete!' );
let temp = 0; // the index for the temp frame buffer array
let result = 1; // the index for the result frame buffer array
filter.drawCall = {
draw: () => {
let bitDepthIndex = app.defaultMenuBar.dropdownIndex.bitDepth;
// Check render targets have correct size
if ( buffer[ bitDepthIndex ][ temp ][ 0 ].width !== app.width || buffer[ bitDepthIndex ][ temp ][ 0 ].height !== app.height ) {
for( let i = 0; i < app.initial.bitDepths.length; i++ ){
for( let j = 0; j < 1; j++ ){
buffer[ i ][ 0 ][ j ].resize( app.width, app.height );
}
for( let k = 0; k < 1; k++ ){
buffer[ i ][ 1 ][ k ].resize( app.width, app.height );
}
}
}
// Call the Gaussian Blur filter
drawCall_GaussianHorizontalAlpha.texture( 'tex', app.texture );
// Write to Buffer
app.drawFramebuffer( buffer[ bitDepthIndex ][ temp ][ 0 ] ).clear();
drawCall_GaussianHorizontalAlpha.draw();
// Feed output of previous stage in next stage
drawCall_GaussianVerticalAlpha.texture( 'alphaSeparableHorizontal', buffer[ bitDepthIndex ][ temp ][ 0 ].colorAttachments[ 0 ] );
/*
// Write to screen
app.defaultDrawFramebuffer().clear();
drawCall_GaussianVerticalAlpha.draw();
// !! NOTE THAT IF YOU ENABLE THE ABOVE, WRITE TO SCREEN CODE
// !! AND THEN COMMENT OUT THE CODE BELOW THIS LINE
// !! EVEN THOUGH THE DEMO IS SET TO USE THE 4-BIT BUFFER
// !! THE RESULTS WILL BE AS IF YOU ARE NOT WORKING WITH 4-BIT DATA AND DISPLAY AS IF IT'S 8-BIT
// !! SIMILAR TO WHAT IS OBSERVED WHEN UPDATING FROM ELECTRON 17.4.11 TO ELECTRON 21.4.1 OR GREATER AND LEAVING THE CODE AS IS.
*/
// Write to Buffer
app.drawFramebuffer( buffer[ bitDepthIndex ][ result ][ 0 ] ).clear();
drawCall_GaussianVerticalAlpha.draw();
// Pass to ImageThroughPut filer
// NOTE!!!
// If you bypass this the resulting image appears as an 8 bit image even though it is in 4 bit
drawCall_ImageThroughPut.texture( 'throughputBuffer', buffer[ bitDepthIndex ][ result ][ 0 ].colorAttachments[ 0 ] );
// Write to screen
app.defaultDrawFramebuffer().clear();
drawCall_ImageThroughPut.draw();
},
};
});
app.activeFilter = filter; // set active filter to the gaussian filter
let webglCanvas = document.getElementById( 'webgl-canvas-container' );
let webgl_canvas_layers = window.document.styleSheets[1];
app.function.menuBar = {
main: {
loadImageMenuBar: async ( files ) => {
return new Promise( async ( resolve ) => {
imageDropdown.style.display = 'inline';
let getBlobFromPath = ( file, callback ) => {
let xhr = new XMLHttpRequest();
xhr.onload = function() {
//let blob = xhr.response;
callback( xhr.response );
};
xhr.open( 'GET', file );
xhr.responseType = 'blob';
xhr.send();
};
// for each image in the image array
for( let j = 0; j < files.length; j++ ){
await new Promise( async ( resolve ) => {
let file = files[j];
await getBlobFromPath(file, async (blob) => {
let imageUrl = window.URL.createObjectURL(blob);
let imageName = file.replace(/^.*[\\\/]/, '');
// create an image object for the dropdown to access
let imageObject = {
index: app.imageArray.length,
type: 'Image',
url: imageUrl,
name: imageName,
};
await app.function.menuBar.loadImage.append(imageObject); // create a dropdown item
app.imageArray.push(imageObject); // push image url and name to the array
await utils.loadImages([imageObject.url]).then(([blob]) => {
return new Promise(async (resolve) => {
let textures = [];
// for each bitDepth dropdown item
// create a texture with that bitDepth
for (let k = 0; k < app.initial.bitDepths.length; k++) {
textures.push(
app.createTexture2D( blob, {
internalFormat: app.initial.bitDepths[k].internalFormat,
flipY: true,
maxAnisotropy: PicoGL.WEBGL_INFO.MAX_TEXTURE_ANISOTROPY,
wrapS: PicoGL.CLAMP_TO_EDGE,
wrapT: PicoGL.CLAMP_TO_EDGE
})
);
}
resolve(app.textureArray.push(textures)); // push bit depths to the texture array
});
});
img.src = file;
img.style.display = "inline-block";
app.texture = app.textureArray[imageDropdown.selectedIndex][bitdepth.selectedIndex];
let name = `Image ${ j }`;
let styleName = `Output Canvas ${ name }`; // CSS style sheet canvas style name
let z = app.initial.zIndex.canvasWebGL; // for z index
webgl_canvas_layers.insertRule(`#${ styleName.replace(/\s/g, '') } {
max-width: ${ 800 }px;
max-height: ${ 600 }px;
z-index: ${ z };
}`, j );
let canvas = document.createElement('canvas');
canvas.id = styleName.replace(/\s/g, '');
canvas.width = app.texture.width;
canvas.height = app.texture.height;
// insert canvas into '#webgl-canvas-collection' div
webglCanvas.insertBefore( canvas, webglCanvas.childNodes[ j ] ); // insert at Index "i"
resolve();
});
});
}
// after all images are loaded
// render the images at index 0
imageDropdown.selectedIndex = 0; // set dropdown to index 0
await app.function.menuBar.loadImage.update(); // get image from dropdown
resolve();
});
},
},
dropdown:{
bitdepth: () => {
return new Promise( async ( resolve ) => {
app.texture = app.textureArray[ imageDropdown.selectedIndex ][ bitdepth.selectedIndex ];
app.defaultMenuBar.dropdownIndex.bitDepth = bitdepth.selectedIndex;
app.needsRender = true;
await app.function.webGL.draw(); // DRAW - draw the image on the canvas //
resolve();
});
},
},
loadImage:{
append: async ( image ) => {
return new Promise( ( resolve ) => {
let option = document.createElement( 'option' ); // create a dropdown item
option.innerHTML = image.name; // use the image name as the dropdown item name
imageDropdown.append( option ); // place it in the dropdown
imageDropdown.selectedIndex = imageDropdown.length - 1;
resolve();
});
},
update: () => {
return new Promise( async ( resolve ) => {
// if there is a canvas context for the app layer collection
// clear that canvas so the new image can be drawn
if( app.layerCollection.webglCTX ){
app.layerCollection.webglCTX.clearRect(0, 0, app.texture.width, app.texture.height);
}
app.layerCollection.renderIndex = imageDropdown.selectedIndex;
img.src = app.imageArray[imageDropdown.selectedIndex].url;
img.style.display = "inline-block";
app.texture = app.textureArray[imageDropdown.selectedIndex][bitdepth.selectedIndex];
// Get and set the active WebGL output canvas
app.layerCollection.webglCanvas = webglCanvas.children[ imageDropdown.selectedIndex ]; // get the webgl output canvas
app.layerCollection.webglCTX = app.layerCollection.webglCanvas.getContext('2d'); // set the webgl canvas context
app.needsRender = true;
await app.function.webGL.draw(); // DRAW - draw the image on the canvas //
resolve();
});
},
},
};
///////////////////// LOAD IMAGES //////////////////////////
let img = document.getElementById( 'reference-image' );
let imageDropdown = document.getElementById( 'selectImageDropdown' );
imageDropdown.oninput = async ( e ) => {
await app.function.menuBar.loadImage.update(); // get image from dropdown
e.stopPropagation();
e.preventDefault();
};
let imgFromHTML = document.getElementById("testImage");
console.log(imgFromHTML.src)
let imageFiles = [
/*'images/image.jpg',
'images/image1.png',*/
/*"https://fastly.picsum.photos/id/972/500/500.jpg?hmac=EOMk-JjJvw1q19X1yyDoUZIIUZJFl1wWEKz9z4D5YVk",*/
imgFromHTML.src,
];
app.function.menuBar.main.loadImageMenuBar( imageFiles );
let formatValue = ( x ) => x.toFixed( 2 );
let setupSlider = ( name, uniformIndex ) => {
let slider = document.getElementById( `${name}Slider` );
let label = document.getElementById( `${name}Amount` );
label.innerHTML = formatValue( 445.72 );
slider.oninput = async () => {
let v = slider.value / 1000.0;
if( v === 0 ){
v = 0.0;
}else {
v = Math.pow( 2.0, ( v * 12.0 ) ) * 0.25;
}
label.innerHTML = formatValue( v );
app.filterParams.Gaussian.set( uniformIndex, v ).update(); // alpha radius
app.needsRender = true;
await app.function.webGL.draw(); // DRAW - draw the image on the canvas //
};
};
setupSlider( 'Gaussian', 1 );
// create dropdown for bit-depth selection //
let bitdepth = document.getElementById( 'bitDepthDropdown' );
bitdepth.oninput = app.function.menuBar.dropdown.bitdepth;
// "app.initial.bitDepths" object that contains the
// bit-depth dropdown parameters
// for each item populate the bit-depth dropdown
// with their parameters
app.initial.bitDepths.forEach( depth => {
let option = document.createElement( 'option' );
option.innerHTML = depth.name;
bitdepth.append( option );
});
bitdepth.selectedIndex = app.layerCollection.menuBar.dropdownIndex.bitDepth;
// End color bit depth dropdown //
}));
//# sourceMappingURL=bundle.js.map
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment