(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : (global.createREGL = factory()); }(this, (function () { 'use strict'; var extend = function (base, opts) { var keys = Object.keys(opts); for (var i = 0; i < keys.length; ++i) { base[keys[i]] = opts[keys[i]]; } return base }; var VARIABLE_COUNTER = 0; var DYN_FUNC = 0; function DynamicVariable (type, data) { this.id = (VARIABLE_COUNTER++); this.type = type; this.data = data; } function escapeStr (str) { return str.replace(/\\/g, '\\\\').replace(/"/g, '\\"') } function splitParts (str) { if (str.length === 0) { return [] } var firstChar = str.charAt(0); var lastChar = str.charAt(str.length - 1); if (str.length > 1 && firstChar === lastChar && (firstChar === '"' || firstChar === "'")) { return ['"' + escapeStr(str.substr(1, str.length - 2)) + '"'] } var parts = /\[(false|true|null|\d+|'[^']*'|"[^"]*")\]/.exec(str); if (parts) { return ( splitParts(str.substr(0, parts.index)) .concat(splitParts(parts[1])) .concat(splitParts(str.substr(parts.index + parts[0].length))) ) } var subparts = str.split('.'); if (subparts.length === 1) { return ['"' + escapeStr(str) + '"'] } var result = []; for (var i = 0; i < subparts.length; ++i) { result = result.concat(splitParts(subparts[i])); } return result } function toAccessorString (str) { return '[' + splitParts(str).join('][') + ']' } function defineDynamic (type, data) { return new DynamicVariable(type, toAccessorString(data + '')) } function isDynamic (x) { return (typeof x === 'function' && !x._reglType) || x instanceof DynamicVariable } function unbox (x, path) { if (typeof x === 'function') { return new DynamicVariable(DYN_FUNC, x) } return x } var dynamic = { DynamicVariable: DynamicVariable, define: defineDynamic, isDynamic: isDynamic, unbox: unbox, accessor: toAccessorString }; /* globals requestAnimationFrame, cancelAnimationFrame */ var raf = { next: typeof requestAnimationFrame === 'function' ? function (cb) { return requestAnimationFrame(cb) } : function (cb) { return setTimeout(cb, 16) }, cancel: typeof cancelAnimationFrame === 'function' ? function (raf) { return cancelAnimationFrame(raf) } : clearTimeout }; /* globals performance */ var clock = (typeof performance !== 'undefined' && performance.now) ? function () { return performance.now() } : function () { return +(new Date()) }; function createStringStore () { var stringIds = {'': 0}; var stringValues = ['']; return { id: function (str) { var result = stringIds[str]; if (result) { return result } result = stringIds[str] = stringValues.length; stringValues.push(str); return result }, str: function (id) { return stringValues[id] } } } // Context and canvas creation helper functions function createCanvas (element, onDone, pixelRatio) { var canvas = document.createElement('canvas'); extend(canvas.style, { border: 0, margin: 0, padding: 0, top: 0, left: 0 }); element.appendChild(canvas); if (element === document.body) { canvas.style.position = 'absolute'; extend(element.style, { margin: 0, padding: 0 }); } function resize () { var w = window.innerWidth; var h = window.innerHeight; if (element !== document.body) { var bounds = element.getBoundingClientRect(); w = bounds.right - bounds.left; h = bounds.bottom - bounds.top; } canvas.width = pixelRatio * w; canvas.height = pixelRatio * h; extend(canvas.style, { width: w + 'px', height: h + 'px' }); } window.addEventListener('resize', resize, false); function onDestroy () { window.removeEventListener('resize', resize); element.removeChild(canvas); } resize(); return { canvas: canvas, onDestroy: onDestroy } } function createContext (canvas, contextAttributes) { function get (name) { try { return canvas.getContext(name, contextAttributes) } catch (e) { return null } } return ( get('webgl') || get('experimental-webgl') || get('webgl-experimental') ) } function isHTMLElement (obj) { return ( typeof obj.nodeName === 'string' && typeof obj.appendChild === 'function' && typeof obj.getBoundingClientRect === 'function' ) } function isWebGLContext (obj) { return ( typeof obj.drawArrays === 'function' || typeof obj.drawElements === 'function' ) } function parseExtensions (input) { if (typeof input === 'string') { return input.split() } return input } function getElement (desc) { if (typeof desc === 'string') { return document.querySelector(desc) } return desc } function parseArgs (args_) { var args = args_ || {}; var element, container, canvas, gl; var contextAttributes = {}; var extensions = []; var optionalExtensions = []; var pixelRatio = (typeof window === 'undefined' ? 1 : window.devicePixelRatio); var profile = false; var onDone = function (err) { if (err) { } }; var onDestroy = function () {}; if (typeof args === 'string') { element = document.querySelector(args); } else if (typeof args === 'object') { if (isHTMLElement(args)) { element = args; } else if (isWebGLContext(args)) { gl = args; canvas = gl.canvas; } else { if ('gl' in args) { gl = args.gl; } else if ('canvas' in args) { canvas = getElement(args.canvas); } else if ('container' in args) { container = getElement(args.container); } if ('attributes' in args) { contextAttributes = args.attributes; } if ('extensions' in args) { extensions = parseExtensions(args.extensions); } if ('optionalExtensions' in args) { optionalExtensions = parseExtensions(args.optionalExtensions); } if ('onDone' in args) { onDone = args.onDone; } if ('profile' in args) { profile = !!args.profile; } if ('pixelRatio' in args) { pixelRatio = +args.pixelRatio; } } } else { } if (element) { if (element.nodeName.toLowerCase() === 'canvas') { canvas = element; } else { container = element; } } if (!gl) { if (!canvas) { var result = createCanvas(container || document.body, onDone, pixelRatio); if (!result) { return null } canvas = result.canvas; onDestroy = result.onDestroy; } gl = createContext(canvas, contextAttributes); } if (!gl) { onDestroy(); onDone('webgl not supported, try upgrading your browser or graphics drivers http://get.webgl.org'); return null } return { gl: gl, canvas: canvas, container: container, extensions: extensions, optionalExtensions: optionalExtensions, pixelRatio: pixelRatio, profile: profile, onDone: onDone, onDestroy: onDestroy } } function createExtensionCache (gl, config) { var extensions = {}; function tryLoadExtension (name_) { var name = name_.toLowerCase(); var ext; try { ext = extensions[name] = gl.getExtension(name); } catch (e) {} return !!ext } for (var i = 0; i < config.extensions.length; ++i) { var name = config.extensions[i]; if (!tryLoadExtension(name)) { config.onDestroy(); config.onDone('"' + name + '" extension is not supported by the current WebGL context, try upgrading your system or a different browser'); return null } } config.optionalExtensions.forEach(tryLoadExtension); return { extensions: extensions, restore: function () { Object.keys(extensions).forEach(function (name) { if (extensions[name] && !tryLoadExtension(name)) { throw new Error('(regl): error restoring extension ' + name) } }); } } } function loop (n, f) { var result = Array(n); for (var i = 0; i < n; ++i) { result[i] = f(i); } return result } var GL_BYTE = 5120; var GL_UNSIGNED_BYTE$1 = 5121; var GL_SHORT = 5122; var GL_UNSIGNED_SHORT = 5123; var GL_INT = 5124; var GL_UNSIGNED_INT = 5125; var GL_FLOAT$1 = 5126; function nextPow16 (v) { for (var i = 16; i <= (1 << 28); i *= 16) { if (v <= i) { return i } } return 0 } function log2 (v) { var r, shift; r = (v > 0xFFFF) << 4; v >>>= r; shift = (v > 0xFF) << 3; v >>>= shift; r |= shift; shift = (v > 0xF) << 2; v >>>= shift; r |= shift; shift = (v > 0x3) << 1; v >>>= shift; r |= shift; return r | (v >> 1) } function createPool () { var bufferPool = loop(8, function () { return [] }); function alloc (n) { var sz = nextPow16(n); var bin = bufferPool[log2(sz) >> 2]; if (bin.length > 0) { return bin.pop() } return new ArrayBuffer(sz) } function free (buf) { bufferPool[log2(buf.byteLength) >> 2].push(buf); } function allocType (type, n) { var result = null; switch (type) { case GL_BYTE: result = new Int8Array(alloc(n), 0, n); break case GL_UNSIGNED_BYTE$1: result = new Uint8Array(alloc(n), 0, n); break case GL_SHORT: result = new Int16Array(alloc(2 * n), 0, n); break case GL_UNSIGNED_SHORT: result = new Uint16Array(alloc(2 * n), 0, n); break case GL_INT: result = new Int32Array(alloc(4 * n), 0, n); break case GL_UNSIGNED_INT: result = new Uint32Array(alloc(4 * n), 0, n); break case GL_FLOAT$1: result = new Float32Array(alloc(4 * n), 0, n); break default: return null } if (result.length !== n) { return result.subarray(0, n) } return result } function freeType (array) { free(array.buffer); } return { alloc: alloc, free: free, allocType: allocType, freeType: freeType } } var pool = createPool(); // zero pool for initial zero data pool.zero = createPool(); var GL_SUBPIXEL_BITS = 0x0D50; var GL_RED_BITS = 0x0D52; var GL_GREEN_BITS = 0x0D53; var GL_BLUE_BITS = 0x0D54; var GL_ALPHA_BITS = 0x0D55; var GL_DEPTH_BITS = 0x0D56; var GL_STENCIL_BITS = 0x0D57; var GL_ALIASED_POINT_SIZE_RANGE = 0x846D; var GL_ALIASED_LINE_WIDTH_RANGE = 0x846E; var GL_MAX_TEXTURE_SIZE = 0x0D33; var GL_MAX_VIEWPORT_DIMS = 0x0D3A; var GL_MAX_VERTEX_ATTRIBS = 0x8869; var GL_MAX_VERTEX_UNIFORM_VECTORS = 0x8DFB; var GL_MAX_VARYING_VECTORS = 0x8DFC; var GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS = 0x8B4D; var GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS = 0x8B4C; var GL_MAX_TEXTURE_IMAGE_UNITS = 0x8872; var GL_MAX_FRAGMENT_UNIFORM_VECTORS = 0x8DFD; var GL_MAX_CUBE_MAP_TEXTURE_SIZE = 0x851C; var GL_MAX_RENDERBUFFER_SIZE = 0x84E8; var GL_VENDOR = 0x1F00; var GL_RENDERER = 0x1F01; var GL_VERSION = 0x1F02; var GL_SHADING_LANGUAGE_VERSION = 0x8B8C; var GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT = 0x84FF; var GL_MAX_COLOR_ATTACHMENTS_WEBGL = 0x8CDF; var GL_MAX_DRAW_BUFFERS_WEBGL = 0x8824; var GL_TEXTURE_2D = 0x0DE1; var GL_TEXTURE_CUBE_MAP = 0x8513; var GL_TEXTURE_CUBE_MAP_POSITIVE_X = 0x8515; var GL_TEXTURE0 = 0x84C0; var GL_RGBA = 0x1908; var GL_FLOAT = 0x1406; var GL_UNSIGNED_BYTE = 0x1401; var GL_FRAMEBUFFER = 0x8D40; var GL_FRAMEBUFFER_COMPLETE = 0x8CD5; var GL_COLOR_ATTACHMENT0 = 0x8CE0; var GL_COLOR_BUFFER_BIT$1 = 0x4000; var wrapLimits = function (gl, extensions) { var maxAnisotropic = 1; if (extensions.ext_texture_filter_anisotropic) { maxAnisotropic = gl.getParameter(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT); } var maxDrawbuffers = 1; var maxColorAttachments = 1; if (extensions.webgl_draw_buffers) { maxDrawbuffers = gl.getParameter(GL_MAX_DRAW_BUFFERS_WEBGL); maxColorAttachments = gl.getParameter(GL_MAX_COLOR_ATTACHMENTS_WEBGL); } // detect if reading float textures is available (Safari doesn't support) var readFloat = !!extensions.oes_texture_float; if (readFloat) { var readFloatTexture = gl.createTexture(); gl.bindTexture(GL_TEXTURE_2D, readFloatTexture); gl.texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_FLOAT, null); var fbo = gl.createFramebuffer(); gl.bindFramebuffer(GL_FRAMEBUFFER, fbo); gl.framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, readFloatTexture, 0); gl.bindTexture(GL_TEXTURE_2D, null); if (gl.checkFramebufferStatus(GL_FRAMEBUFFER) !== GL_FRAMEBUFFER_COMPLETE) readFloat = false; else { gl.viewport(0, 0, 1, 1); gl.clearColor(1.0, 0.0, 0.0, 1.0); gl.clear(GL_COLOR_BUFFER_BIT$1); var pixels = pool.allocType(GL_FLOAT, 4); gl.readPixels(0, 0, 1, 1, GL_RGBA, GL_FLOAT, pixels); if (gl.getError()) readFloat = false; else { gl.deleteFramebuffer(fbo); gl.deleteTexture(readFloatTexture); readFloat = pixels[0] === 1.0; } pool.freeType(pixels); } } // detect non power of two cube textures support (IE doesn't support) var isIE = typeof navigator !== 'undefined' && (/MSIE/.test(navigator.userAgent) || /Trident\//.test(navigator.appVersion) || /Edge/.test(navigator.userAgent)); var npotTextureCube = true; if (!isIE) { var cubeTexture = gl.createTexture(); var data = pool.allocType(GL_UNSIGNED_BYTE, 36); gl.activeTexture(GL_TEXTURE0); gl.bindTexture(GL_TEXTURE_CUBE_MAP, cubeTexture); gl.texImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, GL_RGBA, 3, 3, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); pool.freeType(data); gl.bindTexture(GL_TEXTURE_CUBE_MAP, null); gl.deleteTexture(cubeTexture); npotTextureCube = !gl.getError(); } return { // drawing buffer bit depth colorBits: [ gl.getParameter(GL_RED_BITS), gl.getParameter(GL_GREEN_BITS), gl.getParameter(GL_BLUE_BITS), gl.getParameter(GL_ALPHA_BITS) ], depthBits: gl.getParameter(GL_DEPTH_BITS), stencilBits: gl.getParameter(GL_STENCIL_BITS), subpixelBits: gl.getParameter(GL_SUBPIXEL_BITS), // supported extensions extensions: Object.keys(extensions).filter(function (ext) { return !!extensions[ext] }), // max aniso samples maxAnisotropic: maxAnisotropic, // max draw buffers maxDrawbuffers: maxDrawbuffers, maxColorAttachments: maxColorAttachments, // point and line size ranges pointSizeDims: gl.getParameter(GL_ALIASED_POINT_SIZE_RANGE), lineWidthDims: gl.getParameter(GL_ALIASED_LINE_WIDTH_RANGE), maxViewportDims: gl.getParameter(GL_MAX_VIEWPORT_DIMS), maxCombinedTextureUnits: gl.getParameter(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS), maxCubeMapSize: gl.getParameter(GL_MAX_CUBE_MAP_TEXTURE_SIZE), maxRenderbufferSize: gl.getParameter(GL_MAX_RENDERBUFFER_SIZE), maxTextureUnits: gl.getParameter(GL_MAX_TEXTURE_IMAGE_UNITS), maxTextureSize: gl.getParameter(GL_MAX_TEXTURE_SIZE), maxAttributes: gl.getParameter(GL_MAX_VERTEX_ATTRIBS), maxVertexUniforms: gl.getParameter(GL_MAX_VERTEX_UNIFORM_VECTORS), maxVertexTextureUnits: gl.getParameter(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS), maxVaryingVectors: gl.getParameter(GL_MAX_VARYING_VECTORS), maxFragmentUniforms: gl.getParameter(GL_MAX_FRAGMENT_UNIFORM_VECTORS), // vendor info glsl: gl.getParameter(GL_SHADING_LANGUAGE_VERSION), renderer: gl.getParameter(GL_RENDERER), vendor: gl.getParameter(GL_VENDOR), version: gl.getParameter(GL_VERSION), // quirks readFloat: readFloat, npotTextureCube: npotTextureCube } }; var isTypedArray = function (x) { return ( x instanceof Uint8Array || x instanceof Uint16Array || x instanceof Uint32Array || x instanceof Int8Array || x instanceof Int16Array || x instanceof Int32Array || x instanceof Float32Array || x instanceof Float64Array || x instanceof Uint8ClampedArray ) }; function isNDArrayLike (obj) { return ( !!obj && typeof obj === 'object' && Array.isArray(obj.shape) && Array.isArray(obj.stride) && typeof obj.offset === 'number' && obj.shape.length === obj.stride.length && (Array.isArray(obj.data) || isTypedArray(obj.data))) } var values = function (obj) { return Object.keys(obj).map(function (key) { return obj[key] }) }; var flattenUtils = { shape: arrayShape$1, flatten: flattenArray }; function flatten1D (array, nx, out) { for (var i = 0; i < nx; ++i) { out[i] = array[i]; } } function flatten2D (array, nx, ny, out) { var ptr = 0; for (var i = 0; i < nx; ++i) { var row = array[i]; for (var j = 0; j < ny; ++j) { out[ptr++] = row[j]; } } } function flatten3D (array, nx, ny, nz, out, ptr_) { var ptr = ptr_; for (var i = 0; i < nx; ++i) { var row = array[i]; for (var j = 0; j < ny; ++j) { var col = row[j]; for (var k = 0; k < nz; ++k) { out[ptr++] = col[k]; } } } } function flattenRec (array, shape, level, out, ptr) { var stride = 1; for (var i = level + 1; i < shape.length; ++i) { stride *= shape[i]; } var n = shape[level]; if (shape.length - level === 4) { var nx = shape[level + 1]; var ny = shape[level + 2]; var nz = shape[level + 3]; for (i = 0; i < n; ++i) { flatten3D(array[i], nx, ny, nz, out, ptr); ptr += stride; } } else { for (i = 0; i < n; ++i) { flattenRec(array[i], shape, level + 1, out, ptr); ptr += stride; } } } function flattenArray (array, shape, type, out_) { var sz = 1; if (shape.length) { for (var i = 0; i < shape.length; ++i) { sz *= shape[i]; } } else { sz = 0; } var out = out_ || pool.allocType(type, sz); switch (shape.length) { case 0: break case 1: flatten1D(array, shape[0], out); break case 2: flatten2D(array, shape[0], shape[1], out); break case 3: flatten3D(array, shape[0], shape[1], shape[2], out, 0); break default: flattenRec(array, shape, 0, out, 0); } return out } function arrayShape$1 (array_) { var shape = []; for (var array = array_; array.length; array = array[0]) { shape.push(array.length); } return shape } var arrayTypes = { "[object Int8Array]": 5120, "[object Int16Array]": 5122, "[object Int32Array]": 5124, "[object Uint8Array]": 5121, "[object Uint8ClampedArray]": 5121, "[object Uint16Array]": 5123, "[object Uint32Array]": 5125, "[object Float32Array]": 5126, "[object Float64Array]": 5121, "[object ArrayBuffer]": 5121 }; var int8 = 5120; var int16 = 5122; var int32 = 5124; var uint8 = 5121; var uint16 = 5123; var uint32 = 5125; var float = 5126; var float32 = 5126; var glTypes = { int8: int8, int16: int16, int32: int32, uint8: uint8, uint16: uint16, uint32: uint32, float: float, float32: float32 }; var dynamic$1 = 35048; var stream = 35040; var usageTypes = { dynamic: dynamic$1, stream: stream, "static": 35044 }; var arrayFlatten = flattenUtils.flatten; var arrayShape = flattenUtils.shape; var GL_STATIC_DRAW = 0x88E4; var GL_STREAM_DRAW = 0x88E0; var GL_UNSIGNED_BYTE$2 = 5121; var GL_FLOAT$2 = 5126; var DTYPES_SIZES = []; DTYPES_SIZES[5120] = 1; // int8 DTYPES_SIZES[5122] = 2; // int16 DTYPES_SIZES[5124] = 4; // int32 DTYPES_SIZES[5121] = 1; // uint8 DTYPES_SIZES[5123] = 2; // uint16 DTYPES_SIZES[5125] = 4; // uint32 DTYPES_SIZES[5126] = 4; // float32 function typedArrayCode (data) { return arrayTypes[Object.prototype.toString.call(data)] | 0 } function copyArray (out, inp) { for (var i = 0; i < inp.length; ++i) { out[i] = inp[i]; } } function transpose ( result, data, shapeX, shapeY, strideX, strideY, offset) { var ptr = 0; for (var i = 0; i < shapeX; ++i) { for (var j = 0; j < shapeY; ++j) { result[ptr++] = data[strideX * i + strideY * j + offset]; } } } function wrapBufferState (gl, stats, config, attributeState) { var bufferCount = 0; var bufferSet = {}; function REGLBuffer (type) { this.id = bufferCount++; this.buffer = gl.createBuffer(); this.type = type; this.usage = GL_STATIC_DRAW; this.byteLength = 0; this.dimension = 1; this.dtype = GL_UNSIGNED_BYTE$2; this.persistentData = null; if (config.profile) { this.stats = {size: 0}; } } REGLBuffer.prototype.bind = function () { gl.bindBuffer(this.type, this.buffer); }; REGLBuffer.prototype.destroy = function () { destroy(this); }; var streamPool = []; function createStream (type, data) { var buffer = streamPool.pop(); if (!buffer) { buffer = new REGLBuffer(type); } buffer.bind(); initBufferFromData(buffer, data, GL_STREAM_DRAW, 0, 1, false); return buffer } function destroyStream (stream$$1) { streamPool.push(stream$$1); } function initBufferFromTypedArray (buffer, data, usage) { buffer.byteLength = data.byteLength; gl.bufferData(buffer.type, data, usage); } function initBufferFromData (buffer, data, usage, dtype, dimension, persist) { var shape; buffer.usage = usage; if (Array.isArray(data)) { buffer.dtype = dtype || GL_FLOAT$2; if (data.length > 0) { var flatData; if (Array.isArray(data[0])) { shape = arrayShape(data); var dim = 1; for (var i = 1; i < shape.length; ++i) { dim *= shape[i]; } buffer.dimension = dim; flatData = arrayFlatten(data, shape, buffer.dtype); initBufferFromTypedArray(buffer, flatData, usage); if (persist) { buffer.persistentData = flatData; } else { pool.freeType(flatData); } } else if (typeof data[0] === 'number') { buffer.dimension = dimension; var typedData = pool.allocType(buffer.dtype, data.length); copyArray(typedData, data); initBufferFromTypedArray(buffer, typedData, usage); if (persist) { buffer.persistentData = typedData; } else { pool.freeType(typedData); } } else if (isTypedArray(data[0])) { buffer.dimension = data[0].length; buffer.dtype = dtype || typedArrayCode(data[0]) || GL_FLOAT$2; flatData = arrayFlatten( data, [data.length, data[0].length], buffer.dtype); initBufferFromTypedArray(buffer, flatData, usage); if (persist) { buffer.persistentData = flatData; } else { pool.freeType(flatData); } } else { } } } else if (isTypedArray(data)) { buffer.dtype = dtype || typedArrayCode(data); buffer.dimension = dimension; initBufferFromTypedArray(buffer, data, usage); if (persist) { buffer.persistentData = new Uint8Array(new Uint8Array(data.buffer)); } } else if (isNDArrayLike(data)) { shape = data.shape; var stride = data.stride; var offset = data.offset; var shapeX = 0; var shapeY = 0; var strideX = 0; var strideY = 0; if (shape.length === 1) { shapeX = shape[0]; shapeY = 1; strideX = stride[0]; strideY = 0; } else if (shape.length === 2) { shapeX = shape[0]; shapeY = shape[1]; strideX = stride[0]; strideY = stride[1]; } else { } buffer.dtype = dtype || typedArrayCode(data.data) || GL_FLOAT$2; buffer.dimension = shapeY; var transposeData = pool.allocType(buffer.dtype, shapeX * shapeY); transpose(transposeData, data.data, shapeX, shapeY, strideX, strideY, offset); initBufferFromTypedArray(buffer, transposeData, usage); if (persist) { buffer.persistentData = transposeData; } else { pool.freeType(transposeData); } } else if (data instanceof ArrayBuffer) { buffer.dtype = GL_UNSIGNED_BYTE$2; buffer.dimension = dimension; initBufferFromTypedArray(buffer, data, usage); if (persist) { buffer.persistentData = new Uint8Array(new Uint8Array(data)); } } else { } } function destroy (buffer) { stats.bufferCount--; for (var i = 0; i < attributeState.state.length; ++i) { var record = attributeState.state[i]; if (record.buffer === buffer) { gl.disableVertexAttribArray(i); record.buffer = null; } } var handle = buffer.buffer; gl.deleteBuffer(handle); buffer.buffer = null; delete bufferSet[buffer.id]; } function createBuffer (options, type, deferInit, persistent) { stats.bufferCount++; var buffer = new REGLBuffer(type); bufferSet[buffer.id] = buffer; function reglBuffer (options) { var usage = GL_STATIC_DRAW; var data = null; var byteLength = 0; var dtype = 0; var dimension = 1; if (Array.isArray(options) || isTypedArray(options) || isNDArrayLike(options) || options instanceof ArrayBuffer) { data = options; } else if (typeof options === 'number') { byteLength = options | 0; } else if (options) { if ('data' in options) { data = options.data; } if ('usage' in options) { usage = usageTypes[options.usage]; } if ('type' in options) { dtype = glTypes[options.type]; } if ('dimension' in options) { dimension = options.dimension | 0; } if ('length' in options) { byteLength = options.length | 0; } } buffer.bind(); if (!data) { // #475 if (byteLength) gl.bufferData(buffer.type, byteLength, usage); buffer.dtype = dtype || GL_UNSIGNED_BYTE$2; buffer.usage = usage; buffer.dimension = dimension; buffer.byteLength = byteLength; } else { initBufferFromData(buffer, data, usage, dtype, dimension, persistent); } if (config.profile) { buffer.stats.size = buffer.byteLength * DTYPES_SIZES[buffer.dtype]; } return reglBuffer } function setSubData (data, offset) { gl.bufferSubData(buffer.type, offset, data); } function subdata (data, offset_) { var offset = (offset_ || 0) | 0; var shape; buffer.bind(); if (isTypedArray(data) || data instanceof ArrayBuffer) { setSubData(data, offset); } else if (Array.isArray(data)) { if (data.length > 0) { if (typeof data[0] === 'number') { var converted = pool.allocType(buffer.dtype, data.length); copyArray(converted, data); setSubData(converted, offset); pool.freeType(converted); } else if (Array.isArray(data[0]) || isTypedArray(data[0])) { shape = arrayShape(data); var flatData = arrayFlatten(data, shape, buffer.dtype); setSubData(flatData, offset); pool.freeType(flatData); } else { } } } else if (isNDArrayLike(data)) { shape = data.shape; var stride = data.stride; var shapeX = 0; var shapeY = 0; var strideX = 0; var strideY = 0; if (shape.length === 1) { shapeX = shape[0]; shapeY = 1; strideX = stride[0]; strideY = 0; } else if (shape.length === 2) { shapeX = shape[0]; shapeY = shape[1]; strideX = stride[0]; strideY = stride[1]; } else { } var dtype = Array.isArray(data.data) ? buffer.dtype : typedArrayCode(data.data); var transposeData = pool.allocType(dtype, shapeX * shapeY); transpose(transposeData, data.data, shapeX, shapeY, strideX, strideY, data.offset); setSubData(transposeData, offset); pool.freeType(transposeData); } else { } return reglBuffer } if (!deferInit) { reglBuffer(options); } reglBuffer._reglType = 'buffer'; reglBuffer._buffer = buffer; reglBuffer.subdata = subdata; if (config.profile) { reglBuffer.stats = buffer.stats; } reglBuffer.destroy = function () { destroy(buffer); }; return reglBuffer } function restoreBuffers () { values(bufferSet).forEach(function (buffer) { buffer.buffer = gl.createBuffer(); gl.bindBuffer(buffer.type, buffer.buffer); gl.bufferData( buffer.type, buffer.persistentData || buffer.byteLength, buffer.usage); }); } if (config.profile) { stats.getTotalBufferSize = function () { var total = 0; // TODO: Right now, the streams are not part of the total count. Object.keys(bufferSet).forEach(function (key) { total += bufferSet[key].stats.size; }); return total }; } return { create: createBuffer, createStream: createStream, destroyStream: destroyStream, clear: function () { values(bufferSet).forEach(destroy); streamPool.forEach(destroy); }, getBuffer: function (wrapper) { if (wrapper && wrapper._buffer instanceof REGLBuffer) { return wrapper._buffer } return null }, restore: restoreBuffers, _initBuffer: initBufferFromData } } var points = 0; var point = 0; var lines = 1; var line = 1; var triangles = 4; var triangle = 4; var primTypes = { points: points, point: point, lines: lines, line: line, triangles: triangles, triangle: triangle, "line loop": 2, "line strip": 3, "triangle strip": 5, "triangle fan": 6 }; var GL_POINTS = 0; var GL_LINES = 1; var GL_TRIANGLES = 4; var GL_BYTE$1 = 5120; var GL_UNSIGNED_BYTE$3 = 5121; var GL_SHORT$1 = 5122; var GL_UNSIGNED_SHORT$1 = 5123; var GL_INT$1 = 5124; var GL_UNSIGNED_INT$1 = 5125; var GL_ELEMENT_ARRAY_BUFFER = 34963; var GL_STREAM_DRAW$1 = 0x88E0; var GL_STATIC_DRAW$1 = 0x88E4; function wrapElementsState (gl, extensions, bufferState, stats) { var elementSet = {}; var elementCount = 0; var elementTypes = { 'uint8': GL_UNSIGNED_BYTE$3, 'uint16': GL_UNSIGNED_SHORT$1 }; if (extensions.oes_element_index_uint) { elementTypes.uint32 = GL_UNSIGNED_INT$1; } function REGLElementBuffer (buffer) { this.id = elementCount++; elementSet[this.id] = this; this.buffer = buffer; this.primType = GL_TRIANGLES; this.vertCount = 0; this.type = 0; } REGLElementBuffer.prototype.bind = function () { this.buffer.bind(); }; var bufferPool = []; function createElementStream (data) { var result = bufferPool.pop(); if (!result) { result = new REGLElementBuffer(bufferState.create( null, GL_ELEMENT_ARRAY_BUFFER, true, false)._buffer); } initElements(result, data, GL_STREAM_DRAW$1, -1, -1, 0, 0); return result } function destroyElementStream (elements) { bufferPool.push(elements); } function initElements ( elements, data, usage, prim, count, byteLength, type) { elements.buffer.bind(); if (data) { var predictedType = type; if (!type && ( !isTypedArray(data) || (isNDArrayLike(data) && !isTypedArray(data.data)))) { predictedType = extensions.oes_element_index_uint ? GL_UNSIGNED_INT$1 : GL_UNSIGNED_SHORT$1; } bufferState._initBuffer( elements.buffer, data, usage, predictedType, 3); } else { gl.bufferData(GL_ELEMENT_ARRAY_BUFFER, byteLength, usage); elements.buffer.dtype = dtype || GL_UNSIGNED_BYTE$3; elements.buffer.usage = usage; elements.buffer.dimension = 3; elements.buffer.byteLength = byteLength; } var dtype = type; if (!type) { switch (elements.buffer.dtype) { case GL_UNSIGNED_BYTE$3: case GL_BYTE$1: dtype = GL_UNSIGNED_BYTE$3; break case GL_UNSIGNED_SHORT$1: case GL_SHORT$1: dtype = GL_UNSIGNED_SHORT$1; break case GL_UNSIGNED_INT$1: case GL_INT$1: dtype = GL_UNSIGNED_INT$1; break default: } elements.buffer.dtype = dtype; } elements.type = dtype; // Check oes_element_index_uint extension // try to guess default primitive type and arguments var vertCount = count; if (vertCount < 0) { vertCount = elements.buffer.byteLength; if (dtype === GL_UNSIGNED_SHORT$1) { vertCount >>= 1; } else if (dtype === GL_UNSIGNED_INT$1) { vertCount >>= 2; } } elements.vertCount = vertCount; // try to guess primitive type from cell dimension var primType = prim; if (prim < 0) { primType = GL_TRIANGLES; var dimension = elements.buffer.dimension; if (dimension === 1) primType = GL_POINTS; if (dimension === 2) primType = GL_LINES; if (dimension === 3) primType = GL_TRIANGLES; } elements.primType = primType; } function destroyElements (elements) { stats.elementsCount--; delete elementSet[elements.id]; elements.buffer.destroy(); elements.buffer = null; } function createElements (options, persistent) { var buffer = bufferState.create(null, GL_ELEMENT_ARRAY_BUFFER, true); var elements = new REGLElementBuffer(buffer._buffer); stats.elementsCount++; function reglElements (options) { if (!options) { buffer(); elements.primType = GL_TRIANGLES; elements.vertCount = 0; elements.type = GL_UNSIGNED_BYTE$3; } else if (typeof options === 'number') { buffer(options); elements.primType = GL_TRIANGLES; elements.vertCount = options | 0; elements.type = GL_UNSIGNED_BYTE$3; } else { var data = null; var usage = GL_STATIC_DRAW$1; var primType = -1; var vertCount = -1; var byteLength = 0; var dtype = 0; if (Array.isArray(options) || isTypedArray(options) || isNDArrayLike(options)) { data = options; } else { if ('data' in options) { data = options.data; } if ('usage' in options) { usage = usageTypes[options.usage]; } if ('primitive' in options) { primType = primTypes[options.primitive]; } if ('count' in options) { vertCount = options.count | 0; } if ('type' in options) { dtype = elementTypes[options.type]; } if ('length' in options) { byteLength = options.length | 0; } else { byteLength = vertCount; if (dtype === GL_UNSIGNED_SHORT$1 || dtype === GL_SHORT$1) { byteLength *= 2; } else if (dtype === GL_UNSIGNED_INT$1 || dtype === GL_INT$1) { byteLength *= 4; } } } initElements( elements, data, usage, primType, vertCount, byteLength, dtype); } return reglElements } reglElements(options); reglElements._reglType = 'elements'; reglElements._elements = elements; reglElements.subdata = function (data, offset) { buffer.subdata(data, offset); return reglElements }; reglElements.destroy = function () { destroyElements(elements); }; return reglElements } return { create: createElements, createStream: createElementStream, destroyStream: destroyElementStream, getElements: function (elements) { if (typeof elements === 'function' && elements._elements instanceof REGLElementBuffer) { return elements._elements } return null }, clear: function () { values(elementSet).forEach(destroyElements); } } } var FLOAT = new Float32Array(1); var INT = new Uint32Array(FLOAT.buffer); var GL_UNSIGNED_SHORT$3 = 5123; function convertToHalfFloat (array) { var ushorts = pool.allocType(GL_UNSIGNED_SHORT$3, array.length); for (var i = 0; i < array.length; ++i) { if (isNaN(array[i])) { ushorts[i] = 0xffff; } else if (array[i] === Infinity) { ushorts[i] = 0x7c00; } else if (array[i] === -Infinity) { ushorts[i] = 0xfc00; } else { FLOAT[0] = array[i]; var x = INT[0]; var sgn = (x >>> 31) << 15; var exp = ((x << 1) >>> 24) - 127; var frac = (x >> 13) & ((1 << 10) - 1); if (exp < -24) { // round non-representable denormals to 0 ushorts[i] = sgn; } else if (exp < -14) { // handle denormals var s = -14 - exp; ushorts[i] = sgn + ((frac + (1 << 10)) >> s); } else if (exp > 15) { // round overflow to +/- Infinity ushorts[i] = sgn + 0x7c00; } else { // otherwise convert directly ushorts[i] = sgn + ((exp + 15) << 10) + frac; } } } return ushorts } function isArrayLike (s) { return Array.isArray(s) || isTypedArray(s) } var GL_COMPRESSED_TEXTURE_FORMATS = 0x86A3; var GL_TEXTURE_2D$1 = 0x0DE1; var GL_TEXTURE_CUBE_MAP$1 = 0x8513; var GL_TEXTURE_CUBE_MAP_POSITIVE_X$1 = 0x8515; var GL_RGBA$1 = 0x1908; var GL_ALPHA = 0x1906; var GL_RGB = 0x1907; var GL_LUMINANCE = 0x1909; var GL_LUMINANCE_ALPHA = 0x190A; var GL_RGBA4 = 0x8056; var GL_RGB5_A1 = 0x8057; var GL_RGB565 = 0x8D62; var GL_UNSIGNED_SHORT_4_4_4_4 = 0x8033; var GL_UNSIGNED_SHORT_5_5_5_1 = 0x8034; var GL_UNSIGNED_SHORT_5_6_5 = 0x8363; var GL_UNSIGNED_INT_24_8_WEBGL = 0x84FA; var GL_DEPTH_COMPONENT = 0x1902; var GL_DEPTH_STENCIL = 0x84F9; var GL_SRGB_EXT = 0x8C40; var GL_SRGB_ALPHA_EXT = 0x8C42; var GL_HALF_FLOAT_OES = 0x8D61; var GL_COMPRESSED_RGB_S3TC_DXT1_EXT = 0x83F0; var GL_COMPRESSED_RGBA_S3TC_DXT1_EXT = 0x83F1; var GL_COMPRESSED_RGBA_S3TC_DXT3_EXT = 0x83F2; var GL_COMPRESSED_RGBA_S3TC_DXT5_EXT = 0x83F3; var GL_COMPRESSED_RGB_ATC_WEBGL = 0x8C92; var GL_COMPRESSED_RGBA_ATC_EXPLICIT_ALPHA_WEBGL = 0x8C93; var GL_COMPRESSED_RGBA_ATC_INTERPOLATED_ALPHA_WEBGL = 0x87EE; var GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG = 0x8C00; var GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG = 0x8C01; var GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG = 0x8C02; var GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG = 0x8C03; var GL_COMPRESSED_RGB_ETC1_WEBGL = 0x8D64; var GL_UNSIGNED_BYTE$4 = 0x1401; var GL_UNSIGNED_SHORT$2 = 0x1403; var GL_UNSIGNED_INT$2 = 0x1405; var GL_FLOAT$3 = 0x1406; var GL_TEXTURE_WRAP_S = 0x2802; var GL_TEXTURE_WRAP_T = 0x2803; var GL_REPEAT = 0x2901; var GL_CLAMP_TO_EDGE = 0x812F; var GL_MIRRORED_REPEAT = 0x8370; var GL_TEXTURE_MAG_FILTER = 0x2800; var GL_TEXTURE_MIN_FILTER = 0x2801; var GL_NEAREST = 0x2600; var GL_LINEAR = 0x2601; var GL_NEAREST_MIPMAP_NEAREST = 0x2700; var GL_LINEAR_MIPMAP_NEAREST = 0x2701; var GL_NEAREST_MIPMAP_LINEAR = 0x2702; var GL_LINEAR_MIPMAP_LINEAR = 0x2703; var GL_GENERATE_MIPMAP_HINT = 0x8192; var GL_DONT_CARE = 0x1100; var GL_FASTEST = 0x1101; var GL_NICEST = 0x1102; var GL_TEXTURE_MAX_ANISOTROPY_EXT = 0x84FE; var GL_UNPACK_ALIGNMENT = 0x0CF5; var GL_UNPACK_FLIP_Y_WEBGL = 0x9240; var GL_UNPACK_PREMULTIPLY_ALPHA_WEBGL = 0x9241; var GL_UNPACK_COLORSPACE_CONVERSION_WEBGL = 0x9243; var GL_BROWSER_DEFAULT_WEBGL = 0x9244; var GL_TEXTURE0$1 = 0x84C0; var MIPMAP_FILTERS = [ GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST_MIPMAP_LINEAR, GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_LINEAR ]; var CHANNELS_FORMAT = [ 0, GL_LUMINANCE, GL_LUMINANCE_ALPHA, GL_RGB, GL_RGBA$1 ]; var FORMAT_CHANNELS = {}; FORMAT_CHANNELS[GL_LUMINANCE] = FORMAT_CHANNELS[GL_ALPHA] = FORMAT_CHANNELS[GL_DEPTH_COMPONENT] = 1; FORMAT_CHANNELS[GL_DEPTH_STENCIL] = FORMAT_CHANNELS[GL_LUMINANCE_ALPHA] = 2; FORMAT_CHANNELS[GL_RGB] = FORMAT_CHANNELS[GL_SRGB_EXT] = 3; FORMAT_CHANNELS[GL_RGBA$1] = FORMAT_CHANNELS[GL_SRGB_ALPHA_EXT] = 4; function objectName (str) { return '[object ' + str + ']' } var CANVAS_CLASS = objectName('HTMLCanvasElement'); var CONTEXT2D_CLASS = objectName('CanvasRenderingContext2D'); var BITMAP_CLASS = objectName('ImageBitmap'); var IMAGE_CLASS = objectName('HTMLImageElement'); var VIDEO_CLASS = objectName('HTMLVideoElement'); var PIXEL_CLASSES = Object.keys(arrayTypes).concat([ CANVAS_CLASS, CONTEXT2D_CLASS, BITMAP_CLASS, IMAGE_CLASS, VIDEO_CLASS ]); // for every texture type, store // the size in bytes. var TYPE_SIZES = []; TYPE_SIZES[GL_UNSIGNED_BYTE$4] = 1; TYPE_SIZES[GL_FLOAT$3] = 4; TYPE_SIZES[GL_HALF_FLOAT_OES] = 2; TYPE_SIZES[GL_UNSIGNED_SHORT$2] = 2; TYPE_SIZES[GL_UNSIGNED_INT$2] = 4; var FORMAT_SIZES_SPECIAL = []; FORMAT_SIZES_SPECIAL[GL_RGBA4] = 2; FORMAT_SIZES_SPECIAL[GL_RGB5_A1] = 2; FORMAT_SIZES_SPECIAL[GL_RGB565] = 2; FORMAT_SIZES_SPECIAL[GL_DEPTH_STENCIL] = 4; FORMAT_SIZES_SPECIAL[GL_COMPRESSED_RGB_S3TC_DXT1_EXT] = 0.5; FORMAT_SIZES_SPECIAL[GL_COMPRESSED_RGBA_S3TC_DXT1_EXT] = 0.5; FORMAT_SIZES_SPECIAL[GL_COMPRESSED_RGBA_S3TC_DXT3_EXT] = 1; FORMAT_SIZES_SPECIAL[GL_COMPRESSED_RGBA_S3TC_DXT5_EXT] = 1; FORMAT_SIZES_SPECIAL[GL_COMPRESSED_RGB_ATC_WEBGL] = 0.5; FORMAT_SIZES_SPECIAL[GL_COMPRESSED_RGBA_ATC_EXPLICIT_ALPHA_WEBGL] = 1; FORMAT_SIZES_SPECIAL[GL_COMPRESSED_RGBA_ATC_INTERPOLATED_ALPHA_WEBGL] = 1; FORMAT_SIZES_SPECIAL[GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG] = 0.5; FORMAT_SIZES_SPECIAL[GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG] = 0.25; FORMAT_SIZES_SPECIAL[GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG] = 0.5; FORMAT_SIZES_SPECIAL[GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG] = 0.25; FORMAT_SIZES_SPECIAL[GL_COMPRESSED_RGB_ETC1_WEBGL] = 0.5; function isNumericArray (arr) { return ( Array.isArray(arr) && (arr.length === 0 || typeof arr[0] === 'number')) } function isRectArray (arr) { if (!Array.isArray(arr)) { return false } var width = arr.length; if (width === 0 || !isArrayLike(arr[0])) { return false } return true } function classString (x) { return Object.prototype.toString.call(x) } function isCanvasElement (object) { return classString(object) === CANVAS_CLASS } function isContext2D (object) { return classString(object) === CONTEXT2D_CLASS } function isBitmap (object) { return classString(object) === BITMAP_CLASS } function isImageElement (object) { return classString(object) === IMAGE_CLASS } function isVideoElement (object) { return classString(object) === VIDEO_CLASS } function isPixelData (object) { if (!object) { return false } var className = classString(object); if (PIXEL_CLASSES.indexOf(className) >= 0) { return true } return ( isNumericArray(object) || isRectArray(object) || isNDArrayLike(object)) } function typedArrayCode$1 (data) { return arrayTypes[Object.prototype.toString.call(data)] | 0 } function convertData (result, data) { var n = data.length; switch (result.type) { case GL_UNSIGNED_BYTE$4: case GL_UNSIGNED_SHORT$2: case GL_UNSIGNED_INT$2: case GL_FLOAT$3: var converted = pool.allocType(result.type, n); converted.set(data); result.data = converted; break case GL_HALF_FLOAT_OES: result.data = convertToHalfFloat(data); break default: } } function preConvert (image, n) { return pool.allocType( image.type === GL_HALF_FLOAT_OES ? GL_FLOAT$3 : image.type, n) } function postConvert (image, data) { if (image.type === GL_HALF_FLOAT_OES) { image.data = convertToHalfFloat(data); pool.freeType(data); } else { image.data = data; } } function transposeData (image, array, strideX, strideY, strideC, offset) { var w = image.width; var h = image.height; var c = image.channels; var n = w * h * c; var data = preConvert(image, n); var p = 0; for (var i = 0; i < h; ++i) { for (var j = 0; j < w; ++j) { for (var k = 0; k < c; ++k) { data[p++] = array[strideX * j + strideY * i + strideC * k + offset]; } } } postConvert(image, data); } function getTextureSize (format, type, width, height, isMipmap, isCube) { var s; if (typeof FORMAT_SIZES_SPECIAL[format] !== 'undefined') { // we have a special array for dealing with weird color formats such as RGB5A1 s = FORMAT_SIZES_SPECIAL[format]; } else { s = FORMAT_CHANNELS[format] * TYPE_SIZES[type]; } if (isCube) { s *= 6; } if (isMipmap) { // compute the total size of all the mipmaps. var total = 0; var w = width; while (w >= 1) { // we can only use mipmaps on a square image, // so we can simply use the width and ignore the height: total += s * w * w; w /= 2; } return total } else { return s * width * height } } function createTextureSet ( gl, extensions, limits, reglPoll, contextState, stats, config) { // ------------------------------------------------------- // Initialize constants and parameter tables here // ------------------------------------------------------- var mipmapHint = { "don't care": GL_DONT_CARE, 'dont care': GL_DONT_CARE, 'nice': GL_NICEST, 'fast': GL_FASTEST }; var wrapModes = { 'repeat': GL_REPEAT, 'clamp': GL_CLAMP_TO_EDGE, 'mirror': GL_MIRRORED_REPEAT }; var magFilters = { 'nearest': GL_NEAREST, 'linear': GL_LINEAR }; var minFilters = extend({ 'mipmap': GL_LINEAR_MIPMAP_LINEAR, 'nearest mipmap nearest': GL_NEAREST_MIPMAP_NEAREST, 'linear mipmap nearest': GL_LINEAR_MIPMAP_NEAREST, 'nearest mipmap linear': GL_NEAREST_MIPMAP_LINEAR, 'linear mipmap linear': GL_LINEAR_MIPMAP_LINEAR }, magFilters); var colorSpace = { 'none': 0, 'browser': GL_BROWSER_DEFAULT_WEBGL }; var textureTypes = { 'uint8': GL_UNSIGNED_BYTE$4, 'rgba4': GL_UNSIGNED_SHORT_4_4_4_4, 'rgb565': GL_UNSIGNED_SHORT_5_6_5, 'rgb5 a1': GL_UNSIGNED_SHORT_5_5_5_1 }; var textureFormats = { 'alpha': GL_ALPHA, 'luminance': GL_LUMINANCE, 'luminance alpha': GL_LUMINANCE_ALPHA, 'rgb': GL_RGB, 'rgba': GL_RGBA$1, 'rgba4': GL_RGBA4, 'rgb5 a1': GL_RGB5_A1, 'rgb565': GL_RGB565 }; var compressedTextureFormats = {}; if (extensions.ext_srgb) { textureFormats.srgb = GL_SRGB_EXT; textureFormats.srgba = GL_SRGB_ALPHA_EXT; } if (extensions.oes_texture_float) { textureTypes.float32 = textureTypes.float = GL_FLOAT$3; } if (extensions.oes_texture_half_float) { textureTypes['float16'] = textureTypes['half float'] = GL_HALF_FLOAT_OES; } if (extensions.webgl_depth_texture) { extend(textureFormats, { 'depth': GL_DEPTH_COMPONENT, 'depth stencil': GL_DEPTH_STENCIL }); extend(textureTypes, { 'uint16': GL_UNSIGNED_SHORT$2, 'uint32': GL_UNSIGNED_INT$2, 'depth stencil': GL_UNSIGNED_INT_24_8_WEBGL }); } if (extensions.webgl_compressed_texture_s3tc) { extend(compressedTextureFormats, { 'rgb s3tc dxt1': GL_COMPRESSED_RGB_S3TC_DXT1_EXT, 'rgba s3tc dxt1': GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, 'rgba s3tc dxt3': GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, 'rgba s3tc dxt5': GL_COMPRESSED_RGBA_S3TC_DXT5_EXT }); } if (extensions.webgl_compressed_texture_atc) { extend(compressedTextureFormats, { 'rgb atc': GL_COMPRESSED_RGB_ATC_WEBGL, 'rgba atc explicit alpha': GL_COMPRESSED_RGBA_ATC_EXPLICIT_ALPHA_WEBGL, 'rgba atc interpolated alpha': GL_COMPRESSED_RGBA_ATC_INTERPOLATED_ALPHA_WEBGL }); } if (extensions.webgl_compressed_texture_pvrtc) { extend(compressedTextureFormats, { 'rgb pvrtc 4bppv1': GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG, 'rgb pvrtc 2bppv1': GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG, 'rgba pvrtc 4bppv1': GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG, 'rgba pvrtc 2bppv1': GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG }); } if (extensions.webgl_compressed_texture_etc1) { compressedTextureFormats['rgb etc1'] = GL_COMPRESSED_RGB_ETC1_WEBGL; } // Copy over all texture formats var supportedCompressedFormats = Array.prototype.slice.call( gl.getParameter(GL_COMPRESSED_TEXTURE_FORMATS)); Object.keys(compressedTextureFormats).forEach(function (name) { var format = compressedTextureFormats[name]; if (supportedCompressedFormats.indexOf(format) >= 0) { textureFormats[name] = format; } }); var supportedFormats = Object.keys(textureFormats); limits.textureFormats = supportedFormats; // associate with every format string its // corresponding GL-value. var textureFormatsInvert = []; Object.keys(textureFormats).forEach(function (key) { var val = textureFormats[key]; textureFormatsInvert[val] = key; }); // associate with every type string its // corresponding GL-value. var textureTypesInvert = []; Object.keys(textureTypes).forEach(function (key) { var val = textureTypes[key]; textureTypesInvert[val] = key; }); var magFiltersInvert = []; Object.keys(magFilters).forEach(function (key) { var val = magFilters[key]; magFiltersInvert[val] = key; }); var minFiltersInvert = []; Object.keys(minFilters).forEach(function (key) { var val = minFilters[key]; minFiltersInvert[val] = key; }); var wrapModesInvert = []; Object.keys(wrapModes).forEach(function (key) { var val = wrapModes[key]; wrapModesInvert[val] = key; }); // colorFormats[] gives the format (channels) associated to an // internalformat var colorFormats = supportedFormats.reduce(function (color, key) { var glenum = textureFormats[key]; if (glenum === GL_LUMINANCE || glenum === GL_ALPHA || glenum === GL_LUMINANCE || glenum === GL_LUMINANCE_ALPHA || glenum === GL_DEPTH_COMPONENT || glenum === GL_DEPTH_STENCIL) { color[glenum] = glenum; } else if (glenum === GL_RGB5_A1 || key.indexOf('rgba') >= 0) { color[glenum] = GL_RGBA$1; } else { color[glenum] = GL_RGB; } return color }, {}); function TexFlags () { // format info this.internalformat = GL_RGBA$1; this.format = GL_RGBA$1; this.type = GL_UNSIGNED_BYTE$4; this.compressed = false; // pixel storage this.premultiplyAlpha = false; this.flipY = false; this.unpackAlignment = 1; this.colorSpace = GL_BROWSER_DEFAULT_WEBGL; // shape info this.width = 0; this.height = 0; this.channels = 0; } function copyFlags (result, other) { result.internalformat = other.internalformat; result.format = other.format; result.type = other.type; result.compressed = other.compressed; result.premultiplyAlpha = other.premultiplyAlpha; result.flipY = other.flipY; result.unpackAlignment = other.unpackAlignment; result.colorSpace = other.colorSpace; result.width = other.width; result.height = other.height; result.channels = other.channels; } function parseFlags (flags, options) { if (typeof options !== 'object' || !options) { return } if ('premultiplyAlpha' in options) { flags.premultiplyAlpha = options.premultiplyAlpha; } if ('flipY' in options) { flags.flipY = options.flipY; } if ('alignment' in options) { flags.unpackAlignment = options.alignment; } if ('colorSpace' in options) { flags.colorSpace = colorSpace[options.colorSpace]; } if ('type' in options) { var type = options.type; flags.type = textureTypes[type]; } var w = flags.width; var h = flags.height; var c = flags.channels; var hasChannels = false; if ('shape' in options) { w = options.shape[0]; h = options.shape[1]; if (options.shape.length === 3) { c = options.shape[2]; hasChannels = true; } } else { if ('radius' in options) { w = h = options.radius; } if ('width' in options) { w = options.width; } if ('height' in options) { h = options.height; } if ('channels' in options) { c = options.channels; hasChannels = true; } } flags.width = w | 0; flags.height = h | 0; flags.channels = c | 0; var hasFormat = false; if ('format' in options) { var formatStr = options.format; var internalformat = flags.internalformat = textureFormats[formatStr]; flags.format = colorFormats[internalformat]; if (formatStr in textureTypes) { if (!('type' in options)) { flags.type = textureTypes[formatStr]; } } if (formatStr in compressedTextureFormats) { flags.compressed = true; } hasFormat = true; } // Reconcile channels and format if (!hasChannels && hasFormat) { flags.channels = FORMAT_CHANNELS[flags.format]; } else if (hasChannels && !hasFormat) { if (flags.channels !== CHANNELS_FORMAT[flags.format]) { flags.format = flags.internalformat = CHANNELS_FORMAT[flags.channels]; } } else if (hasFormat && hasChannels) { } } function setFlags (flags) { gl.pixelStorei(GL_UNPACK_FLIP_Y_WEBGL, flags.flipY); gl.pixelStorei(GL_UNPACK_PREMULTIPLY_ALPHA_WEBGL, flags.premultiplyAlpha); gl.pixelStorei(GL_UNPACK_COLORSPACE_CONVERSION_WEBGL, flags.colorSpace); gl.pixelStorei(GL_UNPACK_ALIGNMENT, flags.unpackAlignment); } // ------------------------------------------------------- // Tex image data // ------------------------------------------------------- function TexImage () { TexFlags.call(this); this.xOffset = 0; this.yOffset = 0; // data this.data = null; this.needsFree = false; // html element this.element = null; // copyTexImage info this.needsCopy = false; } function parseImage (image, options) { var data = null; if (isPixelData(options)) { data = options; } else if (options) { parseFlags(image, options); if ('x' in options) { image.xOffset = options.x | 0; } if ('y' in options) { image.yOffset = options.y | 0; } if (isPixelData(options.data)) { data = options.data; } } if (options.copy) { var viewW = contextState.viewportWidth; var viewH = contextState.viewportHeight; image.width = image.width || (viewW - image.xOffset); image.height = image.height || (viewH - image.yOffset); image.needsCopy = true; } else if (!data) { image.width = image.width || 1; image.height = image.height || 1; image.channels = image.channels || 4; } else if (isTypedArray(data)) { image.channels = image.channels || 4; image.data = data; if (!('type' in options) && image.type === GL_UNSIGNED_BYTE$4) { image.type = typedArrayCode$1(data); } } else if (isNumericArray(data)) { image.channels = image.channels || 4; convertData(image, data); image.alignment = 1; image.needsFree = true; } else if (isNDArrayLike(data)) { var array = data.data; if (!Array.isArray(array) && image.type === GL_UNSIGNED_BYTE$4) { image.type = typedArrayCode$1(array); } var shape = data.shape; var stride = data.stride; var shapeX, shapeY, shapeC, strideX, strideY, strideC; if (shape.length === 3) { shapeC = shape[2]; strideC = stride[2]; } else { shapeC = 1; strideC = 1; } shapeX = shape[0]; shapeY = shape[1]; strideX = stride[0]; strideY = stride[1]; image.alignment = 1; image.width = shapeX; image.height = shapeY; image.channels = shapeC; image.format = image.internalformat = CHANNELS_FORMAT[shapeC]; image.needsFree = true; transposeData(image, array, strideX, strideY, strideC, data.offset); } else if (isCanvasElement(data) || isContext2D(data)) { if (isCanvasElement(data)) { image.element = data; } else { image.element = data.canvas; } image.width = image.element.width; image.height = image.element.height; image.channels = 4; } else if (isBitmap(data)) { image.element = data; image.width = data.width; image.height = data.height; image.channels = 4; } else if (isImageElement(data)) { image.element = data; image.width = data.naturalWidth; image.height = data.naturalHeight; image.channels = 4; } else if (isVideoElement(data)) { image.element = data; image.width = data.videoWidth; image.height = data.videoHeight; image.channels = 4; } else if (isRectArray(data)) { var w = image.width || data[0].length; var h = image.height || data.length; var c = image.channels; if (isArrayLike(data[0][0])) { c = c || data[0][0].length; } else { c = c || 1; } var arrayShape = flattenUtils.shape(data); var n = 1; for (var dd = 0; dd < arrayShape.length; ++dd) { n *= arrayShape[dd]; } var allocData = preConvert(image, n); flattenUtils.flatten(data, arrayShape, '', allocData); postConvert(image, allocData); image.alignment = 1; image.width = w; image.height = h; image.channels = c; image.format = image.internalformat = CHANNELS_FORMAT[c]; image.needsFree = true; } if (image.type === GL_FLOAT$3) { } else if (image.type === GL_HALF_FLOAT_OES) { } // do compressed texture validation here. } function setImage (info, target, miplevel) { var element = info.element; var data = info.data; var internalformat = info.internalformat; var format = info.format; var type = info.type; var width = info.width; var height = info.height; var channels = info.channels; setFlags(info); if (element) { gl.texImage2D(target, miplevel, format, format, type, element); } else if (info.compressed) { gl.compressedTexImage2D(target, miplevel, internalformat, width, height, 0, data); } else if (info.needsCopy) { reglPoll(); gl.copyTexImage2D( target, miplevel, format, info.xOffset, info.yOffset, width, height, 0); } else { var nullData = !data; if (nullData) { data = pool.zero.allocType(type, width * height * channels); } gl.texImage2D(target, miplevel, format, width, height, 0, format, type, data); if (nullData && data) { pool.zero.freeType(data); } } } function setSubImage (info, target, x, y, miplevel) { var element = info.element; var data = info.data; var internalformat = info.internalformat; var format = info.format; var type = info.type; var width = info.width; var height = info.height; setFlags(info); if (element) { gl.texSubImage2D( target, miplevel, x, y, format, type, element); } else if (info.compressed) { gl.compressedTexSubImage2D( target, miplevel, x, y, internalformat, width, height, data); } else if (info.needsCopy) { reglPoll(); gl.copyTexSubImage2D( target, miplevel, x, y, info.xOffset, info.yOffset, width, height); } else { gl.texSubImage2D( target, miplevel, x, y, width, height, format, type, data); } } // texImage pool var imagePool = []; function allocImage () { return imagePool.pop() || new TexImage() } function freeImage (image) { if (image.needsFree) { pool.freeType(image.data); } TexImage.call(image); imagePool.push(image); } // ------------------------------------------------------- // Mip map // ------------------------------------------------------- function MipMap () { TexFlags.call(this); this.genMipmaps = false; this.mipmapHint = GL_DONT_CARE; this.mipmask = 0; this.images = Array(16); } function parseMipMapFromShape (mipmap, width, height) { var img = mipmap.images[0] = allocImage(); mipmap.mipmask = 1; img.width = mipmap.width = width; img.height = mipmap.height = height; img.channels = mipmap.channels = 4; } function parseMipMapFromObject (mipmap, options) { var imgData = null; if (isPixelData(options)) { imgData = mipmap.images[0] = allocImage(); copyFlags(imgData, mipmap); parseImage(imgData, options); mipmap.mipmask = 1; } else { parseFlags(mipmap, options); if (Array.isArray(options.mipmap)) { var mipData = options.mipmap; for (var i = 0; i < mipData.length; ++i) { imgData = mipmap.images[i] = allocImage(); copyFlags(imgData, mipmap); imgData.width >>= i; imgData.height >>= i; parseImage(imgData, mipData[i]); mipmap.mipmask |= (1 << i); } } else { imgData = mipmap.images[0] = allocImage(); copyFlags(imgData, mipmap); parseImage(imgData, options); mipmap.mipmask = 1; } } copyFlags(mipmap, mipmap.images[0]); // For textures of the compressed format WEBGL_compressed_texture_s3tc // we must have that // // "When level equals zero width and height must be a multiple of 4. // When level is greater than 0 width and height must be 0, 1, 2 or a multiple of 4. " // // but we do not yet support having multiple mipmap levels for compressed textures, // so we only test for level zero. if (mipmap.compressed && (mipmap.internalformat === GL_COMPRESSED_RGB_S3TC_DXT1_EXT) || (mipmap.internalformat === GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) || (mipmap.internalformat === GL_COMPRESSED_RGBA_S3TC_DXT3_EXT) || (mipmap.internalformat === GL_COMPRESSED_RGBA_S3TC_DXT5_EXT)) { } } function setMipMap (mipmap, target) { var images = mipmap.images; for (var i = 0; i < images.length; ++i) { if (!images[i]) { return } setImage(images[i], target, i); } } var mipPool = []; function allocMipMap () { var result = mipPool.pop() || new MipMap(); TexFlags.call(result); result.mipmask = 0; for (var i = 0; i < 16; ++i) { result.images[i] = null; } return result } function freeMipMap (mipmap) { var images = mipmap.images; for (var i = 0; i < images.length; ++i) { if (images[i]) { freeImage(images[i]); } images[i] = null; } mipPool.push(mipmap); } // ------------------------------------------------------- // Tex info // ------------------------------------------------------- function TexInfo () { this.minFilter = GL_NEAREST; this.magFilter = GL_NEAREST; this.wrapS = GL_CLAMP_TO_EDGE; this.wrapT = GL_CLAMP_TO_EDGE; this.anisotropic = 1; this.genMipmaps = false; this.mipmapHint = GL_DONT_CARE; } function parseTexInfo (info, options) { if ('min' in options) { var minFilter = options.min; info.minFilter = minFilters[minFilter]; if (MIPMAP_FILTERS.indexOf(info.minFilter) >= 0 && !('faces' in options)) { info.genMipmaps = true; } } if ('mag' in options) { var magFilter = options.mag; info.magFilter = magFilters[magFilter]; } var wrapS = info.wrapS; var wrapT = info.wrapT; if ('wrap' in options) { var wrap = options.wrap; if (typeof wrap === 'string') { wrapS = wrapT = wrapModes[wrap]; } else if (Array.isArray(wrap)) { wrapS = wrapModes[wrap[0]]; wrapT = wrapModes[wrap[1]]; } } else { if ('wrapS' in options) { var optWrapS = options.wrapS; wrapS = wrapModes[optWrapS]; } if ('wrapT' in options) { var optWrapT = options.wrapT; wrapT = wrapModes[optWrapT]; } } info.wrapS = wrapS; info.wrapT = wrapT; if ('anisotropic' in options) { var anisotropic = options.anisotropic; info.anisotropic = options.anisotropic; } if ('mipmap' in options) { var hasMipMap = false; switch (typeof options.mipmap) { case 'string': info.mipmapHint = mipmapHint[options.mipmap]; info.genMipmaps = true; hasMipMap = true; break case 'boolean': hasMipMap = info.genMipmaps = options.mipmap; break case 'object': info.genMipmaps = false; hasMipMap = true; break default: } if (hasMipMap && !('min' in options)) { info.minFilter = GL_NEAREST_MIPMAP_NEAREST; } } } function setTexInfo (info, target) { gl.texParameteri(target, GL_TEXTURE_MIN_FILTER, info.minFilter); gl.texParameteri(target, GL_TEXTURE_MAG_FILTER, info.magFilter); gl.texParameteri(target, GL_TEXTURE_WRAP_S, info.wrapS); gl.texParameteri(target, GL_TEXTURE_WRAP_T, info.wrapT); if (extensions.ext_texture_filter_anisotropic) { gl.texParameteri(target, GL_TEXTURE_MAX_ANISOTROPY_EXT, info.anisotropic); } if (info.genMipmaps) { gl.hint(GL_GENERATE_MIPMAP_HINT, info.mipmapHint); gl.generateMipmap(target); } } // ------------------------------------------------------- // Full texture object // ------------------------------------------------------- var textureCount = 0; var textureSet = {}; var numTexUnits = limits.maxTextureUnits; var textureUnits = Array(numTexUnits).map(function () { return null }); function REGLTexture (target) { TexFlags.call(this); this.mipmask = 0; this.internalformat = GL_RGBA$1; this.id = textureCount++; this.refCount = 1; this.target = target; this.texture = gl.createTexture(); this.unit = -1; this.bindCount = 0; this.texInfo = new TexInfo(); if (config.profile) { this.stats = {size: 0}; } } function tempBind (texture) { gl.activeTexture(GL_TEXTURE0$1); gl.bindTexture(texture.target, texture.texture); } function tempRestore () { var prev = textureUnits[0]; if (prev) { gl.bindTexture(prev.target, prev.texture); } else { gl.bindTexture(GL_TEXTURE_2D$1, null); } } function destroy (texture) { var handle = texture.texture; var unit = texture.unit; var target = texture.target; if (unit >= 0) { gl.activeTexture(GL_TEXTURE0$1 + unit); gl.bindTexture(target, null); textureUnits[unit] = null; } gl.deleteTexture(handle); texture.texture = null; texture.params = null; texture.pixels = null; texture.refCount = 0; delete textureSet[texture.id]; stats.textureCount--; } extend(REGLTexture.prototype, { bind: function () { var texture = this; texture.bindCount += 1; var unit = texture.unit; if (unit < 0) { for (var i = 0; i < numTexUnits; ++i) { var other = textureUnits[i]; if (other) { if (other.bindCount > 0) { continue } other.unit = -1; } textureUnits[i] = texture; unit = i; break } if (unit >= numTexUnits) { } if (config.profile && stats.maxTextureUnits < (unit + 1)) { stats.maxTextureUnits = unit + 1; // +1, since the units are zero-based } texture.unit = unit; gl.activeTexture(GL_TEXTURE0$1 + unit); gl.bindTexture(texture.target, texture.texture); } return unit }, unbind: function () { this.bindCount -= 1; }, decRef: function () { if (--this.refCount <= 0) { destroy(this); } } }); function createTexture2D (a, b) { var texture = new REGLTexture(GL_TEXTURE_2D$1); textureSet[texture.id] = texture; stats.textureCount++; function reglTexture2D (a, b) { var texInfo = texture.texInfo; TexInfo.call(texInfo); var mipData = allocMipMap(); if (typeof a === 'number') { if (typeof b === 'number') { parseMipMapFromShape(mipData, a | 0, b | 0); } else { parseMipMapFromShape(mipData, a | 0, a | 0); } } else if (a) { parseTexInfo(texInfo, a); parseMipMapFromObject(mipData, a); } else { // empty textures get assigned a default shape of 1x1 parseMipMapFromShape(mipData, 1, 1); } if (texInfo.genMipmaps) { mipData.mipmask = (mipData.width << 1) - 1; } texture.mipmask = mipData.mipmask; copyFlags(texture, mipData); texture.internalformat = mipData.internalformat; reglTexture2D.width = mipData.width; reglTexture2D.height = mipData.height; tempBind(texture); setMipMap(mipData, GL_TEXTURE_2D$1); setTexInfo(texInfo, GL_TEXTURE_2D$1); tempRestore(); freeMipMap(mipData); if (config.profile) { texture.stats.size = getTextureSize( texture.internalformat, texture.type, mipData.width, mipData.height, texInfo.genMipmaps, false); } reglTexture2D.format = textureFormatsInvert[texture.internalformat]; reglTexture2D.type = textureTypesInvert[texture.type]; reglTexture2D.mag = magFiltersInvert[texInfo.magFilter]; reglTexture2D.min = minFiltersInvert[texInfo.minFilter]; reglTexture2D.wrapS = wrapModesInvert[texInfo.wrapS]; reglTexture2D.wrapT = wrapModesInvert[texInfo.wrapT]; return reglTexture2D } function subimage (image, x_, y_, level_) { var x = x_ | 0; var y = y_ | 0; var level = level_ | 0; var imageData = allocImage(); copyFlags(imageData, texture); imageData.width = 0; imageData.height = 0; parseImage(imageData, image); imageData.width = imageData.width || ((texture.width >> level) - x); imageData.height = imageData.height || ((texture.height >> level) - y); tempBind(texture); setSubImage(imageData, GL_TEXTURE_2D$1, x, y, level); tempRestore(); freeImage(imageData); return reglTexture2D } function resize (w_, h_) { var w = w_ | 0; var h = (h_ | 0) || w; if (w === texture.width && h === texture.height) { return reglTexture2D } reglTexture2D.width = texture.width = w; reglTexture2D.height = texture.height = h; tempBind(texture); var data; var channels = texture.channels; var type = texture.type; for (var i = 0; texture.mipmask >> i; ++i) { var _w = w >> i; var _h = h >> i; if (!_w || !_h) break data = pool.zero.allocType(type, _w * _h * channels); gl.texImage2D( GL_TEXTURE_2D$1, i, texture.format, _w, _h, 0, texture.format, texture.type, data); if (data) pool.zero.freeType(data); } tempRestore(); // also, recompute the texture size. if (config.profile) { texture.stats.size = getTextureSize( texture.internalformat, texture.type, w, h, false, false); } return reglTexture2D } reglTexture2D(a, b); reglTexture2D.subimage = subimage; reglTexture2D.resize = resize; reglTexture2D._reglType = 'texture2d'; reglTexture2D._texture = texture; if (config.profile) { reglTexture2D.stats = texture.stats; } reglTexture2D.destroy = function () { texture.decRef(); }; return reglTexture2D } function createTextureCube (a0, a1, a2, a3, a4, a5) { var texture = new REGLTexture(GL_TEXTURE_CUBE_MAP$1); textureSet[texture.id] = texture; stats.cubeCount++; var faces = new Array(6); function reglTextureCube (a0, a1, a2, a3, a4, a5) { var i; var texInfo = texture.texInfo; TexInfo.call(texInfo); for (i = 0; i < 6; ++i) { faces[i] = allocMipMap(); } if (typeof a0 === 'number' || !a0) { var s = (a0 | 0) || 1; for (i = 0; i < 6; ++i) { parseMipMapFromShape(faces[i], s, s); } } else if (typeof a0 === 'object') { if (a1) { parseMipMapFromObject(faces[0], a0); parseMipMapFromObject(faces[1], a1); parseMipMapFromObject(faces[2], a2); parseMipMapFromObject(faces[3], a3); parseMipMapFromObject(faces[4], a4); parseMipMapFromObject(faces[5], a5); } else { parseTexInfo(texInfo, a0); parseFlags(texture, a0); if ('faces' in a0) { var face_input = a0.faces; for (i = 0; i < 6; ++i) { copyFlags(faces[i], texture); parseMipMapFromObject(faces[i], face_input[i]); } } else { for (i = 0; i < 6; ++i) { parseMipMapFromObject(faces[i], a0); } } } } else { } copyFlags(texture, faces[0]); if (!limits.npotTextureCube) { } if (texInfo.genMipmaps) { texture.mipmask = (faces[0].width << 1) - 1; } else { texture.mipmask = faces[0].mipmask; } texture.internalformat = faces[0].internalformat; reglTextureCube.width = faces[0].width; reglTextureCube.height = faces[0].height; tempBind(texture); for (i = 0; i < 6; ++i) { setMipMap(faces[i], GL_TEXTURE_CUBE_MAP_POSITIVE_X$1 + i); } setTexInfo(texInfo, GL_TEXTURE_CUBE_MAP$1); tempRestore(); if (config.profile) { texture.stats.size = getTextureSize( texture.internalformat, texture.type, reglTextureCube.width, reglTextureCube.height, texInfo.genMipmaps, true); } reglTextureCube.format = textureFormatsInvert[texture.internalformat]; reglTextureCube.type = textureTypesInvert[texture.type]; reglTextureCube.mag = magFiltersInvert[texInfo.magFilter]; reglTextureCube.min = minFiltersInvert[texInfo.minFilter]; reglTextureCube.wrapS = wrapModesInvert[texInfo.wrapS]; reglTextureCube.wrapT = wrapModesInvert[texInfo.wrapT]; for (i = 0; i < 6; ++i) { freeMipMap(faces[i]); } return reglTextureCube } function subimage (face, image, x_, y_, level_) { var x = x_ | 0; var y = y_ | 0; var level = level_ | 0; var imageData = allocImage(); copyFlags(imageData, texture); imageData.width = 0; imageData.height = 0; parseImage(imageData, image); imageData.width = imageData.width || ((texture.width >> level) - x); imageData.height = imageData.height || ((texture.height >> level) - y); tempBind(texture); setSubImage(imageData, GL_TEXTURE_CUBE_MAP_POSITIVE_X$1 + face, x, y, level); tempRestore(); freeImage(imageData); return reglTextureCube } function resize (radius_) { var radius = radius_ | 0; if (radius === texture.width) { return } reglTextureCube.width = texture.width = radius; reglTextureCube.height = texture.height = radius; tempBind(texture); for (var i = 0; i < 6; ++i) { for (var j = 0; texture.mipmask >> j; ++j) { gl.texImage2D( GL_TEXTURE_CUBE_MAP_POSITIVE_X$1 + i, j, texture.format, radius >> j, radius >> j, 0, texture.format, texture.type, null); } } tempRestore(); if (config.profile) { texture.stats.size = getTextureSize( texture.internalformat, texture.type, reglTextureCube.width, reglTextureCube.height, false, true); } return reglTextureCube } reglTextureCube(a0, a1, a2, a3, a4, a5); reglTextureCube.subimage = subimage; reglTextureCube.resize = resize; reglTextureCube._reglType = 'textureCube'; reglTextureCube._texture = texture; if (config.profile) { reglTextureCube.stats = texture.stats; } reglTextureCube.destroy = function () { texture.decRef(); }; return reglTextureCube } // Called when regl is destroyed function destroyTextures () { for (var i = 0; i < numTexUnits; ++i) { gl.activeTexture(GL_TEXTURE0$1 + i); gl.bindTexture(GL_TEXTURE_2D$1, null); textureUnits[i] = null; } values(textureSet).forEach(destroy); stats.cubeCount = 0; stats.textureCount = 0; } if (config.profile) { stats.getTotalTextureSize = function () { var total = 0; Object.keys(textureSet).forEach(function (key) { total += textureSet[key].stats.size; }); return total }; } function restoreTextures () { for (var i = 0; i < numTexUnits; ++i) { var tex = textureUnits[i]; if (tex) { tex.bindCount = 0; tex.unit = -1; textureUnits[i] = null; } } values(textureSet).forEach(function (texture) { texture.texture = gl.createTexture(); gl.bindTexture(texture.target, texture.texture); for (var i = 0; i < 32; ++i) { if ((texture.mipmask & (1 << i)) === 0) { continue } if (texture.target === GL_TEXTURE_2D$1) { gl.texImage2D(GL_TEXTURE_2D$1, i, texture.internalformat, texture.width >> i, texture.height >> i, 0, texture.internalformat, texture.type, null); } else { for (var j = 0; j < 6; ++j) { gl.texImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X$1 + j, i, texture.internalformat, texture.width >> i, texture.height >> i, 0, texture.internalformat, texture.type, null); } } } setTexInfo(texture.texInfo, texture.target); }); } return { create2D: createTexture2D, createCube: createTextureCube, clear: destroyTextures, getTexture: function (wrapper) { return null }, restore: restoreTextures } } var GL_RENDERBUFFER = 0x8D41; var GL_RGBA4$1 = 0x8056; var GL_RGB5_A1$1 = 0x8057; var GL_RGB565$1 = 0x8D62; var GL_DEPTH_COMPONENT16 = 0x81A5; var GL_STENCIL_INDEX8 = 0x8D48; var GL_DEPTH_STENCIL$1 = 0x84F9; var GL_SRGB8_ALPHA8_EXT = 0x8C43; var GL_RGBA32F_EXT = 0x8814; var GL_RGBA16F_EXT = 0x881A; var GL_RGB16F_EXT = 0x881B; var FORMAT_SIZES = []; FORMAT_SIZES[GL_RGBA4$1] = 2; FORMAT_SIZES[GL_RGB5_A1$1] = 2; FORMAT_SIZES[GL_RGB565$1] = 2; FORMAT_SIZES[GL_DEPTH_COMPONENT16] = 2; FORMAT_SIZES[GL_STENCIL_INDEX8] = 1; FORMAT_SIZES[GL_DEPTH_STENCIL$1] = 4; FORMAT_SIZES[GL_SRGB8_ALPHA8_EXT] = 4; FORMAT_SIZES[GL_RGBA32F_EXT] = 16; FORMAT_SIZES[GL_RGBA16F_EXT] = 8; FORMAT_SIZES[GL_RGB16F_EXT] = 6; function getRenderbufferSize (format, width, height) { return FORMAT_SIZES[format] * width * height } var wrapRenderbuffers = function (gl, extensions, limits, stats, config) { var formatTypes = { 'rgba4': GL_RGBA4$1, 'rgb565': GL_RGB565$1, 'rgb5 a1': GL_RGB5_A1$1, 'depth': GL_DEPTH_COMPONENT16, 'stencil': GL_STENCIL_INDEX8, 'depth stencil': GL_DEPTH_STENCIL$1 }; if (extensions.ext_srgb) { formatTypes['srgba'] = GL_SRGB8_ALPHA8_EXT; } if (extensions.ext_color_buffer_half_float) { formatTypes['rgba16f'] = GL_RGBA16F_EXT; formatTypes['rgb16f'] = GL_RGB16F_EXT; } if (extensions.webgl_color_buffer_float) { formatTypes['rgba32f'] = GL_RGBA32F_EXT; } var formatTypesInvert = []; Object.keys(formatTypes).forEach(function (key) { var val = formatTypes[key]; formatTypesInvert[val] = key; }); var renderbufferCount = 0; var renderbufferSet = {}; function REGLRenderbuffer (renderbuffer) { this.id = renderbufferCount++; this.refCount = 1; this.renderbuffer = renderbuffer; this.format = GL_RGBA4$1; this.width = 0; this.height = 0; if (config.profile) { this.stats = {size: 0}; } } REGLRenderbuffer.prototype.decRef = function () { if (--this.refCount <= 0) { destroy(this); } }; function destroy (rb) { var handle = rb.renderbuffer; gl.bindRenderbuffer(GL_RENDERBUFFER, null); gl.deleteRenderbuffer(handle); rb.renderbuffer = null; rb.refCount = 0; delete renderbufferSet[rb.id]; stats.renderbufferCount--; } function createRenderbuffer (a, b) { var renderbuffer = new REGLRenderbuffer(gl.createRenderbuffer()); renderbufferSet[renderbuffer.id] = renderbuffer; stats.renderbufferCount++; function reglRenderbuffer (a, b) { var w = 0; var h = 0; var format = GL_RGBA4$1; if (typeof a === 'object' && a) { var options = a; if ('shape' in options) { var shape = options.shape; w = shape[0] | 0; h = shape[1] | 0; } else { if ('radius' in options) { w = h = options.radius | 0; } if ('width' in options) { w = options.width | 0; } if ('height' in options) { h = options.height | 0; } } if ('format' in options) { format = formatTypes[options.format]; } } else if (typeof a === 'number') { w = a | 0; if (typeof b === 'number') { h = b | 0; } else { h = w; } } else if (!a) { w = h = 1; } else { } // check shape if (w === renderbuffer.width && h === renderbuffer.height && format === renderbuffer.format) { return } reglRenderbuffer.width = renderbuffer.width = w; reglRenderbuffer.height = renderbuffer.height = h; renderbuffer.format = format; gl.bindRenderbuffer(GL_RENDERBUFFER, renderbuffer.renderbuffer); gl.renderbufferStorage(GL_RENDERBUFFER, format, w, h); if (config.profile) { renderbuffer.stats.size = getRenderbufferSize(renderbuffer.format, renderbuffer.width, renderbuffer.height); } reglRenderbuffer.format = formatTypesInvert[renderbuffer.format]; return reglRenderbuffer } function resize (w_, h_) { var w = w_ | 0; var h = (h_ | 0) || w; if (w === renderbuffer.width && h === renderbuffer.height) { return reglRenderbuffer } // check shape reglRenderbuffer.width = renderbuffer.width = w; reglRenderbuffer.height = renderbuffer.height = h; gl.bindRenderbuffer(GL_RENDERBUFFER, renderbuffer.renderbuffer); gl.renderbufferStorage(GL_RENDERBUFFER, renderbuffer.format, w, h); // also, recompute size. if (config.profile) { renderbuffer.stats.size = getRenderbufferSize( renderbuffer.format, renderbuffer.width, renderbuffer.height); } return reglRenderbuffer } reglRenderbuffer(a, b); reglRenderbuffer.resize = resize; reglRenderbuffer._reglType = 'renderbuffer'; reglRenderbuffer._renderbuffer = renderbuffer; if (config.profile) { reglRenderbuffer.stats = renderbuffer.stats; } reglRenderbuffer.destroy = function () { renderbuffer.decRef(); }; return reglRenderbuffer } if (config.profile) { stats.getTotalRenderbufferSize = function () { var total = 0; Object.keys(renderbufferSet).forEach(function (key) { total += renderbufferSet[key].stats.size; }); return total }; } function restoreRenderbuffers () { values(renderbufferSet).forEach(function (rb) { rb.renderbuffer = gl.createRenderbuffer(); gl.bindRenderbuffer(GL_RENDERBUFFER, rb.renderbuffer); gl.renderbufferStorage(GL_RENDERBUFFER, rb.format, rb.width, rb.height); }); gl.bindRenderbuffer(GL_RENDERBUFFER, null); } return { create: createRenderbuffer, clear: function () { values(renderbufferSet).forEach(destroy); }, restore: restoreRenderbuffers } }; // We store these constants so that the minifier can inline them var GL_FRAMEBUFFER$1 = 0x8D40; var GL_RENDERBUFFER$1 = 0x8D41; var GL_TEXTURE_2D$2 = 0x0DE1; var GL_TEXTURE_CUBE_MAP_POSITIVE_X$2 = 0x8515; var GL_COLOR_ATTACHMENT0$1 = 0x8CE0; var GL_DEPTH_ATTACHMENT = 0x8D00; var GL_STENCIL_ATTACHMENT = 0x8D20; var GL_DEPTH_STENCIL_ATTACHMENT = 0x821A; var GL_FRAMEBUFFER_COMPLETE$1 = 0x8CD5; var GL_HALF_FLOAT_OES$1 = 0x8D61; var GL_UNSIGNED_BYTE$5 = 0x1401; var GL_FLOAT$4 = 0x1406; var GL_RGB$1 = 0x1907; var GL_RGBA$2 = 0x1908; // for every texture format, store // the number of channels var textureFormatChannels = []; textureFormatChannels[GL_RGBA$2] = 4; textureFormatChannels[GL_RGB$1] = 3; // for every texture type, store // the size in bytes. var textureTypeSizes = []; textureTypeSizes[GL_UNSIGNED_BYTE$5] = 1; textureTypeSizes[GL_FLOAT$4] = 4; textureTypeSizes[GL_HALF_FLOAT_OES$1] = 2; function wrapFBOState ( gl, extensions, limits, textureState, renderbufferState, stats) { var framebufferState = { cur: null, next: null, dirty: false, setFBO: null }; var colorTextureFormats = ['rgba']; var colorRenderbufferFormats = ['rgba4', 'rgb565', 'rgb5 a1']; if (extensions.ext_srgb) { colorRenderbufferFormats.push('srgba'); } if (extensions.ext_color_buffer_half_float) { colorRenderbufferFormats.push('rgba16f', 'rgb16f'); } if (extensions.webgl_color_buffer_float) { colorRenderbufferFormats.push('rgba32f'); } var colorTypes = ['uint8']; if (extensions.oes_texture_half_float) { colorTypes.push('half float', 'float16'); } if (extensions.oes_texture_float) { colorTypes.push('float', 'float32'); } function FramebufferAttachment (target, texture, renderbuffer) { this.target = target; this.texture = texture; this.renderbuffer = renderbuffer; var w = 0; var h = 0; if (texture) { w = texture.width; h = texture.height; } else if (renderbuffer) { w = renderbuffer.width; h = renderbuffer.height; } this.width = w; this.height = h; } function decRef (attachment) { if (attachment) { if (attachment.texture) { attachment.texture._texture.decRef(); } if (attachment.renderbuffer) { attachment.renderbuffer._renderbuffer.decRef(); } } } function incRefAndCheckShape (attachment, width, height) { if (!attachment) { return } if (attachment.texture) { var texture = attachment.texture._texture; var tw = Math.max(1, texture.width); var th = Math.max(1, texture.height); texture.refCount += 1; } else { var renderbuffer = attachment.renderbuffer._renderbuffer; renderbuffer.refCount += 1; } } function attach (location, attachment) { if (attachment) { if (attachment.texture) { gl.framebufferTexture2D( GL_FRAMEBUFFER$1, location, attachment.target, attachment.texture._texture.texture, 0); } else { gl.framebufferRenderbuffer( GL_FRAMEBUFFER$1, location, GL_RENDERBUFFER$1, attachment.renderbuffer._renderbuffer.renderbuffer); } } } function parseAttachment (attachment) { var target = GL_TEXTURE_2D$2; var texture = null; var renderbuffer = null; var data = attachment; if (typeof attachment === 'object') { data = attachment.data; if ('target' in attachment) { target = attachment.target | 0; } } var type = data._reglType; if (type === 'texture2d') { texture = data; } else if (type === 'textureCube') { texture = data; } else if (type === 'renderbuffer') { renderbuffer = data; target = GL_RENDERBUFFER$1; } else { } return new FramebufferAttachment(target, texture, renderbuffer) } function allocAttachment ( width, height, isTexture, format, type) { if (isTexture) { var texture = textureState.create2D({ width: width, height: height, format: format, type: type }); texture._texture.refCount = 0; return new FramebufferAttachment(GL_TEXTURE_2D$2, texture, null) } else { var rb = renderbufferState.create({ width: width, height: height, format: format }); rb._renderbuffer.refCount = 0; return new FramebufferAttachment(GL_RENDERBUFFER$1, null, rb) } } function unwrapAttachment (attachment) { return attachment && (attachment.texture || attachment.renderbuffer) } function resizeAttachment (attachment, w, h) { if (attachment) { if (attachment.texture) { attachment.texture.resize(w, h); } else if (attachment.renderbuffer) { attachment.renderbuffer.resize(w, h); } attachment.width = w; attachment.height = h; } } var framebufferCount = 0; var framebufferSet = {}; function REGLFramebuffer () { this.id = framebufferCount++; framebufferSet[this.id] = this; this.framebuffer = gl.createFramebuffer(); this.width = 0; this.height = 0; this.colorAttachments = []; this.depthAttachment = null; this.stencilAttachment = null; this.depthStencilAttachment = null; } function decFBORefs (framebuffer) { framebuffer.colorAttachments.forEach(decRef); decRef(framebuffer.depthAttachment); decRef(framebuffer.stencilAttachment); decRef(framebuffer.depthStencilAttachment); } function destroy (framebuffer) { var handle = framebuffer.framebuffer; gl.deleteFramebuffer(handle); framebuffer.framebuffer = null; stats.framebufferCount--; delete framebufferSet[framebuffer.id]; } function updateFramebuffer (framebuffer) { var i; gl.bindFramebuffer(GL_FRAMEBUFFER$1, framebuffer.framebuffer); var colorAttachments = framebuffer.colorAttachments; for (i = 0; i < colorAttachments.length; ++i) { attach(GL_COLOR_ATTACHMENT0$1 + i, colorAttachments[i]); } for (i = colorAttachments.length; i < limits.maxColorAttachments; ++i) { gl.framebufferTexture2D( GL_FRAMEBUFFER$1, GL_COLOR_ATTACHMENT0$1 + i, GL_TEXTURE_2D$2, null, 0); } gl.framebufferTexture2D( GL_FRAMEBUFFER$1, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D$2, null, 0); gl.framebufferTexture2D( GL_FRAMEBUFFER$1, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D$2, null, 0); gl.framebufferTexture2D( GL_FRAMEBUFFER$1, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D$2, null, 0); attach(GL_DEPTH_ATTACHMENT, framebuffer.depthAttachment); attach(GL_STENCIL_ATTACHMENT, framebuffer.stencilAttachment); attach(GL_DEPTH_STENCIL_ATTACHMENT, framebuffer.depthStencilAttachment); // Check status code var status = gl.checkFramebufferStatus(GL_FRAMEBUFFER$1); if (!gl.isContextLost() && status !== GL_FRAMEBUFFER_COMPLETE$1) { } gl.bindFramebuffer(GL_FRAMEBUFFER$1, framebufferState.next ? framebufferState.next.framebuffer : null); framebufferState.cur = framebufferState.next; // FIXME: Clear error code here. This is a work around for a bug in // headless-gl gl.getError(); } function createFBO (a0, a1) { var framebuffer = new REGLFramebuffer(); stats.framebufferCount++; function reglFramebuffer (a, b) { var i; var width = 0; var height = 0; var needsDepth = true; var needsStencil = true; var colorBuffer = null; var colorTexture = true; var colorFormat = 'rgba'; var colorType = 'uint8'; var colorCount = 1; var depthBuffer = null; var stencilBuffer = null; var depthStencilBuffer = null; var depthStencilTexture = false; if (typeof a === 'number') { width = a | 0; height = (b | 0) || width; } else if (!a) { width = height = 1; } else { var options = a; if ('shape' in options) { var shape = options.shape; width = shape[0]; height = shape[1]; } else { if ('radius' in options) { width = height = options.radius; } if ('width' in options) { width = options.width; } if ('height' in options) { height = options.height; } } if ('color' in options || 'colors' in options) { colorBuffer = options.color || options.colors; if (Array.isArray(colorBuffer)) { } } if (!colorBuffer) { if ('colorCount' in options) { colorCount = options.colorCount | 0; } if ('colorTexture' in options) { colorTexture = !!options.colorTexture; colorFormat = 'rgba4'; } if ('colorType' in options) { colorType = options.colorType; if (!colorTexture) { if (colorType === 'half float' || colorType === 'float16') { colorFormat = 'rgba16f'; } else if (colorType === 'float' || colorType === 'float32') { colorFormat = 'rgba32f'; } } else { } } if ('colorFormat' in options) { colorFormat = options.colorFormat; if (colorTextureFormats.indexOf(colorFormat) >= 0) { colorTexture = true; } else if (colorRenderbufferFormats.indexOf(colorFormat) >= 0) { colorTexture = false; } else { if (colorTexture) { } else { } } } } if ('depthTexture' in options || 'depthStencilTexture' in options) { depthStencilTexture = !!(options.depthTexture || options.depthStencilTexture); } if ('depth' in options) { if (typeof options.depth === 'boolean') { needsDepth = options.depth; } else { depthBuffer = options.depth; needsStencil = false; } } if ('stencil' in options) { if (typeof options.stencil === 'boolean') { needsStencil = options.stencil; } else { stencilBuffer = options.stencil; needsDepth = false; } } if ('depthStencil' in options) { if (typeof options.depthStencil === 'boolean') { needsDepth = needsStencil = options.depthStencil; } else { depthStencilBuffer = options.depthStencil; needsDepth = false; needsStencil = false; } } } // parse attachments var colorAttachments = null; var depthAttachment = null; var stencilAttachment = null; var depthStencilAttachment = null; // Set up color attachments if (Array.isArray(colorBuffer)) { colorAttachments = colorBuffer.map(parseAttachment); } else if (colorBuffer) { colorAttachments = [parseAttachment(colorBuffer)]; } else { colorAttachments = new Array(colorCount); for (i = 0; i < colorCount; ++i) { colorAttachments[i] = allocAttachment( width, height, colorTexture, colorFormat, colorType); } } width = width || colorAttachments[0].width; height = height || colorAttachments[0].height; if (depthBuffer) { depthAttachment = parseAttachment(depthBuffer); } else if (needsDepth && !needsStencil) { depthAttachment = allocAttachment( width, height, depthStencilTexture, 'depth', 'uint32'); } if (stencilBuffer) { stencilAttachment = parseAttachment(stencilBuffer); } else if (needsStencil && !needsDepth) { stencilAttachment = allocAttachment( width, height, false, 'stencil', 'uint8'); } if (depthStencilBuffer) { depthStencilAttachment = parseAttachment(depthStencilBuffer); } else if (!depthBuffer && !stencilBuffer && needsStencil && needsDepth) { depthStencilAttachment = allocAttachment( width, height, depthStencilTexture, 'depth stencil', 'depth stencil'); } var commonColorAttachmentSize = null; for (i = 0; i < colorAttachments.length; ++i) { incRefAndCheckShape(colorAttachments[i], width, height); if (colorAttachments[i] && colorAttachments[i].texture) { var colorAttachmentSize = textureFormatChannels[colorAttachments[i].texture._texture.format] * textureTypeSizes[colorAttachments[i].texture._texture.type]; if (commonColorAttachmentSize === null) { commonColorAttachmentSize = colorAttachmentSize; } else { // We need to make sure that all color attachments have the same number of bitplanes // (that is, the same numer of bits per pixel) // This is required by the GLES2.0 standard. See the beginning of Chapter 4 in that document. } } } incRefAndCheckShape(depthAttachment, width, height); incRefAndCheckShape(stencilAttachment, width, height); incRefAndCheckShape(depthStencilAttachment, width, height); // decrement references decFBORefs(framebuffer); framebuffer.width = width; framebuffer.height = height; framebuffer.colorAttachments = colorAttachments; framebuffer.depthAttachment = depthAttachment; framebuffer.stencilAttachment = stencilAttachment; framebuffer.depthStencilAttachment = depthStencilAttachment; reglFramebuffer.color = colorAttachments.map(unwrapAttachment); reglFramebuffer.depth = unwrapAttachment(depthAttachment); reglFramebuffer.stencil = unwrapAttachment(stencilAttachment); reglFramebuffer.depthStencil = unwrapAttachment(depthStencilAttachment); reglFramebuffer.width = framebuffer.width; reglFramebuffer.height = framebuffer.height; updateFramebuffer(framebuffer); return reglFramebuffer } function resize (w_, h_) { var w = Math.max(w_ | 0, 1); var h = Math.max((h_ | 0) || w, 1); if (w === framebuffer.width && h === framebuffer.height) { return reglFramebuffer } // resize all buffers var colorAttachments = framebuffer.colorAttachments; for (var i = 0; i < colorAttachments.length; ++i) { resizeAttachment(colorAttachments[i], w, h); } resizeAttachment(framebuffer.depthAttachment, w, h); resizeAttachment(framebuffer.stencilAttachment, w, h); resizeAttachment(framebuffer.depthStencilAttachment, w, h); framebuffer.width = reglFramebuffer.width = w; framebuffer.height = reglFramebuffer.height = h; updateFramebuffer(framebuffer); return reglFramebuffer } reglFramebuffer(a0, a1); return extend(reglFramebuffer, { resize: resize, _reglType: 'framebuffer', _framebuffer: framebuffer, destroy: function () { destroy(framebuffer); decFBORefs(framebuffer); }, use: function (block) { framebufferState.setFBO({ framebuffer: reglFramebuffer }, block); } }) } function createCubeFBO (options) { var faces = Array(6); function reglFramebufferCube (a) { var i; var params = { color: null }; var radius = 0; var colorBuffer = null; var colorFormat = 'rgba'; var colorType = 'uint8'; var colorCount = 1; if (typeof a === 'number') { radius = a | 0; } else if (!a) { radius = 1; } else { var options = a; if ('shape' in options) { var shape = options.shape; radius = shape[0]; } else { if ('radius' in options) { radius = options.radius | 0; } if ('width' in options) { radius = options.width | 0; if ('height' in options) { } } else if ('height' in options) { radius = options.height | 0; } } if ('color' in options || 'colors' in options) { colorBuffer = options.color || options.colors; if (Array.isArray(colorBuffer)) { } } if (!colorBuffer) { if ('colorCount' in options) { colorCount = options.colorCount | 0; } if ('colorType' in options) { colorType = options.colorType; } if ('colorFormat' in options) { colorFormat = options.colorFormat; } } if ('depth' in options) { params.depth = options.depth; } if ('stencil' in options) { params.stencil = options.stencil; } if ('depthStencil' in options) { params.depthStencil = options.depthStencil; } } var colorCubes; if (colorBuffer) { if (Array.isArray(colorBuffer)) { colorCubes = []; for (i = 0; i < colorBuffer.length; ++i) { colorCubes[i] = colorBuffer[i]; } } else { colorCubes = [ colorBuffer ]; } } else { colorCubes = Array(colorCount); var cubeMapParams = { radius: radius, format: colorFormat, type: colorType }; for (i = 0; i < colorCount; ++i) { colorCubes[i] = textureState.createCube(cubeMapParams); } } // Check color cubes params.color = Array(colorCubes.length); for (i = 0; i < colorCubes.length; ++i) { var cube = colorCubes[i]; radius = radius || cube.width; params.color[i] = { target: GL_TEXTURE_CUBE_MAP_POSITIVE_X$2, data: colorCubes[i] }; } for (i = 0; i < 6; ++i) { for (var j = 0; j < colorCubes.length; ++j) { params.color[j].target = GL_TEXTURE_CUBE_MAP_POSITIVE_X$2 + i; } // reuse depth-stencil attachments across all cube maps if (i > 0) { params.depth = faces[0].depth; params.stencil = faces[0].stencil; params.depthStencil = faces[0].depthStencil; } if (faces[i]) { (faces[i])(params); } else { faces[i] = createFBO(params); } } return extend(reglFramebufferCube, { width: radius, height: radius, color: colorCubes }) } function resize (radius_) { var i; var radius = radius_ | 0; if (radius === reglFramebufferCube.width) { return reglFramebufferCube } var colors = reglFramebufferCube.color; for (i = 0; i < colors.length; ++i) { colors[i].resize(radius); } for (i = 0; i < 6; ++i) { faces[i].resize(radius); } reglFramebufferCube.width = reglFramebufferCube.height = radius; return reglFramebufferCube } reglFramebufferCube(options); return extend(reglFramebufferCube, { faces: faces, resize: resize, _reglType: 'framebufferCube', destroy: function () { faces.forEach(function (f) { f.destroy(); }); } }) } function restoreFramebuffers () { framebufferState.cur = null; framebufferState.next = null; framebufferState.dirty = true; values(framebufferSet).forEach(function (fb) { fb.framebuffer = gl.createFramebuffer(); updateFramebuffer(fb); }); } return extend(framebufferState, { getFramebuffer: function (object) { if (typeof object === 'function' && object._reglType === 'framebuffer') { var fbo = object._framebuffer; if (fbo instanceof REGLFramebuffer) { return fbo } } return null }, create: createFBO, createCube: createCubeFBO, clear: function () { values(framebufferSet).forEach(destroy); }, restore: restoreFramebuffers }) } var GL_FLOAT$5 = 5126; function AttributeRecord () { this.state = 0; this.x = 0.0; this.y = 0.0; this.z = 0.0; this.w = 0.0; this.buffer = null; this.size = 0; this.normalized = false; this.type = GL_FLOAT$5; this.offset = 0; this.stride = 0; this.divisor = 0; } function wrapAttributeState ( gl, extensions, limits, stringStore) { var NUM_ATTRIBUTES = limits.maxAttributes; var attributeBindings = new Array(NUM_ATTRIBUTES); for (var i = 0; i < NUM_ATTRIBUTES; ++i) { attributeBindings[i] = new AttributeRecord(); } return { Record: AttributeRecord, scope: {}, state: attributeBindings } } var GL_FRAGMENT_SHADER = 35632; var GL_VERTEX_SHADER = 35633; var GL_ACTIVE_UNIFORMS = 0x8B86; var GL_ACTIVE_ATTRIBUTES = 0x8B89; function wrapShaderState (gl, stringStore, stats, config) { // =================================================== // glsl compilation and linking // =================================================== var fragShaders = {}; var vertShaders = {}; function ActiveInfo (name, id, location, info) { this.name = name; this.id = id; this.location = location; this.info = info; } function insertActiveInfo (list, info) { for (var i = 0; i < list.length; ++i) { if (list[i].id === info.id) { list[i].location = info.location; return } } list.push(info); } function getShader (type, id, command) { var cache = type === GL_FRAGMENT_SHADER ? fragShaders : vertShaders; var shader = cache[id]; if (!shader) { var source = stringStore.str(id); shader = gl.createShader(type); gl.shaderSource(shader, source); gl.compileShader(shader); cache[id] = shader; } return shader } // =================================================== // program linking // =================================================== var programCache = {}; var programList = []; var PROGRAM_COUNTER = 0; function REGLProgram (fragId, vertId) { this.id = PROGRAM_COUNTER++; this.fragId = fragId; this.vertId = vertId; this.program = null; this.uniforms = []; this.attributes = []; if (config.profile) { this.stats = { uniformsCount: 0, attributesCount: 0 }; } } function linkProgram (desc, command) { var i, info; // ------------------------------- // compile & link // ------------------------------- var fragShader = getShader(GL_FRAGMENT_SHADER, desc.fragId); var vertShader = getShader(GL_VERTEX_SHADER, desc.vertId); var program = desc.program = gl.createProgram(); gl.attachShader(program, fragShader); gl.attachShader(program, vertShader); gl.linkProgram(program); // ------------------------------- // grab uniforms // ------------------------------- var numUniforms = gl.getProgramParameter(program, GL_ACTIVE_UNIFORMS); if (config.profile) { desc.stats.uniformsCount = numUniforms; } var uniforms = desc.uniforms; for (i = 0; i < numUniforms; ++i) { info = gl.getActiveUniform(program, i); if (info) { if (info.size > 1) { for (var j = 0; j < info.size; ++j) { var name = info.name.replace('[0]', '[' + j + ']'); insertActiveInfo(uniforms, new ActiveInfo( name, stringStore.id(name), gl.getUniformLocation(program, name), info)); } } else { insertActiveInfo(uniforms, new ActiveInfo( info.name, stringStore.id(info.name), gl.getUniformLocation(program, info.name), info)); } } } // ------------------------------- // grab attributes // ------------------------------- var numAttributes = gl.getProgramParameter(program, GL_ACTIVE_ATTRIBUTES); if (config.profile) { desc.stats.attributesCount = numAttributes; } var attributes = desc.attributes; for (i = 0; i < numAttributes; ++i) { info = gl.getActiveAttrib(program, i); if (info) { insertActiveInfo(attributes, new ActiveInfo( info.name, stringStore.id(info.name), gl.getAttribLocation(program, info.name), info)); } } } if (config.profile) { stats.getMaxUniformsCount = function () { var m = 0; programList.forEach(function (desc) { if (desc.stats.uniformsCount > m) { m = desc.stats.uniformsCount; } }); return m }; stats.getMaxAttributesCount = function () { var m = 0; programList.forEach(function (desc) { if (desc.stats.attributesCount > m) { m = desc.stats.attributesCount; } }); return m }; } function restoreShaders () { fragShaders = {}; vertShaders = {}; for (var i = 0; i < programList.length; ++i) { linkProgram(programList[i]); } } return { clear: function () { var deleteShader = gl.deleteShader.bind(gl); values(fragShaders).forEach(deleteShader); fragShaders = {}; values(vertShaders).forEach(deleteShader); vertShaders = {}; programList.forEach(function (desc) { gl.deleteProgram(desc.program); }); programList.length = 0; programCache = {}; stats.shaderCount = 0; }, program: function (vertId, fragId, command) { var cache = programCache[fragId]; if (!cache) { cache = programCache[fragId] = {}; } var program = cache[vertId]; if (!program) { program = new REGLProgram(fragId, vertId); stats.shaderCount++; linkProgram(program, command); cache[vertId] = program; programList.push(program); } return program }, restore: restoreShaders, shader: getShader, frag: -1, vert: -1 } } var GL_RGBA$3 = 6408; var GL_UNSIGNED_BYTE$6 = 5121; var GL_PACK_ALIGNMENT = 0x0D05; var GL_FLOAT$6 = 0x1406; // 5126 function wrapReadPixels ( gl, framebufferState, reglPoll, context, glAttributes, extensions, limits) { function readPixelsImpl (input) { var type; if (framebufferState.next === null) { type = GL_UNSIGNED_BYTE$6; } else { type = framebufferState.next.colorAttachments[0].texture._texture.type; if (extensions.oes_texture_float) { if (type === GL_FLOAT$6) { } } else { } } var x = 0; var y = 0; var width = context.framebufferWidth; var height = context.framebufferHeight; var data = null; if (isTypedArray(input)) { data = input; } else if (input) { x = input.x | 0; y = input.y | 0; width = (input.width || (context.framebufferWidth - x)) | 0; height = (input.height || (context.framebufferHeight - y)) | 0; data = input.data || null; } // sanity check input.data if (data) { if (type === GL_UNSIGNED_BYTE$6) { } else if (type === GL_FLOAT$6) { } } // Update WebGL state reglPoll(); // Compute size var size = width * height * 4; // Allocate data if (!data) { if (type === GL_UNSIGNED_BYTE$6) { data = new Uint8Array(size); } else if (type === GL_FLOAT$6) { data = data || new Float32Array(size); } } // Type check // Run read pixels gl.pixelStorei(GL_PACK_ALIGNMENT, 4); gl.readPixels(x, y, width, height, GL_RGBA$3, type, data); return data } function readPixelsFBO (options) { var result; framebufferState.setFBO({ framebuffer: options.framebuffer }, function () { result = readPixelsImpl(options); }); return result } function readPixels (options) { if (!options || !('framebuffer' in options)) { return readPixelsImpl(options) } else { return readPixelsFBO(options) } } return readPixels } function slice (x) { return Array.prototype.slice.call(x) } function join (x) { return slice(x).join('') } function createEnvironment () { // Unique variable id counter var varCounter = 0; // Linked values are passed from this scope into the generated code block // Calling link() passes a value into the generated scope and returns // the variable name which it is bound to var linkedNames = []; var linkedValues = []; function link (value) { for (var i = 0; i < linkedValues.length; ++i) { if (linkedValues[i] === value) { return linkedNames[i] } } var name = 'g' + (varCounter++); linkedNames.push(name); linkedValues.push(value); return name } // create a code block function block () { var code = []; function push () { code.push.apply(code, slice(arguments)); } var vars = []; function def () { var name = 'v' + (varCounter++); vars.push(name); if (arguments.length > 0) { code.push(name, '='); code.push.apply(code, slice(arguments)); code.push(';'); } return name } return extend(push, { def: def, toString: function () { return join([ (vars.length > 0 ? 'var ' + vars.join(',') + ';' : ''), join(code) ]) } }) } function scope () { var entry = block(); var exit = block(); var entryToString = entry.toString; var exitToString = exit.toString; function save (object, prop) { exit(object, prop, '=', entry.def(object, prop), ';'); } return extend(function () { entry.apply(entry, slice(arguments)); }, { def: entry.def, entry: entry, exit: exit, save: save, set: function (object, prop, value) { save(object, prop); entry(object, prop, '=', value, ';'); }, toString: function () { return entryToString() + exitToString() } }) } function conditional () { var pred = join(arguments); var thenBlock = scope(); var elseBlock = scope(); var thenToString = thenBlock.toString; var elseToString = elseBlock.toString; return extend(thenBlock, { then: function () { thenBlock.apply(thenBlock, slice(arguments)); return this }, else: function () { elseBlock.apply(elseBlock, slice(arguments)); return this }, toString: function () { var elseClause = elseToString(); if (elseClause) { elseClause = 'else{' + elseClause + '}'; } return join([ 'if(', pred, '){', thenToString(), '}', elseClause ]) } }) } // procedure list var globalBlock = block(); var procedures = {}; function proc (name, count) { var args = []; function arg () { var name = 'a' + args.length; args.push(name); return name } count = count || 0; for (var i = 0; i < count; ++i) { arg(); } var body = scope(); var bodyToString = body.toString; var result = procedures[name] = extend(body, { arg: arg, toString: function () { return join([ 'function(', args.join(), '){', bodyToString(), '}' ]) } }); return result } function compile () { var code = ['"use strict";', globalBlock, 'return {']; Object.keys(procedures).forEach(function (name) { code.push('"', name, '":', procedures[name].toString(), ','); }); code.push('}'); var src = join(code) .replace(/;/g, ';\n') .replace(/}/g, '}\n') .replace(/{/g, '{\n'); var proc = Function.apply(null, linkedNames.concat(src)); return proc.apply(null, linkedValues) } return { global: globalBlock, link: link, block: block, proc: proc, scope: scope, cond: conditional, compile: compile } } // "cute" names for vector components var CUTE_COMPONENTS = 'xyzw'.split(''); var GL_UNSIGNED_BYTE$7 = 5121; var ATTRIB_STATE_POINTER = 1; var ATTRIB_STATE_CONSTANT = 2; var DYN_FUNC$1 = 0; var DYN_PROP$1 = 1; var DYN_CONTEXT$1 = 2; var DYN_STATE$1 = 3; var DYN_THUNK = 4; var S_DITHER = 'dither'; var S_BLEND_ENABLE = 'blend.enable'; var S_BLEND_COLOR = 'blend.color'; var S_BLEND_EQUATION = 'blend.equation'; var S_BLEND_FUNC = 'blend.func'; var S_DEPTH_ENABLE = 'depth.enable'; var S_DEPTH_FUNC = 'depth.func'; var S_DEPTH_RANGE = 'depth.range'; var S_DEPTH_MASK = 'depth.mask'; var S_COLOR_MASK = 'colorMask'; var S_CULL_ENABLE = 'cull.enable'; var S_CULL_FACE = 'cull.face'; var S_FRONT_FACE = 'frontFace'; var S_LINE_WIDTH = 'lineWidth'; var S_POLYGON_OFFSET_ENABLE = 'polygonOffset.enable'; var S_POLYGON_OFFSET_OFFSET = 'polygonOffset.offset'; var S_SAMPLE_ALPHA = 'sample.alpha'; var S_SAMPLE_ENABLE = 'sample.enable'; var S_SAMPLE_COVERAGE = 'sample.coverage'; var S_STENCIL_ENABLE = 'stencil.enable'; var S_STENCIL_MASK = 'stencil.mask'; var S_STENCIL_FUNC = 'stencil.func'; var S_STENCIL_OPFRONT = 'stencil.opFront'; var S_STENCIL_OPBACK = 'stencil.opBack'; var S_SCISSOR_ENABLE = 'scissor.enable'; var S_SCISSOR_BOX = 'scissor.box'; var S_VIEWPORT = 'viewport'; var S_PROFILE = 'profile'; var S_FRAMEBUFFER = 'framebuffer'; var S_VERT = 'vert'; var S_FRAG = 'frag'; var S_ELEMENTS = 'elements'; var S_PRIMITIVE = 'primitive'; var S_COUNT = 'count'; var S_OFFSET = 'offset'; var S_INSTANCES = 'instances'; var SUFFIX_WIDTH = 'Width'; var SUFFIX_HEIGHT = 'Height'; var S_FRAMEBUFFER_WIDTH = S_FRAMEBUFFER + SUFFIX_WIDTH; var S_FRAMEBUFFER_HEIGHT = S_FRAMEBUFFER + SUFFIX_HEIGHT; var S_VIEWPORT_WIDTH = S_VIEWPORT + SUFFIX_WIDTH; var S_VIEWPORT_HEIGHT = S_VIEWPORT + SUFFIX_HEIGHT; var S_DRAWINGBUFFER = 'drawingBuffer'; var S_DRAWINGBUFFER_WIDTH = S_DRAWINGBUFFER + SUFFIX_WIDTH; var S_DRAWINGBUFFER_HEIGHT = S_DRAWINGBUFFER + SUFFIX_HEIGHT; var NESTED_OPTIONS = [ S_BLEND_FUNC, S_BLEND_EQUATION, S_STENCIL_FUNC, S_STENCIL_OPFRONT, S_STENCIL_OPBACK, S_SAMPLE_COVERAGE, S_VIEWPORT, S_SCISSOR_BOX, S_POLYGON_OFFSET_OFFSET ]; var GL_ARRAY_BUFFER$1 = 34962; var GL_ELEMENT_ARRAY_BUFFER$1 = 34963; var GL_CULL_FACE = 0x0B44; var GL_BLEND = 0x0BE2; var GL_DITHER = 0x0BD0; var GL_STENCIL_TEST = 0x0B90; var GL_DEPTH_TEST = 0x0B71; var GL_SCISSOR_TEST = 0x0C11; var GL_POLYGON_OFFSET_FILL = 0x8037; var GL_SAMPLE_ALPHA_TO_COVERAGE = 0x809E; var GL_SAMPLE_COVERAGE = 0x80A0; var GL_FLOAT$7 = 5126; var GL_FLOAT_VEC2 = 35664; var GL_FLOAT_VEC3 = 35665; var GL_FLOAT_VEC4 = 35666; var GL_INT$2 = 5124; var GL_INT_VEC2 = 35667; var GL_INT_VEC3 = 35668; var GL_INT_VEC4 = 35669; var GL_BOOL = 35670; var GL_BOOL_VEC2 = 35671; var GL_BOOL_VEC3 = 35672; var GL_BOOL_VEC4 = 35673; var GL_FLOAT_MAT2 = 35674; var GL_FLOAT_MAT3 = 35675; var GL_FLOAT_MAT4 = 35676; var GL_SAMPLER_2D = 35678; var GL_SAMPLER_CUBE = 35680; var GL_TRIANGLES$1 = 4; var GL_FRONT = 1028; var GL_BACK = 1029; var GL_CW = 0x0900; var GL_CCW = 0x0901; var GL_MIN_EXT = 0x8007; var GL_MAX_EXT = 0x8008; var GL_ALWAYS = 519; var GL_KEEP = 7680; var GL_ZERO = 0; var GL_ONE = 1; var GL_FUNC_ADD = 0x8006; var GL_LESS = 513; var GL_FRAMEBUFFER$2 = 0x8D40; var GL_COLOR_ATTACHMENT0$2 = 0x8CE0; var blendFuncs = { '0': 0, '1': 1, 'zero': 0, 'one': 1, 'src color': 768, 'one minus src color': 769, 'src alpha': 770, 'one minus src alpha': 771, 'dst color': 774, 'one minus dst color': 775, 'dst alpha': 772, 'one minus dst alpha': 773, 'constant color': 32769, 'one minus constant color': 32770, 'constant alpha': 32771, 'one minus constant alpha': 32772, 'src alpha saturate': 776 }; var compareFuncs = { 'never': 512, 'less': 513, '<': 513, 'equal': 514, '=': 514, '==': 514, '===': 514, 'lequal': 515, '<=': 515, 'greater': 516, '>': 516, 'notequal': 517, '!=': 517, '!==': 517, 'gequal': 518, '>=': 518, 'always': 519 }; var stencilOps = { '0': 0, 'zero': 0, 'keep': 7680, 'replace': 7681, 'increment': 7682, 'decrement': 7683, 'increment wrap': 34055, 'decrement wrap': 34056, 'invert': 5386 }; var orientationType = { 'cw': GL_CW, 'ccw': GL_CCW }; function isBufferArgs (x) { return Array.isArray(x) || isTypedArray(x) || isNDArrayLike(x) } // Make sure viewport is processed first function sortState (state) { return state.sort(function (a, b) { if (a === S_VIEWPORT) { return -1 } else if (b === S_VIEWPORT) { return 1 } return (a < b) ? -1 : 1 }) } function Declaration (thisDep, contextDep, propDep, append) { this.thisDep = thisDep; this.contextDep = contextDep; this.propDep = propDep; this.append = append; } function isStatic (decl) { return decl && !(decl.thisDep || decl.contextDep || decl.propDep) } function createStaticDecl (append) { return new Declaration(false, false, false, append) } function createDynamicDecl (dyn, append) { var type = dyn.type; if (type === DYN_FUNC$1) { var numArgs = dyn.data.length; return new Declaration( true, numArgs >= 1, numArgs >= 2, append) } else if (type === DYN_THUNK) { var data = dyn.data; return new Declaration( data.thisDep, data.contextDep, data.propDep, append) } else { return new Declaration( type === DYN_STATE$1, type === DYN_CONTEXT$1, type === DYN_PROP$1, append) } } var SCOPE_DECL = new Declaration(false, false, false, function () {}); function reglCore ( gl, stringStore, extensions, limits, bufferState, elementState, textureState, framebufferState, uniformState, attributeState, shaderState, drawState, contextState, timer, config) { var AttributeRecord = attributeState.Record; var blendEquations = { 'add': 32774, 'subtract': 32778, 'reverse subtract': 32779 }; if (extensions.ext_blend_minmax) { blendEquations.min = GL_MIN_EXT; blendEquations.max = GL_MAX_EXT; } var extInstancing = extensions.angle_instanced_arrays; var extDrawBuffers = extensions.webgl_draw_buffers; // =================================================== // =================================================== // WEBGL STATE // =================================================== // =================================================== var currentState = { dirty: true, profile: config.profile }; var nextState = {}; var GL_STATE_NAMES = []; var GL_FLAGS = {}; var GL_VARIABLES = {}; function propName (name) { return name.replace('.', '_') } function stateFlag (sname, cap, init) { var name = propName(sname); GL_STATE_NAMES.push(sname); nextState[name] = currentState[name] = !!init; GL_FLAGS[name] = cap; } function stateVariable (sname, func, init) { var name = propName(sname); GL_STATE_NAMES.push(sname); if (Array.isArray(init)) { currentState[name] = init.slice(); nextState[name] = init.slice(); } else { currentState[name] = nextState[name] = init; } GL_VARIABLES[name] = func; } // Dithering stateFlag(S_DITHER, GL_DITHER); // Blending stateFlag(S_BLEND_ENABLE, GL_BLEND); stateVariable(S_BLEND_COLOR, 'blendColor', [0, 0, 0, 0]); stateVariable(S_BLEND_EQUATION, 'blendEquationSeparate', [GL_FUNC_ADD, GL_FUNC_ADD]); stateVariable(S_BLEND_FUNC, 'blendFuncSeparate', [GL_ONE, GL_ZERO, GL_ONE, GL_ZERO]); // Depth stateFlag(S_DEPTH_ENABLE, GL_DEPTH_TEST, true); stateVariable(S_DEPTH_FUNC, 'depthFunc', GL_LESS); stateVariable(S_DEPTH_RANGE, 'depthRange', [0, 1]); stateVariable(S_DEPTH_MASK, 'depthMask', true); // Color mask stateVariable(S_COLOR_MASK, S_COLOR_MASK, [true, true, true, true]); // Face culling stateFlag(S_CULL_ENABLE, GL_CULL_FACE); stateVariable(S_CULL_FACE, 'cullFace', GL_BACK); // Front face orientation stateVariable(S_FRONT_FACE, S_FRONT_FACE, GL_CCW); // Line width stateVariable(S_LINE_WIDTH, S_LINE_WIDTH, 1); // Polygon offset stateFlag(S_POLYGON_OFFSET_ENABLE, GL_POLYGON_OFFSET_FILL); stateVariable(S_POLYGON_OFFSET_OFFSET, 'polygonOffset', [0, 0]); // Sample coverage stateFlag(S_SAMPLE_ALPHA, GL_SAMPLE_ALPHA_TO_COVERAGE); stateFlag(S_SAMPLE_ENABLE, GL_SAMPLE_COVERAGE); stateVariable(S_SAMPLE_COVERAGE, 'sampleCoverage', [1, false]); // Stencil stateFlag(S_STENCIL_ENABLE, GL_STENCIL_TEST); stateVariable(S_STENCIL_MASK, 'stencilMask', -1); stateVariable(S_STENCIL_FUNC, 'stencilFunc', [GL_ALWAYS, 0, -1]); stateVariable(S_STENCIL_OPFRONT, 'stencilOpSeparate', [GL_FRONT, GL_KEEP, GL_KEEP, GL_KEEP]); stateVariable(S_STENCIL_OPBACK, 'stencilOpSeparate', [GL_BACK, GL_KEEP, GL_KEEP, GL_KEEP]); // Scissor stateFlag(S_SCISSOR_ENABLE, GL_SCISSOR_TEST); stateVariable(S_SCISSOR_BOX, 'scissor', [0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight]); // Viewport stateVariable(S_VIEWPORT, S_VIEWPORT, [0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight]); // =================================================== // =================================================== // ENVIRONMENT // =================================================== // =================================================== var sharedState = { gl: gl, context: contextState, strings: stringStore, next: nextState, current: currentState, draw: drawState, elements: elementState, buffer: bufferState, shader: shaderState, attributes: attributeState.state, uniforms: uniformState, framebuffer: framebufferState, extensions: extensions, timer: timer, isBufferArgs: isBufferArgs }; var sharedConstants = { primTypes: primTypes, compareFuncs: compareFuncs, blendFuncs: blendFuncs, blendEquations: blendEquations, stencilOps: stencilOps, glTypes: glTypes, orientationType: orientationType }; if (extDrawBuffers) { sharedConstants.backBuffer = [GL_BACK]; sharedConstants.drawBuffer = loop(limits.maxDrawbuffers, function (i) { if (i === 0) { return [0] } return loop(i, function (j) { return GL_COLOR_ATTACHMENT0$2 + j }) }); } var drawCallCounter = 0; function createREGLEnvironment () { var env = createEnvironment(); var link = env.link; var global = env.global; env.id = drawCallCounter++; env.batchId = '0'; // link shared state var SHARED = link(sharedState); var shared = env.shared = { props: 'a0' }; Object.keys(sharedState).forEach(function (prop) { shared[prop] = global.def(SHARED, '.', prop); }); // Inject runtime assertion stuff for debug builds // Copy GL state variables over var nextVars = env.next = {}; var currentVars = env.current = {}; Object.keys(GL_VARIABLES).forEach(function (variable) { if (Array.isArray(currentState[variable])) { nextVars[variable] = global.def(shared.next, '.', variable); currentVars[variable] = global.def(shared.current, '.', variable); } }); // Initialize shared constants var constants = env.constants = {}; Object.keys(sharedConstants).forEach(function (name) { constants[name] = global.def(JSON.stringify(sharedConstants[name])); }); // Helper function for calling a block env.invoke = function (block, x) { switch (x.type) { case DYN_FUNC$1: var argList = [ 'this', shared.context, shared.props, env.batchId ]; return block.def( link(x.data), '.call(', argList.slice(0, Math.max(x.data.length + 1, 4)), ')') case DYN_PROP$1: return block.def(shared.props, x.data) case DYN_CONTEXT$1: return block.def(shared.context, x.data) case DYN_STATE$1: return block.def('this', x.data) case DYN_THUNK: x.data.append(env, block); return x.data.ref } }; env.attribCache = {}; var scopeAttribs = {}; env.scopeAttrib = function (name) { var id = stringStore.id(name); if (id in scopeAttribs) { return scopeAttribs[id] } var binding = attributeState.scope[id]; if (!binding) { binding = attributeState.scope[id] = new AttributeRecord(); } var result = scopeAttribs[id] = link(binding); return result }; return env } // =================================================== // =================================================== // PARSING // =================================================== // =================================================== function parseProfile (options) { var staticOptions = options.static; var dynamicOptions = options.dynamic; var profileEnable; if (S_PROFILE in staticOptions) { var value = !!staticOptions[S_PROFILE]; profileEnable = createStaticDecl(function (env, scope) { return value }); profileEnable.enable = value; } else if (S_PROFILE in dynamicOptions) { var dyn = dynamicOptions[S_PROFILE]; profileEnable = createDynamicDecl(dyn, function (env, scope) { return env.invoke(scope, dyn) }); } return profileEnable } function parseFramebuffer (options, env) { var staticOptions = options.static; var dynamicOptions = options.dynamic; if (S_FRAMEBUFFER in staticOptions) { var framebuffer = staticOptions[S_FRAMEBUFFER]; if (framebuffer) { framebuffer = framebufferState.getFramebuffer(framebuffer); return createStaticDecl(function (env, block) { var FRAMEBUFFER = env.link(framebuffer); var shared = env.shared; block.set( shared.framebuffer, '.next', FRAMEBUFFER); var CONTEXT = shared.context; block.set( CONTEXT, '.' + S_FRAMEBUFFER_WIDTH, FRAMEBUFFER + '.width'); block.set( CONTEXT, '.' + S_FRAMEBUFFER_HEIGHT, FRAMEBUFFER + '.height'); return FRAMEBUFFER }) } else { return createStaticDecl(function (env, scope) { var shared = env.shared; scope.set( shared.framebuffer, '.next', 'null'); var CONTEXT = shared.context; scope.set( CONTEXT, '.' + S_FRAMEBUFFER_WIDTH, CONTEXT + '.' + S_DRAWINGBUFFER_WIDTH); scope.set( CONTEXT, '.' + S_FRAMEBUFFER_HEIGHT, CONTEXT + '.' + S_DRAWINGBUFFER_HEIGHT); return 'null' }) } } else if (S_FRAMEBUFFER in dynamicOptions) { var dyn = dynamicOptions[S_FRAMEBUFFER]; return createDynamicDecl(dyn, function (env, scope) { var FRAMEBUFFER_FUNC = env.invoke(scope, dyn); var shared = env.shared; var FRAMEBUFFER_STATE = shared.framebuffer; var FRAMEBUFFER = scope.def( FRAMEBUFFER_STATE, '.getFramebuffer(', FRAMEBUFFER_FUNC, ')'); scope.set( FRAMEBUFFER_STATE, '.next', FRAMEBUFFER); var CONTEXT = shared.context; scope.set( CONTEXT, '.' + S_FRAMEBUFFER_WIDTH, FRAMEBUFFER + '?' + FRAMEBUFFER + '.width:' + CONTEXT + '.' + S_DRAWINGBUFFER_WIDTH); scope.set( CONTEXT, '.' + S_FRAMEBUFFER_HEIGHT, FRAMEBUFFER + '?' + FRAMEBUFFER + '.height:' + CONTEXT + '.' + S_DRAWINGBUFFER_HEIGHT); return FRAMEBUFFER }) } else { return null } } function parseViewportScissor (options, framebuffer, env) { var staticOptions = options.static; var dynamicOptions = options.dynamic; function parseBox (param) { if (param in staticOptions) { var box = staticOptions[param]; var isStatic = true; var x = box.x | 0; var y = box.y | 0; var w, h; if ('width' in box) { w = box.width | 0; } else { isStatic = false; } if ('height' in box) { h = box.height | 0; } else { isStatic = false; } return new Declaration( !isStatic && framebuffer && framebuffer.thisDep, !isStatic && framebuffer && framebuffer.contextDep, !isStatic && framebuffer && framebuffer.propDep, function (env, scope) { var CONTEXT = env.shared.context; var BOX_W = w; if (!('width' in box)) { BOX_W = scope.def(CONTEXT, '.', S_FRAMEBUFFER_WIDTH, '-', x); } var BOX_H = h; if (!('height' in box)) { BOX_H = scope.def(CONTEXT, '.', S_FRAMEBUFFER_HEIGHT, '-', y); } return [x, y, BOX_W, BOX_H] }) } else if (param in dynamicOptions) { var dynBox = dynamicOptions[param]; var result = createDynamicDecl(dynBox, function (env, scope) { var BOX = env.invoke(scope, dynBox); var CONTEXT = env.shared.context; var BOX_X = scope.def(BOX, '.x|0'); var BOX_Y = scope.def(BOX, '.y|0'); var BOX_W = scope.def( '"width" in ', BOX, '?', BOX, '.width|0:', '(', CONTEXT, '.', S_FRAMEBUFFER_WIDTH, '-', BOX_X, ')'); var BOX_H = scope.def( '"height" in ', BOX, '?', BOX, '.height|0:', '(', CONTEXT, '.', S_FRAMEBUFFER_HEIGHT, '-', BOX_Y, ')'); return [BOX_X, BOX_Y, BOX_W, BOX_H] }); if (framebuffer) { result.thisDep = result.thisDep || framebuffer.thisDep; result.contextDep = result.contextDep || framebuffer.contextDep; result.propDep = result.propDep || framebuffer.propDep; } return result } else if (framebuffer) { return new Declaration( framebuffer.thisDep, framebuffer.contextDep, framebuffer.propDep, function (env, scope) { var CONTEXT = env.shared.context; return [ 0, 0, scope.def(CONTEXT, '.', S_FRAMEBUFFER_WIDTH), scope.def(CONTEXT, '.', S_FRAMEBUFFER_HEIGHT)] }) } else { return null } } var viewport = parseBox(S_VIEWPORT); if (viewport) { var prevViewport = viewport; viewport = new Declaration( viewport.thisDep, viewport.contextDep, viewport.propDep, function (env, scope) { var VIEWPORT = prevViewport.append(env, scope); var CONTEXT = env.shared.context; scope.set( CONTEXT, '.' + S_VIEWPORT_WIDTH, VIEWPORT[2]); scope.set( CONTEXT, '.' + S_VIEWPORT_HEIGHT, VIEWPORT[3]); return VIEWPORT }); } return { viewport: viewport, scissor_box: parseBox(S_SCISSOR_BOX) } } function parseProgram (options) { var staticOptions = options.static; var dynamicOptions = options.dynamic; function parseShader (name) { if (name in staticOptions) { var id = stringStore.id(staticOptions[name]); var result = createStaticDecl(function () { return id }); result.id = id; return result } else if (name in dynamicOptions) { var dyn = dynamicOptions[name]; return createDynamicDecl(dyn, function (env, scope) { var str = env.invoke(scope, dyn); var id = scope.def(env.shared.strings, '.id(', str, ')'); return id }) } return null } var frag = parseShader(S_FRAG); var vert = parseShader(S_VERT); var program = null; var progVar; if (isStatic(frag) && isStatic(vert)) { program = shaderState.program(vert.id, frag.id); progVar = createStaticDecl(function (env, scope) { return env.link(program) }); } else { progVar = new Declaration( (frag && frag.thisDep) || (vert && vert.thisDep), (frag && frag.contextDep) || (vert && vert.contextDep), (frag && frag.propDep) || (vert && vert.propDep), function (env, scope) { var SHADER_STATE = env.shared.shader; var fragId; if (frag) { fragId = frag.append(env, scope); } else { fragId = scope.def(SHADER_STATE, '.', S_FRAG); } var vertId; if (vert) { vertId = vert.append(env, scope); } else { vertId = scope.def(SHADER_STATE, '.', S_VERT); } var progDef = SHADER_STATE + '.program(' + vertId + ',' + fragId; return scope.def(progDef + ')') }); } return { frag: frag, vert: vert, progVar: progVar, program: program } } function parseDraw (options, env) { var staticOptions = options.static; var dynamicOptions = options.dynamic; function parseElements () { if (S_ELEMENTS in staticOptions) { var elements = staticOptions[S_ELEMENTS]; if (isBufferArgs(elements)) { elements = elementState.getElements(elementState.create(elements, true)); } else if (elements) { elements = elementState.getElements(elements); } var result = createStaticDecl(function (env, scope) { if (elements) { var result = env.link(elements); env.ELEMENTS = result; return result } env.ELEMENTS = null; return null }); result.value = elements; return result } else if (S_ELEMENTS in dynamicOptions) { var dyn = dynamicOptions[S_ELEMENTS]; return createDynamicDecl(dyn, function (env, scope) { var shared = env.shared; var IS_BUFFER_ARGS = shared.isBufferArgs; var ELEMENT_STATE = shared.elements; var elementDefn = env.invoke(scope, dyn); var elements = scope.def('null'); var elementStream = scope.def(IS_BUFFER_ARGS, '(', elementDefn, ')'); var ifte = env.cond(elementStream) .then(elements, '=', ELEMENT_STATE, '.createStream(', elementDefn, ');') .else(elements, '=', ELEMENT_STATE, '.getElements(', elementDefn, ');'); scope.entry(ifte); scope.exit( env.cond(elementStream) .then(ELEMENT_STATE, '.destroyStream(', elements, ');')); env.ELEMENTS = elements; return elements }) } return null } var elements = parseElements(); function parsePrimitive () { if (S_PRIMITIVE in staticOptions) { var primitive = staticOptions[S_PRIMITIVE]; return createStaticDecl(function (env, scope) { return primTypes[primitive] }) } else if (S_PRIMITIVE in dynamicOptions) { var dynPrimitive = dynamicOptions[S_PRIMITIVE]; return createDynamicDecl(dynPrimitive, function (env, scope) { var PRIM_TYPES = env.constants.primTypes; var prim = env.invoke(scope, dynPrimitive); return scope.def(PRIM_TYPES, '[', prim, ']') }) } else if (elements) { if (isStatic(elements)) { if (elements.value) { return createStaticDecl(function (env, scope) { return scope.def(env.ELEMENTS, '.primType') }) } else { return createStaticDecl(function () { return GL_TRIANGLES$1 }) } } else { return new Declaration( elements.thisDep, elements.contextDep, elements.propDep, function (env, scope) { var elements = env.ELEMENTS; return scope.def(elements, '?', elements, '.primType:', GL_TRIANGLES$1) }) } } return null } function parseParam (param, isOffset) { if (param in staticOptions) { var value = staticOptions[param] | 0; return createStaticDecl(function (env, scope) { if (isOffset) { env.OFFSET = value; } return value }) } else if (param in dynamicOptions) { var dynValue = dynamicOptions[param]; return createDynamicDecl(dynValue, function (env, scope) { var result = env.invoke(scope, dynValue); if (isOffset) { env.OFFSET = result; } return result }) } else if (isOffset && elements) { return createStaticDecl(function (env, scope) { env.OFFSET = '0'; return 0 }) } return null } var OFFSET = parseParam(S_OFFSET, true); function parseVertCount () { if (S_COUNT in staticOptions) { var count = staticOptions[S_COUNT] | 0; return createStaticDecl(function () { return count }) } else if (S_COUNT in dynamicOptions) { var dynCount = dynamicOptions[S_COUNT]; return createDynamicDecl(dynCount, function (env, scope) { var result = env.invoke(scope, dynCount); return result }) } else if (elements) { if (isStatic(elements)) { if (elements) { if (OFFSET) { return new Declaration( OFFSET.thisDep, OFFSET.contextDep, OFFSET.propDep, function (env, scope) { var result = scope.def( env.ELEMENTS, '.vertCount-', env.OFFSET); return result }) } else { return createStaticDecl(function (env, scope) { return scope.def(env.ELEMENTS, '.vertCount') }) } } else { var result = createStaticDecl(function () { return -1 }); return result } } else { var variable = new Declaration( elements.thisDep || OFFSET.thisDep, elements.contextDep || OFFSET.contextDep, elements.propDep || OFFSET.propDep, function (env, scope) { var elements = env.ELEMENTS; if (env.OFFSET) { return scope.def(elements, '?', elements, '.vertCount-', env.OFFSET, ':-1') } return scope.def(elements, '?', elements, '.vertCount:-1') }); return variable } } return null } return { elements: elements, primitive: parsePrimitive(), count: parseVertCount(), instances: parseParam(S_INSTANCES, false), offset: OFFSET } } function parseGLState (options, env) { var staticOptions = options.static; var dynamicOptions = options.dynamic; var STATE = {}; GL_STATE_NAMES.forEach(function (prop) { var param = propName(prop); function parseParam (parseStatic, parseDynamic) { if (prop in staticOptions) { var value = parseStatic(staticOptions[prop]); STATE[param] = createStaticDecl(function () { return value }); } else if (prop in dynamicOptions) { var dyn = dynamicOptions[prop]; STATE[param] = createDynamicDecl(dyn, function (env, scope) { return parseDynamic(env, scope, env.invoke(scope, dyn)) }); } } switch (prop) { case S_CULL_ENABLE: case S_BLEND_ENABLE: case S_DITHER: case S_STENCIL_ENABLE: case S_DEPTH_ENABLE: case S_SCISSOR_ENABLE: case S_POLYGON_OFFSET_ENABLE: case S_SAMPLE_ALPHA: case S_SAMPLE_ENABLE: case S_DEPTH_MASK: return parseParam( function (value) { return value }, function (env, scope, value) { return value }) case S_DEPTH_FUNC: return parseParam( function (value) { return compareFuncs[value] }, function (env, scope, value) { var COMPARE_FUNCS = env.constants.compareFuncs; return scope.def(COMPARE_FUNCS, '[', value, ']') }) case S_DEPTH_RANGE: return parseParam( function (value) { return value }, function (env, scope, value) { var Z_NEAR = scope.def('+', value, '[0]'); var Z_FAR = scope.def('+', value, '[1]'); return [Z_NEAR, Z_FAR] }) case S_BLEND_FUNC: return parseParam( function (value) { var srcRGB = ('srcRGB' in value ? value.srcRGB : value.src); var srcAlpha = ('srcAlpha' in value ? value.srcAlpha : value.src); var dstRGB = ('dstRGB' in value ? value.dstRGB : value.dst); var dstAlpha = ('dstAlpha' in value ? value.dstAlpha : value.dst); return [ blendFuncs[srcRGB], blendFuncs[dstRGB], blendFuncs[srcAlpha], blendFuncs[dstAlpha] ] }, function (env, scope, value) { var BLEND_FUNCS = env.constants.blendFuncs; function read (prefix, suffix) { var func = scope.def( '"', prefix, suffix, '" in ', value, '?', value, '.', prefix, suffix, ':', value, '.', prefix); return func } var srcRGB = read('src', 'RGB'); var dstRGB = read('dst', 'RGB'); var SRC_RGB = scope.def(BLEND_FUNCS, '[', srcRGB, ']'); var SRC_ALPHA = scope.def(BLEND_FUNCS, '[', read('src', 'Alpha'), ']'); var DST_RGB = scope.def(BLEND_FUNCS, '[', dstRGB, ']'); var DST_ALPHA = scope.def(BLEND_FUNCS, '[', read('dst', 'Alpha'), ']'); return [SRC_RGB, DST_RGB, SRC_ALPHA, DST_ALPHA] }) case S_BLEND_EQUATION: return parseParam( function (value) { if (typeof value === 'string') { return [ blendEquations[value], blendEquations[value] ] } else if (typeof value === 'object') { return [ blendEquations[value.rgb], blendEquations[value.alpha] ] } else { } }, function (env, scope, value) { var BLEND_EQUATIONS = env.constants.blendEquations; var RGB = scope.def(); var ALPHA = scope.def(); var ifte = env.cond('typeof ', value, '==="string"'); ifte.then( RGB, '=', ALPHA, '=', BLEND_EQUATIONS, '[', value, '];'); ifte.else( RGB, '=', BLEND_EQUATIONS, '[', value, '.rgb];', ALPHA, '=', BLEND_EQUATIONS, '[', value, '.alpha];'); scope(ifte); return [RGB, ALPHA] }) case S_BLEND_COLOR: return parseParam( function (value) { return loop(4, function (i) { return +value[i] }) }, function (env, scope, value) { return loop(4, function (i) { return scope.def('+', value, '[', i, ']') }) }) case S_STENCIL_MASK: return parseParam( function (value) { return value | 0 }, function (env, scope, value) { return scope.def(value, '|0') }) case S_STENCIL_FUNC: return parseParam( function (value) { var cmp = value.cmp || 'keep'; var ref = value.ref || 0; var mask = 'mask' in value ? value.mask : -1; return [ compareFuncs[cmp], ref, mask ] }, function (env, scope, value) { var COMPARE_FUNCS = env.constants.compareFuncs; var cmp = scope.def( '"cmp" in ', value, '?', COMPARE_FUNCS, '[', value, '.cmp]', ':', GL_KEEP); var ref = scope.def(value, '.ref|0'); var mask = scope.def( '"mask" in ', value, '?', value, '.mask|0:-1'); return [cmp, ref, mask] }) case S_STENCIL_OPFRONT: case S_STENCIL_OPBACK: return parseParam( function (value) { var fail = value.fail || 'keep'; var zfail = value.zfail || 'keep'; var zpass = value.zpass || 'keep'; return [ prop === S_STENCIL_OPBACK ? GL_BACK : GL_FRONT, stencilOps[fail], stencilOps[zfail], stencilOps[zpass] ] }, function (env, scope, value) { var STENCIL_OPS = env.constants.stencilOps; function read (name) { return scope.def( '"', name, '" in ', value, '?', STENCIL_OPS, '[', value, '.', name, ']:', GL_KEEP) } return [ prop === S_STENCIL_OPBACK ? GL_BACK : GL_FRONT, read('fail'), read('zfail'), read('zpass') ] }) case S_POLYGON_OFFSET_OFFSET: return parseParam( function (value) { var factor = value.factor | 0; var units = value.units | 0; return [factor, units] }, function (env, scope, value) { var FACTOR = scope.def(value, '.factor|0'); var UNITS = scope.def(value, '.units|0'); return [FACTOR, UNITS] }) case S_CULL_FACE: return parseParam( function (value) { var face = 0; if (value === 'front') { face = GL_FRONT; } else if (value === 'back') { face = GL_BACK; } return face }, function (env, scope, value) { return scope.def(value, '==="front"?', GL_FRONT, ':', GL_BACK) }) case S_LINE_WIDTH: return parseParam( function (value) { return value }, function (env, scope, value) { return value }) case S_FRONT_FACE: return parseParam( function (value) { return orientationType[value] }, function (env, scope, value) { return scope.def(value + '==="cw"?' + GL_CW + ':' + GL_CCW) }) case S_COLOR_MASK: return parseParam( function (value) { return value.map(function (v) { return !!v }) }, function (env, scope, value) { return loop(4, function (i) { return '!!' + value + '[' + i + ']' }) }) case S_SAMPLE_COVERAGE: return parseParam( function (value) { var sampleValue = 'value' in value ? value.value : 1; var sampleInvert = !!value.invert; return [sampleValue, sampleInvert] }, function (env, scope, value) { var VALUE = scope.def( '"value" in ', value, '?+', value, '.value:1'); var INVERT = scope.def('!!', value, '.invert'); return [VALUE, INVERT] }) } }); return STATE } function parseUniforms (uniforms, env) { var staticUniforms = uniforms.static; var dynamicUniforms = uniforms.dynamic; var UNIFORMS = {}; Object.keys(staticUniforms).forEach(function (name) { var value = staticUniforms[name]; var result; if (typeof value === 'number' || typeof value === 'boolean') { result = createStaticDecl(function () { return value }); } else if (typeof value === 'function') { var reglType = value._reglType; if (reglType === 'texture2d' || reglType === 'textureCube') { result = createStaticDecl(function (env) { return env.link(value) }); } else if (reglType === 'framebuffer' || reglType === 'framebufferCube') { result = createStaticDecl(function (env) { return env.link(value.color[0]) }); } else { } } else if (isArrayLike(value)) { result = createStaticDecl(function (env) { var ITEM = env.global.def('[', loop(value.length, function (i) { return value[i] }), ']'); return ITEM }); } else { } result.value = value; UNIFORMS[name] = result; }); Object.keys(dynamicUniforms).forEach(function (key) { var dyn = dynamicUniforms[key]; UNIFORMS[key] = createDynamicDecl(dyn, function (env, scope) { return env.invoke(scope, dyn) }); }); return UNIFORMS } function parseAttributes (attributes, env) { var staticAttributes = attributes.static; var dynamicAttributes = attributes.dynamic; var attributeDefs = {}; Object.keys(staticAttributes).forEach(function (attribute) { var value = staticAttributes[attribute]; var id = stringStore.id(attribute); var record = new AttributeRecord(); if (isBufferArgs(value)) { record.state = ATTRIB_STATE_POINTER; record.buffer = bufferState.getBuffer( bufferState.create(value, GL_ARRAY_BUFFER$1, false, true)); record.type = 0; } else { var buffer = bufferState.getBuffer(value); if (buffer) { record.state = ATTRIB_STATE_POINTER; record.buffer = buffer; record.type = 0; } else { if ('constant' in value) { var constant = value.constant; record.buffer = 'null'; record.state = ATTRIB_STATE_CONSTANT; if (typeof constant === 'number') { record.x = constant; } else { CUTE_COMPONENTS.forEach(function (c, i) { if (i < constant.length) { record[c] = constant[i]; } }); } } else { if (isBufferArgs(value.buffer)) { buffer = bufferState.getBuffer( bufferState.create(value.buffer, GL_ARRAY_BUFFER$1, false, true)); } else { buffer = bufferState.getBuffer(value.buffer); } var offset = value.offset | 0; var stride = value.stride | 0; var size = value.size | 0; var normalized = !!value.normalized; var type = 0; if ('type' in value) { type = glTypes[value.type]; } var divisor = value.divisor | 0; if ('divisor' in value) { } record.buffer = buffer; record.state = ATTRIB_STATE_POINTER; record.size = size; record.normalized = normalized; record.type = type || buffer.dtype; record.offset = offset; record.stride = stride; record.divisor = divisor; } } } attributeDefs[attribute] = createStaticDecl(function (env, scope) { var cache = env.attribCache; if (id in cache) { return cache[id] } var result = { isStream: false }; Object.keys(record).forEach(function (key) { result[key] = record[key]; }); if (record.buffer) { result.buffer = env.link(record.buffer); result.type = result.type || (result.buffer + '.dtype'); } cache[id] = result; return result }); }); Object.keys(dynamicAttributes).forEach(function (attribute) { var dyn = dynamicAttributes[attribute]; function appendAttributeCode (env, block) { var VALUE = env.invoke(block, dyn); var shared = env.shared; var constants = env.constants; var IS_BUFFER_ARGS = shared.isBufferArgs; var BUFFER_STATE = shared.buffer; // Perform validation on attribute // allocate names for result var result = { isStream: block.def(false) }; var defaultRecord = new AttributeRecord(); defaultRecord.state = ATTRIB_STATE_POINTER; Object.keys(defaultRecord).forEach(function (key) { result[key] = block.def('' + defaultRecord[key]); }); var BUFFER = result.buffer; var TYPE = result.type; block( 'if(', IS_BUFFER_ARGS, '(', VALUE, ')){', result.isStream, '=true;', BUFFER, '=', BUFFER_STATE, '.createStream(', GL_ARRAY_BUFFER$1, ',', VALUE, ');', TYPE, '=', BUFFER, '.dtype;', '}else{', BUFFER, '=', BUFFER_STATE, '.getBuffer(', VALUE, ');', 'if(', BUFFER, '){', TYPE, '=', BUFFER, '.dtype;', '}else if("constant" in ', VALUE, '){', result.state, '=', ATTRIB_STATE_CONSTANT, ';', 'if(typeof ' + VALUE + '.constant === "number"){', result[CUTE_COMPONENTS[0]], '=', VALUE, '.constant;', CUTE_COMPONENTS.slice(1).map(function (n) { return result[n] }).join('='), '=0;', '}else{', CUTE_COMPONENTS.map(function (name, i) { return ( result[name] + '=' + VALUE + '.constant.length>' + i + '?' + VALUE + '.constant[' + i + ']:0;' ) }).join(''), '}}else{', 'if(', IS_BUFFER_ARGS, '(', VALUE, '.buffer)){', BUFFER, '=', BUFFER_STATE, '.createStream(', GL_ARRAY_BUFFER$1, ',', VALUE, '.buffer);', '}else{', BUFFER, '=', BUFFER_STATE, '.getBuffer(', VALUE, '.buffer);', '}', TYPE, '="type" in ', VALUE, '?', constants.glTypes, '[', VALUE, '.type]:', BUFFER, '.dtype;', result.normalized, '=!!', VALUE, '.normalized;'); function emitReadRecord (name) { block(result[name], '=', VALUE, '.', name, '|0;'); } emitReadRecord('size'); emitReadRecord('offset'); emitReadRecord('stride'); emitReadRecord('divisor'); block('}}'); block.exit( 'if(', result.isStream, '){', BUFFER_STATE, '.destroyStream(', BUFFER, ');', '}'); return result } attributeDefs[attribute] = createDynamicDecl(dyn, appendAttributeCode); }); return attributeDefs } function parseContext (context) { var staticContext = context.static; var dynamicContext = context.dynamic; var result = {}; Object.keys(staticContext).forEach(function (name) { var value = staticContext[name]; result[name] = createStaticDecl(function (env, scope) { if (typeof value === 'number' || typeof value === 'boolean') { return '' + value } else { return env.link(value) } }); }); Object.keys(dynamicContext).forEach(function (name) { var dyn = dynamicContext[name]; result[name] = createDynamicDecl(dyn, function (env, scope) { return env.invoke(scope, dyn) }); }); return result } function parseArguments (options, attributes, uniforms, context, env) { var staticOptions = options.static; var dynamicOptions = options.dynamic; var framebuffer = parseFramebuffer(options, env); var viewportAndScissor = parseViewportScissor(options, framebuffer, env); var draw = parseDraw(options, env); var state = parseGLState(options, env); var shader = parseProgram(options, env); function copyBox (name) { var defn = viewportAndScissor[name]; if (defn) { state[name] = defn; } } copyBox(S_VIEWPORT); copyBox(propName(S_SCISSOR_BOX)); var dirty = Object.keys(state).length > 0; var result = { framebuffer: framebuffer, draw: draw, shader: shader, state: state, dirty: dirty }; result.profile = parseProfile(options, env); result.uniforms = parseUniforms(uniforms, env); result.attributes = parseAttributes(attributes, env); result.context = parseContext(context, env); return result } // =================================================== // =================================================== // COMMON UPDATE FUNCTIONS // =================================================== // =================================================== function emitContext (env, scope, context) { var shared = env.shared; var CONTEXT = shared.context; var contextEnter = env.scope(); Object.keys(context).forEach(function (name) { scope.save(CONTEXT, '.' + name); var defn = context[name]; contextEnter(CONTEXT, '.', name, '=', defn.append(env, scope), ';'); }); scope(contextEnter); } // =================================================== // =================================================== // COMMON DRAWING FUNCTIONS // =================================================== // =================================================== function emitPollFramebuffer (env, scope, framebuffer, skipCheck) { var shared = env.shared; var GL = shared.gl; var FRAMEBUFFER_STATE = shared.framebuffer; var EXT_DRAW_BUFFERS; if (extDrawBuffers) { EXT_DRAW_BUFFERS = scope.def(shared.extensions, '.webgl_draw_buffers'); } var constants = env.constants; var DRAW_BUFFERS = constants.drawBuffer; var BACK_BUFFER = constants.backBuffer; var NEXT; if (framebuffer) { NEXT = framebuffer.append(env, scope); } else { NEXT = scope.def(FRAMEBUFFER_STATE, '.next'); } if (!skipCheck) { scope('if(', NEXT, '!==', FRAMEBUFFER_STATE, '.cur){'); } scope( 'if(', NEXT, '){', GL, '.bindFramebuffer(', GL_FRAMEBUFFER$2, ',', NEXT, '.framebuffer);'); if (extDrawBuffers) { scope(EXT_DRAW_BUFFERS, '.drawBuffersWEBGL(', DRAW_BUFFERS, '[', NEXT, '.colorAttachments.length]);'); } scope('}else{', GL, '.bindFramebuffer(', GL_FRAMEBUFFER$2, ',null);'); if (extDrawBuffers) { scope(EXT_DRAW_BUFFERS, '.drawBuffersWEBGL(', BACK_BUFFER, ');'); } scope( '}', FRAMEBUFFER_STATE, '.cur=', NEXT, ';'); if (!skipCheck) { scope('}'); } } function emitPollState (env, scope, args) { var shared = env.shared; var GL = shared.gl; var CURRENT_VARS = env.current; var NEXT_VARS = env.next; var CURRENT_STATE = shared.current; var NEXT_STATE = shared.next; var block = env.cond(CURRENT_STATE, '.dirty'); GL_STATE_NAMES.forEach(function (prop) { var param = propName(prop); if (param in args.state) { return } var NEXT, CURRENT; if (param in NEXT_VARS) { NEXT = NEXT_VARS[param]; CURRENT = CURRENT_VARS[param]; var parts = loop(currentState[param].length, function (i) { return block.def(NEXT, '[', i, ']') }); block(env.cond(parts.map(function (p, i) { return p + '!==' + CURRENT + '[' + i + ']' }).join('||')) .then( GL, '.', GL_VARIABLES[param], '(', parts, ');', parts.map(function (p, i) { return CURRENT + '[' + i + ']=' + p }).join(';'), ';')); } else { NEXT = block.def(NEXT_STATE, '.', param); var ifte = env.cond(NEXT, '!==', CURRENT_STATE, '.', param); block(ifte); if (param in GL_FLAGS) { ifte( env.cond(NEXT) .then(GL, '.enable(', GL_FLAGS[param], ');') .else(GL, '.disable(', GL_FLAGS[param], ');'), CURRENT_STATE, '.', param, '=', NEXT, ';'); } else { ifte( GL, '.', GL_VARIABLES[param], '(', NEXT, ');', CURRENT_STATE, '.', param, '=', NEXT, ';'); } } }); if (Object.keys(args.state).length === 0) { block(CURRENT_STATE, '.dirty=false;'); } scope(block); } function emitSetOptions (env, scope, options, filter) { var shared = env.shared; var CURRENT_VARS = env.current; var CURRENT_STATE = shared.current; var GL = shared.gl; sortState(Object.keys(options)).forEach(function (param) { var defn = options[param]; if (filter && !filter(defn)) { return } var variable = defn.append(env, scope); if (GL_FLAGS[param]) { var flag = GL_FLAGS[param]; if (isStatic(defn)) { if (variable) { scope(GL, '.enable(', flag, ');'); } else { scope(GL, '.disable(', flag, ');'); } } else { scope(env.cond(variable) .then(GL, '.enable(', flag, ');') .else(GL, '.disable(', flag, ');')); } scope(CURRENT_STATE, '.', param, '=', variable, ';'); } else if (isArrayLike(variable)) { var CURRENT = CURRENT_VARS[param]; scope( GL, '.', GL_VARIABLES[param], '(', variable, ');', variable.map(function (v, i) { return CURRENT + '[' + i + ']=' + v }).join(';'), ';'); } else { scope( GL, '.', GL_VARIABLES[param], '(', variable, ');', CURRENT_STATE, '.', param, '=', variable, ';'); } }); } function injectExtensions (env, scope) { if (extInstancing) { env.instancing = scope.def( env.shared.extensions, '.angle_instanced_arrays'); } } function emitProfile (env, scope, args, useScope, incrementCounter) { var shared = env.shared; var STATS = env.stats; var CURRENT_STATE = shared.current; var TIMER = shared.timer; var profileArg = args.profile; function perfCounter () { if (typeof performance === 'undefined') { return 'Date.now()' } else { return 'performance.now()' } } var CPU_START, QUERY_COUNTER; function emitProfileStart (block) { CPU_START = scope.def(); block(CPU_START, '=', perfCounter(), ';'); if (typeof incrementCounter === 'string') { block(STATS, '.count+=', incrementCounter, ';'); } else { block(STATS, '.count++;'); } if (timer) { if (useScope) { QUERY_COUNTER = scope.def(); block(QUERY_COUNTER, '=', TIMER, '.getNumPendingQueries();'); } else { block(TIMER, '.beginQuery(', STATS, ');'); } } } function emitProfileEnd (block) { block(STATS, '.cpuTime+=', perfCounter(), '-', CPU_START, ';'); if (timer) { if (useScope) { block(TIMER, '.pushScopeStats(', QUERY_COUNTER, ',', TIMER, '.getNumPendingQueries(),', STATS, ');'); } else { block(TIMER, '.endQuery();'); } } } function scopeProfile (value) { var prev = scope.def(CURRENT_STATE, '.profile'); scope(CURRENT_STATE, '.profile=', value, ';'); scope.exit(CURRENT_STATE, '.profile=', prev, ';'); } var USE_PROFILE; if (profileArg) { if (isStatic(profileArg)) { if (profileArg.enable) { emitProfileStart(scope); emitProfileEnd(scope.exit); scopeProfile('true'); } else { scopeProfile('false'); } return } USE_PROFILE = profileArg.append(env, scope); scopeProfile(USE_PROFILE); } else { USE_PROFILE = scope.def(CURRENT_STATE, '.profile'); } var start = env.block(); emitProfileStart(start); scope('if(', USE_PROFILE, '){', start, '}'); var end = env.block(); emitProfileEnd(end); scope.exit('if(', USE_PROFILE, '){', end, '}'); } function emitAttributes (env, scope, args, attributes, filter) { var shared = env.shared; function typeLength (x) { switch (x) { case GL_FLOAT_VEC2: case GL_INT_VEC2: case GL_BOOL_VEC2: return 2 case GL_FLOAT_VEC3: case GL_INT_VEC3: case GL_BOOL_VEC3: return 3 case GL_FLOAT_VEC4: case GL_INT_VEC4: case GL_BOOL_VEC4: return 4 default: return 1 } } function emitBindAttribute (ATTRIBUTE, size, record) { var GL = shared.gl; var LOCATION = scope.def(ATTRIBUTE, '.location'); var BINDING = scope.def(shared.attributes, '[', LOCATION, ']'); var STATE = record.state; var BUFFER = record.buffer; var CONST_COMPONENTS = [ record.x, record.y, record.z, record.w ]; var COMMON_KEYS = [ 'buffer', 'normalized', 'offset', 'stride' ]; function emitBuffer () { scope( 'if(!', BINDING, '.buffer){', GL, '.enableVertexAttribArray(', LOCATION, ');}'); var TYPE = record.type; var SIZE; if (!record.size) { SIZE = size; } else { SIZE = scope.def(record.size, '||', size); } scope('if(', BINDING, '.type!==', TYPE, '||', BINDING, '.size!==', SIZE, '||', COMMON_KEYS.map(function (key) { return BINDING + '.' + key + '!==' + record[key] }).join('||'), '){', GL, '.bindBuffer(', GL_ARRAY_BUFFER$1, ',', BUFFER, '.buffer);', GL, '.vertexAttribPointer(', [ LOCATION, SIZE, TYPE, record.normalized, record.stride, record.offset ], ');', BINDING, '.type=', TYPE, ';', BINDING, '.size=', SIZE, ';', COMMON_KEYS.map(function (key) { return BINDING + '.' + key + '=' + record[key] + ';' }).join(''), '}'); if (extInstancing) { var DIVISOR = record.divisor; scope( 'if(', BINDING, '.divisor!==', DIVISOR, '){', env.instancing, '.vertexAttribDivisorANGLE(', [LOCATION, DIVISOR], ');', BINDING, '.divisor=', DIVISOR, ';}'); } } function emitConstant () { scope( 'if(', BINDING, '.buffer){', GL, '.disableVertexAttribArray(', LOCATION, ');', BINDING, '.buffer=null;', '}if(', CUTE_COMPONENTS.map(function (c, i) { return BINDING + '.' + c + '!==' + CONST_COMPONENTS[i] }).join('||'), '){', GL, '.vertexAttrib4f(', LOCATION, ',', CONST_COMPONENTS, ');', CUTE_COMPONENTS.map(function (c, i) { return BINDING + '.' + c + '=' + CONST_COMPONENTS[i] + ';' }).join(''), '}'); } if (STATE === ATTRIB_STATE_POINTER) { emitBuffer(); } else if (STATE === ATTRIB_STATE_CONSTANT) { emitConstant(); } else { scope('if(', STATE, '===', ATTRIB_STATE_POINTER, '){'); emitBuffer(); scope('}else{'); emitConstant(); scope('}'); } } attributes.forEach(function (attribute) { var name = attribute.name; var arg = args.attributes[name]; var record; if (arg) { if (!filter(arg)) { return } record = arg.append(env, scope); } else { if (!filter(SCOPE_DECL)) { return } var scopeAttrib = env.scopeAttrib(name); record = {}; Object.keys(new AttributeRecord()).forEach(function (key) { record[key] = scope.def(scopeAttrib, '.', key); }); } emitBindAttribute( env.link(attribute), typeLength(attribute.info.type), record); }); } function emitUniforms (env, scope, args, uniforms, filter) { var shared = env.shared; var GL = shared.gl; var infix; for (var i = 0; i < uniforms.length; ++i) { var uniform = uniforms[i]; var name = uniform.name; var type = uniform.info.type; var arg = args.uniforms[name]; var UNIFORM = env.link(uniform); var LOCATION = UNIFORM + '.location'; var VALUE; if (arg) { if (!filter(arg)) { continue } if (isStatic(arg)) { var value = arg.value; if (type === GL_SAMPLER_2D || type === GL_SAMPLER_CUBE) { var TEX_VALUE = env.link(value._texture || value.color[0]._texture); scope(GL, '.uniform1i(', LOCATION, ',', TEX_VALUE + '.bind());'); scope.exit(TEX_VALUE, '.unbind();'); } else if ( type === GL_FLOAT_MAT2 || type === GL_FLOAT_MAT3 || type === GL_FLOAT_MAT4) { var MAT_VALUE = env.global.def('new Float32Array([' + Array.prototype.slice.call(value) + '])'); var dim = 2; if (type === GL_FLOAT_MAT3) { dim = 3; } else if (type === GL_FLOAT_MAT4) { dim = 4; } scope( GL, '.uniformMatrix', dim, 'fv(', LOCATION, ',false,', MAT_VALUE, ');'); } else { switch (type) { case GL_FLOAT$7: infix = '1f'; break case GL_FLOAT_VEC2: infix = '2f'; break case GL_FLOAT_VEC3: infix = '3f'; break case GL_FLOAT_VEC4: infix = '4f'; break case GL_BOOL: infix = '1i'; break case GL_INT$2: infix = '1i'; break case GL_BOOL_VEC2: infix = '2i'; break case GL_INT_VEC2: infix = '2i'; break case GL_BOOL_VEC3: infix = '3i'; break case GL_INT_VEC3: infix = '3i'; break case GL_BOOL_VEC4: infix = '4i'; break case GL_INT_VEC4: infix = '4i'; break } scope(GL, '.uniform', infix, '(', LOCATION, ',', isArrayLike(value) ? Array.prototype.slice.call(value) : value, ');'); } continue } else { VALUE = arg.append(env, scope); } } else { if (!filter(SCOPE_DECL)) { continue } VALUE = scope.def(shared.uniforms, '[', stringStore.id(name), ']'); } if (type === GL_SAMPLER_2D) { scope( 'if(', VALUE, '&&', VALUE, '._reglType==="framebuffer"){', VALUE, '=', VALUE, '.color[0];', '}'); } else if (type === GL_SAMPLER_CUBE) { scope( 'if(', VALUE, '&&', VALUE, '._reglType==="framebufferCube"){', VALUE, '=', VALUE, '.color[0];', '}'); } // perform type validation var unroll = 1; switch (type) { case GL_SAMPLER_2D: case GL_SAMPLER_CUBE: var TEX = scope.def(VALUE, '._texture'); scope(GL, '.uniform1i(', LOCATION, ',', TEX, '.bind());'); scope.exit(TEX, '.unbind();'); continue case GL_INT$2: case GL_BOOL: infix = '1i'; break case GL_INT_VEC2: case GL_BOOL_VEC2: infix = '2i'; unroll = 2; break case GL_INT_VEC3: case GL_BOOL_VEC3: infix = '3i'; unroll = 3; break case GL_INT_VEC4: case GL_BOOL_VEC4: infix = '4i'; unroll = 4; break case GL_FLOAT$7: infix = '1f'; break case GL_FLOAT_VEC2: infix = '2f'; unroll = 2; break case GL_FLOAT_VEC3: infix = '3f'; unroll = 3; break case GL_FLOAT_VEC4: infix = '4f'; unroll = 4; break case GL_FLOAT_MAT2: infix = 'Matrix2fv'; break case GL_FLOAT_MAT3: infix = 'Matrix3fv'; break case GL_FLOAT_MAT4: infix = 'Matrix4fv'; break } scope(GL, '.uniform', infix, '(', LOCATION, ','); if (infix.charAt(0) === 'M') { var matSize = Math.pow(type - GL_FLOAT_MAT2 + 2, 2); var STORAGE = env.global.def('new Float32Array(', matSize, ')'); scope( 'false,(Array.isArray(', VALUE, ')||', VALUE, ' instanceof Float32Array)?', VALUE, ':(', loop(matSize, function (i) { return STORAGE + '[' + i + ']=' + VALUE + '[' + i + ']' }), ',', STORAGE, ')'); } else if (unroll > 1) { scope(loop(unroll, function (i) { return VALUE + '[' + i + ']' })); } else { scope(VALUE); } scope(');'); } } function emitDraw (env, outer, inner, args) { var shared = env.shared; var GL = shared.gl; var DRAW_STATE = shared.draw; var drawOptions = args.draw; function emitElements () { var defn = drawOptions.elements; var ELEMENTS; var scope = outer; if (defn) { if ((defn.contextDep && args.contextDynamic) || defn.propDep) { scope = inner; } ELEMENTS = defn.append(env, scope); } else { ELEMENTS = scope.def(DRAW_STATE, '.', S_ELEMENTS); } if (ELEMENTS) { scope( 'if(' + ELEMENTS + ')' + GL + '.bindBuffer(' + GL_ELEMENT_ARRAY_BUFFER$1 + ',' + ELEMENTS + '.buffer.buffer);'); } return ELEMENTS } function emitCount () { var defn = drawOptions.count; var COUNT; var scope = outer; if (defn) { if ((defn.contextDep && args.contextDynamic) || defn.propDep) { scope = inner; } COUNT = defn.append(env, scope); } else { COUNT = scope.def(DRAW_STATE, '.', S_COUNT); } return COUNT } var ELEMENTS = emitElements(); function emitValue (name) { var defn = drawOptions[name]; if (defn) { if ((defn.contextDep && args.contextDynamic) || defn.propDep) { return defn.append(env, inner) } else { return defn.append(env, outer) } } else { return outer.def(DRAW_STATE, '.', name) } } var PRIMITIVE = emitValue(S_PRIMITIVE); var OFFSET = emitValue(S_OFFSET); var COUNT = emitCount(); if (typeof COUNT === 'number') { if (COUNT === 0) { return } } else { inner('if(', COUNT, '){'); inner.exit('}'); } var INSTANCES, EXT_INSTANCING; if (extInstancing) { INSTANCES = emitValue(S_INSTANCES); EXT_INSTANCING = env.instancing; } var ELEMENT_TYPE = ELEMENTS + '.type'; var elementsStatic = drawOptions.elements && isStatic(drawOptions.elements); function emitInstancing () { function drawElements () { inner(EXT_INSTANCING, '.drawElementsInstancedANGLE(', [ PRIMITIVE, COUNT, ELEMENT_TYPE, OFFSET + '<<((' + ELEMENT_TYPE + '-' + GL_UNSIGNED_BYTE$7 + ')>>1)', INSTANCES ], ');'); } function drawArrays () { inner(EXT_INSTANCING, '.drawArraysInstancedANGLE(', [PRIMITIVE, OFFSET, COUNT, INSTANCES], ');'); } if (ELEMENTS) { if (!elementsStatic) { inner('if(', ELEMENTS, '){'); drawElements(); inner('}else{'); drawArrays(); inner('}'); } else { drawElements(); } } else { drawArrays(); } } function emitRegular () { function drawElements () { inner(GL + '.drawElements(' + [ PRIMITIVE, COUNT, ELEMENT_TYPE, OFFSET + '<<((' + ELEMENT_TYPE + '-' + GL_UNSIGNED_BYTE$7 + ')>>1)' ] + ');'); } function drawArrays () { inner(GL + '.drawArrays(' + [PRIMITIVE, OFFSET, COUNT] + ');'); } if (ELEMENTS) { if (!elementsStatic) { inner('if(', ELEMENTS, '){'); drawElements(); inner('}else{'); drawArrays(); inner('}'); } else { drawElements(); } } else { drawArrays(); } } if (extInstancing && (typeof INSTANCES !== 'number' || INSTANCES >= 0)) { if (typeof INSTANCES === 'string') { inner('if(', INSTANCES, '>0){'); emitInstancing(); inner('}else if(', INSTANCES, '<0){'); emitRegular(); inner('}'); } else { emitInstancing(); } } else { emitRegular(); } } function createBody (emitBody, parentEnv, args, program, count) { var env = createREGLEnvironment(); var scope = env.proc('body', count); if (extInstancing) { env.instancing = scope.def( env.shared.extensions, '.angle_instanced_arrays'); } emitBody(env, scope, args, program); return env.compile().body } // =================================================== // =================================================== // DRAW PROC // =================================================== // =================================================== function emitDrawBody (env, draw, args, program) { injectExtensions(env, draw); emitAttributes(env, draw, args, program.attributes, function () { return true }); emitUniforms(env, draw, args, program.uniforms, function () { return true }); emitDraw(env, draw, draw, args); } function emitDrawProc (env, args) { var draw = env.proc('draw', 1); injectExtensions(env, draw); emitContext(env, draw, args.context); emitPollFramebuffer(env, draw, args.framebuffer); emitPollState(env, draw, args); emitSetOptions(env, draw, args.state); emitProfile(env, draw, args, false, true); var program = args.shader.progVar.append(env, draw); draw(env.shared.gl, '.useProgram(', program, '.program);'); if (args.shader.program) { emitDrawBody(env, draw, args, args.shader.program); } else { var drawCache = env.global.def('{}'); var PROG_ID = draw.def(program, '.id'); var CACHED_PROC = draw.def(drawCache, '[', PROG_ID, ']'); draw( env.cond(CACHED_PROC) .then(CACHED_PROC, '.call(this,a0);') .else( CACHED_PROC, '=', drawCache, '[', PROG_ID, ']=', env.link(function (program) { return createBody(emitDrawBody, env, args, program, 1) }), '(', program, ');', CACHED_PROC, '.call(this,a0);')); } if (Object.keys(args.state).length > 0) { draw(env.shared.current, '.dirty=true;'); } } // =================================================== // =================================================== // BATCH PROC // =================================================== // =================================================== function emitBatchDynamicShaderBody (env, scope, args, program) { env.batchId = 'a1'; injectExtensions(env, scope); function all () { return true } emitAttributes(env, scope, args, program.attributes, all); emitUniforms(env, scope, args, program.uniforms, all); emitDraw(env, scope, scope, args); } function emitBatchBody (env, scope, args, program) { injectExtensions(env, scope); var contextDynamic = args.contextDep; var BATCH_ID = scope.def(); var PROP_LIST = 'a0'; var NUM_PROPS = 'a1'; var PROPS = scope.def(); env.shared.props = PROPS; env.batchId = BATCH_ID; var outer = env.scope(); var inner = env.scope(); scope( outer.entry, 'for(', BATCH_ID, '=0;', BATCH_ID, '<', NUM_PROPS, ';++', BATCH_ID, '){', PROPS, '=', PROP_LIST, '[', BATCH_ID, '];', inner, '}', outer.exit); function isInnerDefn (defn) { return ((defn.contextDep && contextDynamic) || defn.propDep) } function isOuterDefn (defn) { return !isInnerDefn(defn) } if (args.needsContext) { emitContext(env, inner, args.context); } if (args.needsFramebuffer) { emitPollFramebuffer(env, inner, args.framebuffer); } emitSetOptions(env, inner, args.state, isInnerDefn); if (args.profile && isInnerDefn(args.profile)) { emitProfile(env, inner, args, false, true); } if (!program) { var progCache = env.global.def('{}'); var PROGRAM = args.shader.progVar.append(env, inner); var PROG_ID = inner.def(PROGRAM, '.id'); var CACHED_PROC = inner.def(progCache, '[', PROG_ID, ']'); inner( env.shared.gl, '.useProgram(', PROGRAM, '.program);', 'if(!', CACHED_PROC, '){', CACHED_PROC, '=', progCache, '[', PROG_ID, ']=', env.link(function (program) { return createBody( emitBatchDynamicShaderBody, env, args, program, 2) }), '(', PROGRAM, ');}', CACHED_PROC, '.call(this,a0[', BATCH_ID, '],', BATCH_ID, ');'); } else { emitAttributes(env, outer, args, program.attributes, isOuterDefn); emitAttributes(env, inner, args, program.attributes, isInnerDefn); emitUniforms(env, outer, args, program.uniforms, isOuterDefn); emitUniforms(env, inner, args, program.uniforms, isInnerDefn); emitDraw(env, outer, inner, args); } } function emitBatchProc (env, args) { var batch = env.proc('batch', 2); env.batchId = '0'; injectExtensions(env, batch); // Check if any context variables depend on props var contextDynamic = false; var needsContext = true; Object.keys(args.context).forEach(function (name) { contextDynamic = contextDynamic || args.context[name].propDep; }); if (!contextDynamic) { emitContext(env, batch, args.context); needsContext = false; } // framebuffer state affects framebufferWidth/height context vars var framebuffer = args.framebuffer; var needsFramebuffer = false; if (framebuffer) { if (framebuffer.propDep) { contextDynamic = needsFramebuffer = true; } else if (framebuffer.contextDep && contextDynamic) { needsFramebuffer = true; } if (!needsFramebuffer) { emitPollFramebuffer(env, batch, framebuffer); } } else { emitPollFramebuffer(env, batch, null); } // viewport is weird because it can affect context vars if (args.state.viewport && args.state.viewport.propDep) { contextDynamic = true; } function isInnerDefn (defn) { return (defn.contextDep && contextDynamic) || defn.propDep } // set webgl options emitPollState(env, batch, args); emitSetOptions(env, batch, args.state, function (defn) { return !isInnerDefn(defn) }); if (!args.profile || !isInnerDefn(args.profile)) { emitProfile(env, batch, args, false, 'a1'); } // Save these values to args so that the batch body routine can use them args.contextDep = contextDynamic; args.needsContext = needsContext; args.needsFramebuffer = needsFramebuffer; // determine if shader is dynamic var progDefn = args.shader.progVar; if ((progDefn.contextDep && contextDynamic) || progDefn.propDep) { emitBatchBody( env, batch, args, null); } else { var PROGRAM = progDefn.append(env, batch); batch(env.shared.gl, '.useProgram(', PROGRAM, '.program);'); if (args.shader.program) { emitBatchBody( env, batch, args, args.shader.program); } else { var batchCache = env.global.def('{}'); var PROG_ID = batch.def(PROGRAM, '.id'); var CACHED_PROC = batch.def(batchCache, '[', PROG_ID, ']'); batch( env.cond(CACHED_PROC) .then(CACHED_PROC, '.call(this,a0,a1);') .else( CACHED_PROC, '=', batchCache, '[', PROG_ID, ']=', env.link(function (program) { return createBody(emitBatchBody, env, args, program, 2) }), '(', PROGRAM, ');', CACHED_PROC, '.call(this,a0,a1);')); } } if (Object.keys(args.state).length > 0) { batch(env.shared.current, '.dirty=true;'); } } // =================================================== // =================================================== // SCOPE COMMAND // =================================================== // =================================================== function emitScopeProc (env, args) { var scope = env.proc('scope', 3); env.batchId = 'a2'; var shared = env.shared; var CURRENT_STATE = shared.current; emitContext(env, scope, args.context); if (args.framebuffer) { args.framebuffer.append(env, scope); } sortState(Object.keys(args.state)).forEach(function (name) { var defn = args.state[name]; var value = defn.append(env, scope); if (isArrayLike(value)) { value.forEach(function (v, i) { scope.set(env.next[name], '[' + i + ']', v); }); } else { scope.set(shared.next, '.' + name, value); } }); emitProfile(env, scope, args, true, true) ;[S_ELEMENTS, S_OFFSET, S_COUNT, S_INSTANCES, S_PRIMITIVE].forEach( function (opt) { var variable = args.draw[opt]; if (!variable) { return } scope.set(shared.draw, '.' + opt, '' + variable.append(env, scope)); }); Object.keys(args.uniforms).forEach(function (opt) { scope.set( shared.uniforms, '[' + stringStore.id(opt) + ']', args.uniforms[opt].append(env, scope)); }); Object.keys(args.attributes).forEach(function (name) { var record = args.attributes[name].append(env, scope); var scopeAttrib = env.scopeAttrib(name); Object.keys(new AttributeRecord()).forEach(function (prop) { scope.set(scopeAttrib, '.' + prop, record[prop]); }); }); function saveShader (name) { var shader = args.shader[name]; if (shader) { scope.set(shared.shader, '.' + name, shader.append(env, scope)); } } saveShader(S_VERT); saveShader(S_FRAG); if (Object.keys(args.state).length > 0) { scope(CURRENT_STATE, '.dirty=true;'); scope.exit(CURRENT_STATE, '.dirty=true;'); } scope('a1(', env.shared.context, ',a0,', env.batchId, ');'); } function isDynamicObject (object) { if (typeof object !== 'object' || isArrayLike(object)) { return } var props = Object.keys(object); for (var i = 0; i < props.length; ++i) { if (dynamic.isDynamic(object[props[i]])) { return true } } return false } function splatObject (env, options, name) { var object = options.static[name]; if (!object || !isDynamicObject(object)) { return } var globals = env.global; var keys = Object.keys(object); var thisDep = false; var contextDep = false; var propDep = false; var objectRef = env.global.def('{}'); keys.forEach(function (key) { var value = object[key]; if (dynamic.isDynamic(value)) { if (typeof value === 'function') { value = object[key] = dynamic.unbox(value); } var deps = createDynamicDecl(value, null); thisDep = thisDep || deps.thisDep; propDep = propDep || deps.propDep; contextDep = contextDep || deps.contextDep; } else { globals(objectRef, '.', key, '='); switch (typeof value) { case 'number': globals(value); break case 'string': globals('"', value, '"'); break case 'object': if (Array.isArray(value)) { globals('[', value.join(), ']'); } break default: globals(env.link(value)); break } globals(';'); } }); function appendBlock (env, block) { keys.forEach(function (key) { var value = object[key]; if (!dynamic.isDynamic(value)) { return } var ref = env.invoke(block, value); block(objectRef, '.', key, '=', ref, ';'); }); } options.dynamic[name] = new dynamic.DynamicVariable(DYN_THUNK, { thisDep: thisDep, contextDep: contextDep, propDep: propDep, ref: objectRef, append: appendBlock }); delete options.static[name]; } // =========================================================================== // =========================================================================== // MAIN DRAW COMMAND // =========================================================================== // =========================================================================== function compileCommand (options, attributes, uniforms, context, stats) { var env = createREGLEnvironment(); // link stats, so that we can easily access it in the program. env.stats = env.link(stats); // splat options and attributes to allow for dynamic nested properties Object.keys(attributes.static).forEach(function (key) { splatObject(env, attributes, key); }); NESTED_OPTIONS.forEach(function (name) { splatObject(env, options, name); }); var args = parseArguments(options, attributes, uniforms, context, env); emitDrawProc(env, args); emitScopeProc(env, args); emitBatchProc(env, args); return env.compile() } // =========================================================================== // =========================================================================== // POLL / REFRESH // =========================================================================== // =========================================================================== return { next: nextState, current: currentState, procs: (function () { var env = createREGLEnvironment(); var poll = env.proc('poll'); var refresh = env.proc('refresh'); var common = env.block(); poll(common); refresh(common); var shared = env.shared; var GL = shared.gl; var NEXT_STATE = shared.next; var CURRENT_STATE = shared.current; common(CURRENT_STATE, '.dirty=false;'); emitPollFramebuffer(env, poll); emitPollFramebuffer(env, refresh, null, true); // Refresh updates all attribute state changes var INSTANCING; if (extInstancing) { INSTANCING = env.link(extInstancing); } for (var i = 0; i < limits.maxAttributes; ++i) { var BINDING = refresh.def(shared.attributes, '[', i, ']'); var ifte = env.cond(BINDING, '.buffer'); ifte.then( GL, '.enableVertexAttribArray(', i, ');', GL, '.bindBuffer(', GL_ARRAY_BUFFER$1, ',', BINDING, '.buffer.buffer);', GL, '.vertexAttribPointer(', i, ',', BINDING, '.size,', BINDING, '.type,', BINDING, '.normalized,', BINDING, '.stride,', BINDING, '.offset);' ).else( GL, '.disableVertexAttribArray(', i, ');', GL, '.vertexAttrib4f(', i, ',', BINDING, '.x,', BINDING, '.y,', BINDING, '.z,', BINDING, '.w);', BINDING, '.buffer=null;'); refresh(ifte); if (extInstancing) { refresh( INSTANCING, '.vertexAttribDivisorANGLE(', i, ',', BINDING, '.divisor);'); } } Object.keys(GL_FLAGS).forEach(function (flag) { var cap = GL_FLAGS[flag]; var NEXT = common.def(NEXT_STATE, '.', flag); var block = env.block(); block('if(', NEXT, '){', GL, '.enable(', cap, ')}else{', GL, '.disable(', cap, ')}', CURRENT_STATE, '.', flag, '=', NEXT, ';'); refresh(block); poll( 'if(', NEXT, '!==', CURRENT_STATE, '.', flag, '){', block, '}'); }); Object.keys(GL_VARIABLES).forEach(function (name) { var func = GL_VARIABLES[name]; var init = currentState[name]; var NEXT, CURRENT; var block = env.block(); block(GL, '.', func, '('); if (isArrayLike(init)) { var n = init.length; NEXT = env.global.def(NEXT_STATE, '.', name); CURRENT = env.global.def(CURRENT_STATE, '.', name); block( loop(n, function (i) { return NEXT + '[' + i + ']' }), ');', loop(n, function (i) { return CURRENT + '[' + i + ']=' + NEXT + '[' + i + '];' }).join('')); poll( 'if(', loop(n, function (i) { return NEXT + '[' + i + ']!==' + CURRENT + '[' + i + ']' }).join('||'), '){', block, '}'); } else { NEXT = common.def(NEXT_STATE, '.', name); CURRENT = common.def(CURRENT_STATE, '.', name); block( NEXT, ');', CURRENT_STATE, '.', name, '=', NEXT, ';'); poll( 'if(', NEXT, '!==', CURRENT, '){', block, '}'); } refresh(block); }); return env.compile() })(), compile: compileCommand } } function stats () { return { bufferCount: 0, elementsCount: 0, framebufferCount: 0, shaderCount: 0, textureCount: 0, cubeCount: 0, renderbufferCount: 0, maxTextureUnits: 0 } } var GL_QUERY_RESULT_EXT = 0x8866; var GL_QUERY_RESULT_AVAILABLE_EXT = 0x8867; var GL_TIME_ELAPSED_EXT = 0x88BF; var createTimer = function (gl, extensions) { if (!extensions.ext_disjoint_timer_query) { return null } // QUERY POOL BEGIN var queryPool = []; function allocQuery () { return queryPool.pop() || extensions.ext_disjoint_timer_query.createQueryEXT() } function freeQuery (query) { queryPool.push(query); } // QUERY POOL END var pendingQueries = []; function beginQuery (stats) { var query = allocQuery(); extensions.ext_disjoint_timer_query.beginQueryEXT(GL_TIME_ELAPSED_EXT, query); pendingQueries.push(query); pushScopeStats(pendingQueries.length - 1, pendingQueries.length, stats); } function endQuery () { extensions.ext_disjoint_timer_query.endQueryEXT(GL_TIME_ELAPSED_EXT); } // // Pending stats pool. // function PendingStats () { this.startQueryIndex = -1; this.endQueryIndex = -1; this.sum = 0; this.stats = null; } var pendingStatsPool = []; function allocPendingStats () { return pendingStatsPool.pop() || new PendingStats() } function freePendingStats (pendingStats) { pendingStatsPool.push(pendingStats); } // Pending stats pool end var pendingStats = []; function pushScopeStats (start, end, stats) { var ps = allocPendingStats(); ps.startQueryIndex = start; ps.endQueryIndex = end; ps.sum = 0; ps.stats = stats; pendingStats.push(ps); } // we should call this at the beginning of the frame, // in order to update gpuTime var timeSum = []; var queryPtr = []; function update () { var ptr, i; var n = pendingQueries.length; if (n === 0) { return } // Reserve space queryPtr.length = Math.max(queryPtr.length, n + 1); timeSum.length = Math.max(timeSum.length, n + 1); timeSum[0] = 0; queryPtr[0] = 0; // Update all pending timer queries var queryTime = 0; ptr = 0; for (i = 0; i < pendingQueries.length; ++i) { var query = pendingQueries[i]; if (extensions.ext_disjoint_timer_query.getQueryObjectEXT(query, GL_QUERY_RESULT_AVAILABLE_EXT)) { queryTime += extensions.ext_disjoint_timer_query.getQueryObjectEXT(query, GL_QUERY_RESULT_EXT); freeQuery(query); } else { pendingQueries[ptr++] = query; } timeSum[i + 1] = queryTime; queryPtr[i + 1] = ptr; } pendingQueries.length = ptr; // Update all pending stat queries ptr = 0; for (i = 0; i < pendingStats.length; ++i) { var stats = pendingStats[i]; var start = stats.startQueryIndex; var end = stats.endQueryIndex; stats.sum += timeSum[end] - timeSum[start]; var startPtr = queryPtr[start]; var endPtr = queryPtr[end]; if (endPtr === startPtr) { stats.stats.gpuTime += stats.sum / 1e6; freePendingStats(stats); } else { stats.startQueryIndex = startPtr; stats.endQueryIndex = endPtr; pendingStats[ptr++] = stats; } } pendingStats.length = ptr; } return { beginQuery: beginQuery, endQuery: endQuery, pushScopeStats: pushScopeStats, update: update, getNumPendingQueries: function () { return pendingQueries.length }, clear: function () { queryPool.push.apply(queryPool, pendingQueries); for (var i = 0; i < queryPool.length; i++) { extensions.ext_disjoint_timer_query.deleteQueryEXT(queryPool[i]); } pendingQueries.length = 0; queryPool.length = 0; }, restore: function () { pendingQueries.length = 0; queryPool.length = 0; } } }; var GL_COLOR_BUFFER_BIT = 16384; var GL_DEPTH_BUFFER_BIT = 256; var GL_STENCIL_BUFFER_BIT = 1024; var GL_ARRAY_BUFFER = 34962; var CONTEXT_LOST_EVENT = 'webglcontextlost'; var CONTEXT_RESTORED_EVENT = 'webglcontextrestored'; var DYN_PROP = 1; var DYN_CONTEXT = 2; var DYN_STATE = 3; function find (haystack, needle) { for (var i = 0; i < haystack.length; ++i) { if (haystack[i] === needle) { return i } } return -1 } function wrapREGL (args) { var config = parseArgs(args); if (!config) { return null } var gl = config.gl; var glAttributes = gl.getContextAttributes(); var contextLost = gl.isContextLost(); var extensionState = createExtensionCache(gl, config); if (!extensionState) { return null } var stringStore = createStringStore(); var stats$$1 = stats(); var extensions = extensionState.extensions; var timer = createTimer(gl, extensions); var START_TIME = clock(); var WIDTH = gl.drawingBufferWidth; var HEIGHT = gl.drawingBufferHeight; var contextState = { tick: 0, time: 0, viewportWidth: WIDTH, viewportHeight: HEIGHT, framebufferWidth: WIDTH, framebufferHeight: HEIGHT, drawingBufferWidth: WIDTH, drawingBufferHeight: HEIGHT, pixelRatio: config.pixelRatio }; var uniformState = {}; var drawState = { elements: null, primitive: 4, // GL_TRIANGLES count: -1, offset: 0, instances: -1 }; var limits = wrapLimits(gl, extensions); var attributeState = wrapAttributeState( gl, extensions, limits, stringStore); var bufferState = wrapBufferState( gl, stats$$1, config, attributeState); var elementState = wrapElementsState(gl, extensions, bufferState, stats$$1); var shaderState = wrapShaderState(gl, stringStore, stats$$1, config); var textureState = createTextureSet( gl, extensions, limits, function () { core.procs.poll(); }, contextState, stats$$1, config); var renderbufferState = wrapRenderbuffers(gl, extensions, limits, stats$$1, config); var framebufferState = wrapFBOState( gl, extensions, limits, textureState, renderbufferState, stats$$1); var core = reglCore( gl, stringStore, extensions, limits, bufferState, elementState, textureState, framebufferState, uniformState, attributeState, shaderState, drawState, contextState, timer, config); var readPixels = wrapReadPixels( gl, framebufferState, core.procs.poll, contextState, glAttributes, extensions, limits); var nextState = core.next; var canvas = gl.canvas; var rafCallbacks = []; var lossCallbacks = []; var restoreCallbacks = []; var destroyCallbacks = [config.onDestroy]; var activeRAF = null; function handleRAF () { if (rafCallbacks.length === 0) { if (timer) { timer.update(); } activeRAF = null; return } // schedule next animation frame activeRAF = raf.next(handleRAF); // poll for changes poll(); // fire a callback for all pending rafs for (var i = rafCallbacks.length - 1; i >= 0; --i) { var cb = rafCallbacks[i]; if (cb) { cb(contextState, null, 0); } } // flush all pending webgl calls gl.flush(); // poll GPU timers *after* gl.flush so we don't delay command dispatch if (timer) { timer.update(); } } function startRAF () { if (!activeRAF && rafCallbacks.length > 0) { activeRAF = raf.next(handleRAF); } } function stopRAF () { if (activeRAF) { raf.cancel(handleRAF); activeRAF = null; } } function handleContextLoss (event) { event.preventDefault(); // set context lost flag contextLost = true; // pause request animation frame stopRAF(); // lose context lossCallbacks.forEach(function (cb) { cb(); }); } function handleContextRestored (event) { // clear error code gl.getError(); // clear context lost flag contextLost = false; // refresh state extensionState.restore(); shaderState.restore(); bufferState.restore(); textureState.restore(); renderbufferState.restore(); framebufferState.restore(); if (timer) { timer.restore(); } // refresh state core.procs.refresh(); // restart RAF startRAF(); // restore context restoreCallbacks.forEach(function (cb) { cb(); }); } if (canvas) { canvas.addEventListener(CONTEXT_LOST_EVENT, handleContextLoss, false); canvas.addEventListener(CONTEXT_RESTORED_EVENT, handleContextRestored, false); } function destroy () { rafCallbacks.length = 0; stopRAF(); if (canvas) { canvas.removeEventListener(CONTEXT_LOST_EVENT, handleContextLoss); canvas.removeEventListener(CONTEXT_RESTORED_EVENT, handleContextRestored); } shaderState.clear(); framebufferState.clear(); renderbufferState.clear(); textureState.clear(); elementState.clear(); bufferState.clear(); if (timer) { timer.clear(); } destroyCallbacks.forEach(function (cb) { cb(); }); } function compileProcedure (options) { function flattenNestedOptions (options) { var result = extend({}, options); delete result.uniforms; delete result.attributes; delete result.context; if ('stencil' in result && result.stencil.op) { result.stencil.opBack = result.stencil.opFront = result.stencil.op; delete result.stencil.op; } function merge (name) { if (name in result) { var child = result[name]; delete result[name]; Object.keys(child).forEach(function (prop) { result[name + '.' + prop] = child[prop]; }); } } merge('blend'); merge('depth'); merge('cull'); merge('stencil'); merge('polygonOffset'); merge('scissor'); merge('sample'); return result } function separateDynamic (object) { var staticItems = {}; var dynamicItems = {}; Object.keys(object).forEach(function (option) { var value = object[option]; if (dynamic.isDynamic(value)) { dynamicItems[option] = dynamic.unbox(value, option); } else { staticItems[option] = value; } }); return { dynamic: dynamicItems, static: staticItems } } // Treat context variables separate from other dynamic variables var context = separateDynamic(options.context || {}); var uniforms = separateDynamic(options.uniforms || {}); var attributes = separateDynamic(options.attributes || {}); var opts = separateDynamic(flattenNestedOptions(options)); var stats$$1 = { gpuTime: 0.0, cpuTime: 0.0, count: 0 }; var compiled = core.compile(opts, attributes, uniforms, context, stats$$1); var draw = compiled.draw; var batch = compiled.batch; var scope = compiled.scope; // FIXME: we should modify code generation for batch commands so this // isn't necessary var EMPTY_ARRAY = []; function reserve (count) { while (EMPTY_ARRAY.length < count) { EMPTY_ARRAY.push(null); } return EMPTY_ARRAY } function REGLCommand (args, body) { var i; if (contextLost) { } if (typeof args === 'function') { return scope.call(this, null, args, 0) } else if (typeof body === 'function') { if (typeof args === 'number') { for (i = 0; i < args; ++i) { scope.call(this, null, body, i); } return } else if (Array.isArray(args)) { for (i = 0; i < args.length; ++i) { scope.call(this, args[i], body, i); } return } else { return scope.call(this, args, body, 0) } } else if (typeof args === 'number') { if (args > 0) { return batch.call(this, reserve(args | 0), args | 0) } } else if (Array.isArray(args)) { if (args.length) { return batch.call(this, args, args.length) } } else { return draw.call(this, args) } } return extend(REGLCommand, { stats: stats$$1 }) } var setFBO = framebufferState.setFBO = compileProcedure({ framebuffer: dynamic.define.call(null, DYN_PROP, 'framebuffer') }); function clearImpl (_, options) { var clearFlags = 0; core.procs.poll(); var c = options.color; if (c) { gl.clearColor(+c[0] || 0, +c[1] || 0, +c[2] || 0, +c[3] || 0); clearFlags |= GL_COLOR_BUFFER_BIT; } if ('depth' in options) { gl.clearDepth(+options.depth); clearFlags |= GL_DEPTH_BUFFER_BIT; } if ('stencil' in options) { gl.clearStencil(options.stencil | 0); clearFlags |= GL_STENCIL_BUFFER_BIT; } gl.clear(clearFlags); } function clear (options) { if ('framebuffer' in options) { if (options.framebuffer && options.framebuffer_reglType === 'framebufferCube') { for (var i = 0; i < 6; ++i) { setFBO(extend({ framebuffer: options.framebuffer.faces[i] }, options), clearImpl); } } else { setFBO(options, clearImpl); } } else { clearImpl(null, options); } } function frame (cb) { rafCallbacks.push(cb); function cancel () { // FIXME: should we check something other than equals cb here? // what if a user calls frame twice with the same callback... // var i = find(rafCallbacks, cb); function pendingCancel () { var index = find(rafCallbacks, pendingCancel); rafCallbacks[index] = rafCallbacks[rafCallbacks.length - 1]; rafCallbacks.length -= 1; if (rafCallbacks.length <= 0) { stopRAF(); } } rafCallbacks[i] = pendingCancel; } startRAF(); return { cancel: cancel } } // poll viewport function pollViewport () { var viewport = nextState.viewport; var scissorBox = nextState.scissor_box; viewport[0] = viewport[1] = scissorBox[0] = scissorBox[1] = 0; contextState.viewportWidth = contextState.framebufferWidth = contextState.drawingBufferWidth = viewport[2] = scissorBox[2] = gl.drawingBufferWidth; contextState.viewportHeight = contextState.framebufferHeight = contextState.drawingBufferHeight = viewport[3] = scissorBox[3] = gl.drawingBufferHeight; } function poll () { contextState.tick += 1; contextState.time = now(); pollViewport(); core.procs.poll(); } function refresh () { pollViewport(); core.procs.refresh(); if (timer) { timer.update(); } } function now () { return (clock() - START_TIME) / 1000.0 } refresh(); function addListener (event, callback) { var callbacks; switch (event) { case 'frame': return frame(callback) case 'lost': callbacks = lossCallbacks; break case 'restore': callbacks = restoreCallbacks; break case 'destroy': callbacks = destroyCallbacks; break default: } callbacks.push(callback); return { cancel: function () { for (var i = 0; i < callbacks.length; ++i) { if (callbacks[i] === callback) { callbacks[i] = callbacks[callbacks.length - 1]; callbacks.pop(); return } } } } } var regl = extend(compileProcedure, { // Clear current FBO clear: clear, // Short cuts for dynamic variables prop: dynamic.define.bind(null, DYN_PROP), context: dynamic.define.bind(null, DYN_CONTEXT), this: dynamic.define.bind(null, DYN_STATE), // executes an empty draw command draw: compileProcedure({}), // Resources buffer: function (options) { return bufferState.create(options, GL_ARRAY_BUFFER, false, false) }, elements: function (options) { return elementState.create(options, false) }, texture: textureState.create2D, cube: textureState.createCube, renderbuffer: renderbufferState.create, framebuffer: framebufferState.create, framebufferCube: framebufferState.createCube, // Expose context attributes attributes: glAttributes, // Frame rendering frame: frame, on: addListener, // System limits limits: limits, hasExtension: function (name) { return limits.extensions.indexOf(name.toLowerCase()) >= 0 }, // Read pixels read: readPixels, // Destroy regl and all associated resources destroy: destroy, // Direct GL state manipulation _gl: gl, _refresh: refresh, poll: function () { poll(); if (timer) { timer.update(); } }, // Current time now: now, // regl Statistics Information stats: stats$$1 }); config.onDone(null, regl); return regl } return wrapREGL; })));