D7net
Home
Console
Upload
information
Create File
Create Folder
About
Tools
:
/
home
/
tvinntxo
/
www
/
wp-content
/
plugins
/
waves
/
blocks
/
twgl
/
Filename :
twgl.js
back
Copy
/*! * @license twgl.js 4.14.2 Copyright (c) 2015, Gregg Tavares All Rights Reserved. * Available via the MIT license. * see: http://github.com/greggman/twgl.js for details */ (function webpackUniversalModuleDefinition(root, factory) { if(typeof exports === 'object' && typeof module === 'object') module.exports = factory(); else if(typeof define === 'function' && define.amd) define([], factory); else if(typeof exports === 'object') exports["twgl"] = factory(); else root["twgl"] = factory(); })(typeof self !== 'undefined' ? self : this, function() { return /******/ (function(modules) { // webpackBootstrap /******/ // The module cache /******/ var installedModules = {}; /******/ /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ /******/ // Check if module is in cache /******/ if(installedModules[moduleId]) { /******/ return installedModules[moduleId].exports; /******/ } /******/ // Create a new module (and put it into the cache) /******/ var module = installedModules[moduleId] = { /******/ i: moduleId, /******/ l: false, /******/ exports: {} /******/ }; /******/ /******/ // Execute the module function /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); /******/ /******/ // Flag the module as loaded /******/ module.l = true; /******/ /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ /******/ /******/ // expose the modules object (__webpack_modules__) /******/ __webpack_require__.m = modules; /******/ /******/ // expose the module cache /******/ __webpack_require__.c = installedModules; /******/ /******/ // define getter function for harmony exports /******/ __webpack_require__.d = function(exports, name, getter) { /******/ if(!__webpack_require__.o(exports, name)) { /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); /******/ } /******/ }; /******/ /******/ // define __esModule on exports /******/ __webpack_require__.r = function(exports) { /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); /******/ } /******/ Object.defineProperty(exports, '__esModule', { value: true }); /******/ }; /******/ /******/ // create a fake namespace object /******/ // mode & 1: value is a module id, require it /******/ // mode & 2: merge all properties of value into the ns /******/ // mode & 4: return value when already ns object /******/ // mode & 8|1: behave like require /******/ __webpack_require__.t = function(value, mode) { /******/ if(mode & 1) value = __webpack_require__(value); /******/ if(mode & 8) return value; /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; /******/ var ns = Object.create(null); /******/ __webpack_require__.r(ns); /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); /******/ return ns; /******/ }; /******/ /******/ // getDefaultExport function for compatibility with non-harmony modules /******/ __webpack_require__.n = function(module) { /******/ var getter = module && module.__esModule ? /******/ function getDefault() { return module['default']; } : /******/ function getModuleExports() { return module; }; /******/ __webpack_require__.d(getter, 'a', getter); /******/ return getter; /******/ }; /******/ /******/ // Object.prototype.hasOwnProperty.call /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; /******/ /******/ // __webpack_public_path__ /******/ __webpack_require__.p = ""; /******/ /******/ /******/ // Load entry module and return exports /******/ return __webpack_require__(__webpack_require__.s = "./src/twgl-base.js"); /******/ }) /************************************************************************/ /******/ ({ /***/ "./src/attributes.js": /*!***************************!*\ !*** ./src/attributes.js ***! \***************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; exports.__esModule = true; exports.createAttribsFromArrays = createAttribsFromArrays; exports.createBuffersFromArrays = createBuffersFromArrays; exports.createBufferFromArray = createBufferFromArray; exports.createBufferFromTypedArray = createBufferFromTypedArray; exports.createBufferInfoFromArrays = createBufferInfoFromArrays; exports.setAttribInfoBufferFromArray = setAttribInfoBufferFromArray; exports.setAttributePrefix = setAttributePrefix; exports.setAttributeDefaults_ = setDefaults; exports.getNumComponents_ = getNumComponents; exports.getArray_ = getArray; var typedArrays = _interopRequireWildcard(__webpack_require__(/*! ./typedarrays.js */ "./src/typedarrays.js")); var helper = _interopRequireWildcard(__webpack_require__(/*! ./helper.js */ "./src/helper.js")); function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function _getRequireWildcardCache() { return cache; }; return cache; } function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; if (obj != null) { var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj["default"] = obj; if (cache) { cache.set(obj, newObj); } return newObj; } /* * Copyright 2019 Gregg Tavares * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ var STATIC_DRAW = 0x88e4; var ARRAY_BUFFER = 0x8892; var ELEMENT_ARRAY_BUFFER = 0x8893; var BUFFER_SIZE = 0x8764; var BYTE = 0x1400; var UNSIGNED_BYTE = 0x1401; var SHORT = 0x1402; var UNSIGNED_SHORT = 0x1403; var INT = 0x1404; var UNSIGNED_INT = 0x1405; var FLOAT = 0x1406; /** * Low level attribute and buffer related functions * * You should generally not need to use these functions. They are provided * for those cases where you're doing something out of the ordinary * and you need lower level access. * * For backward compatibility they are available at both `twgl.attributes` and `twgl` * itself * * See {@link module:twgl} for core functions * * @module twgl/attributes */ // make sure we don't see a global gl var gl = undefined; /* eslint-disable-line */ /* lgtm [js/unused-local-variable] */ var defaults = { attribPrefix: "" }; /** * Sets the default attrib prefix * * When writing shaders I prefer to name attributes with `a_`, uniforms with `u_` and varyings with `v_` * as it makes it clear where they came from. But, when building geometry I prefer using un-prefixed names. * * In other words I'll create arrays of geometry like this * * var arrays = { * position: ... * normal: ... * texcoord: ... * }; * * But need those mapped to attributes and my attributes start with `a_`. * * @deprecated see {@link module:twgl.setDefaults} * @param {string} prefix prefix for attribs * @memberOf module:twgl/attributes */ function setAttributePrefix(prefix) { defaults.attribPrefix = prefix; } function setDefaults(newDefaults) { helper.copyExistingProperties(newDefaults, defaults); } function setBufferFromTypedArray(gl, type, buffer, array, drawType) { gl.bindBuffer(type, buffer); gl.bufferData(type, array, drawType || STATIC_DRAW); } /** * Given typed array creates a WebGLBuffer and copies the typed array * into it. * * @param {WebGLRenderingContext} gl A WebGLRenderingContext * @param {ArrayBuffer|SharedArrayBuffer|ArrayBufferView|WebGLBuffer} typedArray the typed array. Note: If a WebGLBuffer is passed in it will just be returned. No action will be taken * @param {number} [type] the GL bind type for the buffer. Default = `gl.ARRAY_BUFFER`. * @param {number} [drawType] the GL draw type for the buffer. Default = 'gl.STATIC_DRAW`. * @return {WebGLBuffer} the created WebGLBuffer * @memberOf module:twgl/attributes */ function createBufferFromTypedArray(gl, typedArray, type, drawType) { if (helper.isBuffer(gl, typedArray)) { return typedArray; } type = type || ARRAY_BUFFER; var buffer = gl.createBuffer(); setBufferFromTypedArray(gl, type, buffer, typedArray, drawType); return buffer; } function isIndices(name) { return name === "indices"; } // This is really just a guess. Though I can't really imagine using // anything else? Maybe for some compression? function getNormalizationForTypedArray(typedArray) { if (typedArray instanceof Int8Array) { return true; } // eslint-disable-line if (typedArray instanceof Uint8Array) { return true; } // eslint-disable-line return false; } // This is really just a guess. Though I can't really imagine using // anything else? Maybe for some compression? function getNormalizationForTypedArrayType(typedArrayType) { if (typedArrayType === Int8Array) { return true; } // eslint-disable-line if (typedArrayType === Uint8Array) { return true; } // eslint-disable-line return false; } function getArray(array) { return array.length ? array : array.data; } var texcoordRE = /coord|texture/i; var colorRE = /color|colour/i; function guessNumComponentsFromName(name, length) { var numComponents; if (texcoordRE.test(name)) { numComponents = 2; } else if (colorRE.test(name)) { numComponents = 4; } else { numComponents = 3; // position, normals, indices ... } if (length % numComponents > 0) { throw new Error("Can not guess numComponents for attribute '".concat(name, "'. Tried ").concat(numComponents, " but ").concat(length, " values is not evenly divisible by ").concat(numComponents, ". You should specify it.")); } return numComponents; } function getNumComponents(array, arrayName) { return array.numComponents || array.size || guessNumComponentsFromName(arrayName, getArray(array).length); } function makeTypedArray(array, name) { if (typedArrays.isArrayBuffer(array)) { return array; } if (typedArrays.isArrayBuffer(array.data)) { return array.data; } if (Array.isArray(array)) { array = { data: array }; } var Type = array.type; if (!Type) { if (isIndices(name)) { Type = Uint16Array; } else { Type = Float32Array; } } return new Type(array.data); } /** * The info for an attribute. This is effectively just the arguments to `gl.vertexAttribPointer` plus the WebGLBuffer * for the attribute. * * @typedef {Object} AttribInfo * @property {number[]|ArrayBufferView} [value] a constant value for the attribute. Note: if this is set the attribute will be * disabled and set to this constant value and all other values will be ignored. * @property {number} [numComponents] the number of components for this attribute. * @property {number} [size] synonym for `numComponents`. * @property {number} [type] the type of the attribute (eg. `gl.FLOAT`, `gl.UNSIGNED_BYTE`, etc...) Default = `gl.FLOAT` * @property {boolean} [normalize] whether or not to normalize the data. Default = false * @property {number} [offset] offset into buffer in bytes. Default = 0 * @property {number} [stride] the stride in bytes per element. Default = 0 * @property {number} [divisor] the divisor in instances. Default = undefined. Note: undefined = don't call gl.vertexAttribDivisor * where as anything else = do call it with this value * @property {WebGLBuffer} buffer the buffer that contains the data for this attribute * @property {number} [drawType] the draw type passed to gl.bufferData. Default = gl.STATIC_DRAW * @memberOf module:twgl */ /** * Use this type of array spec when TWGL can't guess the type or number of components of an array * @typedef {Object} FullArraySpec * @property {number[]|ArrayBufferView} [value] a constant value for the attribute. Note: if this is set the attribute will be * disabled and set to this constant value and all other values will be ignored. * @property {(number|number[]|ArrayBufferView)} data The data of the array. A number alone becomes the number of elements of type. * @property {number} [numComponents] number of components for `vertexAttribPointer`. Default is based on the name of the array. * If `coord` is in the name assumes `numComponents = 2`. * If `color` is in the name assumes `numComponents = 4`. * otherwise assumes `numComponents = 3` * @property {constructor} [type] type. This is only used if `data` is a JavaScript array. It is the constructor for the typedarray. (eg. `Uint8Array`). * For example if you want colors in a `Uint8Array` you might have a `FullArraySpec` like `{ type: Uint8Array, data: [255,0,255,255, ...], }`. * @property {number} [size] synonym for `numComponents`. * @property {boolean} [normalize] normalize for `vertexAttribPointer`. Default is true if type is `Int8Array` or `Uint8Array` otherwise false. * @property {number} [stride] stride for `vertexAttribPointer`. Default = 0 * @property {number} [offset] offset for `vertexAttribPointer`. Default = 0 * @property {number} [divisor] divisor for `vertexAttribDivisor`. Default = undefined. Note: undefined = don't call gl.vertexAttribDivisor * where as anything else = do call it with this value * @property {string} [attrib] name of attribute this array maps to. Defaults to same name as array prefixed by the default attribPrefix. * @property {string} [name] synonym for `attrib`. * @property {string} [attribName] synonym for `attrib`. * @property {WebGLBuffer} [buffer] Buffer to use for this attribute. This lets you use your own buffer * but you will need to supply `numComponents` and `type`. You can effectively pass an `AttribInfo` * to provide this. Example: * * const bufferInfo1 = twgl.createBufferInfoFromArrays(gl, { * position: [1, 2, 3, ... ], * }); * const bufferInfo2 = twgl.createBufferInfoFromArrays(gl, { * position: bufferInfo1.attribs.position, // use the same buffer from bufferInfo1 * }); * * @memberOf module:twgl */ /** * An individual array in {@link module:twgl.Arrays} * * When passed to {@link module:twgl.createBufferInfoFromArrays} if an ArraySpec is `number[]` or `ArrayBufferView` * the types will be guessed based on the name. `indices` will be `Uint16Array`, everything else will * be `Float32Array`. If an ArraySpec is a number it's the number of floats for an empty (zeroed) buffer. * * @typedef {(number|number[]|ArrayBufferView|module:twgl.FullArraySpec)} ArraySpec * @memberOf module:twgl */ /** * This is a JavaScript object of arrays by name. The names should match your shader's attributes. If your * attributes have a common prefix you can specify it by calling {@link module:twgl.setAttributePrefix}. * * Bare JavaScript Arrays * * var arrays = { * position: [-1, 1, 0], * normal: [0, 1, 0], * ... * } * * Bare TypedArrays * * var arrays = { * position: new Float32Array([-1, 1, 0]), * color: new Uint8Array([255, 128, 64, 255]), * ... * } * * * Will guess at `numComponents` if not specified based on name. * * If `coord` is in the name assumes `numComponents = 2` * * If `color` is in the name assumes `numComponents = 4` * * otherwise assumes `numComponents = 3` * * Objects with various fields. See {@link module:twgl.FullArraySpec}. * * var arrays = { * position: { numComponents: 3, data: [0, 0, 0, 10, 0, 0, 0, 10, 0, 10, 10, 0], }, * texcoord: { numComponents: 2, data: [0, 0, 0, 1, 1, 0, 1, 1], }, * normal: { numComponents: 3, data: [0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1], }, * indices: { numComponents: 3, data: [0, 1, 2, 1, 2, 3], }, * }; * * @typedef {Object.<string, module:twgl.ArraySpec>} Arrays * @memberOf module:twgl */ /** * Creates a set of attribute data and WebGLBuffers from set of arrays * * Given * * var arrays = { * position: { numComponents: 3, data: [0, 0, 0, 10, 0, 0, 0, 10, 0, 10, 10, 0], }, * texcoord: { numComponents: 2, data: [0, 0, 0, 1, 1, 0, 1, 1], }, * normal: { numComponents: 3, data: [0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1], }, * color: { numComponents: 4, data: [255, 255, 255, 255, 255, 0, 0, 255, 0, 0, 255, 255], type: Uint8Array, }, * indices: { numComponents: 3, data: [0, 1, 2, 1, 2, 3], }, * }; * * returns something like * * var attribs = { * position: { numComponents: 3, type: gl.FLOAT, normalize: false, buffer: WebGLBuffer, }, * texcoord: { numComponents: 2, type: gl.FLOAT, normalize: false, buffer: WebGLBuffer, }, * normal: { numComponents: 3, type: gl.FLOAT, normalize: false, buffer: WebGLBuffer, }, * color: { numComponents: 4, type: gl.UNSIGNED_BYTE, normalize: true, buffer: WebGLBuffer, }, * }; * * notes: * * * Arrays can take various forms * * Bare JavaScript Arrays * * var arrays = { * position: [-1, 1, 0], * normal: [0, 1, 0], * ... * } * * Bare TypedArrays * * var arrays = { * position: new Float32Array([-1, 1, 0]), * color: new Uint8Array([255, 128, 64, 255]), * ... * } * * * Will guess at `numComponents` if not specified based on name. * * If `coord` is in the name assumes `numComponents = 2` * * If `color` is in the name assumes `numComponents = 4` * * otherwise assumes `numComponents = 3` * * @param {WebGLRenderingContext} gl The webgl rendering context. * @param {module:twgl.Arrays} arrays The arrays * @param {module:twgl.BufferInfo} [srcBufferInfo] a BufferInfo to copy from * This lets you share buffers. Any arrays you supply will override * the buffers from srcBufferInfo. * @return {Object.<string, module:twgl.AttribInfo>} the attribs * @memberOf module:twgl/attributes */ function createAttribsFromArrays(gl, arrays) { var attribs = {}; Object.keys(arrays).forEach(function (arrayName) { if (!isIndices(arrayName)) { var array = arrays[arrayName]; var attribName = array.attrib || array.name || array.attribName || defaults.attribPrefix + arrayName; if (array.value) { if (!Array.isArray(array.value) && !typedArrays.isArrayBuffer(array.value)) { throw new Error('array.value is not array or typedarray'); } attribs[attribName] = { value: array.value }; } else { var buffer; var type; var normalization; var numComponents; if (array.buffer && array.buffer instanceof WebGLBuffer) { buffer = array.buffer; numComponents = array.numComponents || array.size; type = array.type; normalization = array.normalize; } else if (typeof array === "number" || typeof array.data === "number") { var numValues = array.data || array; var arrayType = array.type || Float32Array; var numBytes = numValues * arrayType.BYTES_PER_ELEMENT; type = typedArrays.getGLTypeForTypedArrayType(arrayType); normalization = array.normalize !== undefined ? array.normalize : getNormalizationForTypedArrayType(arrayType); numComponents = array.numComponents || array.size || guessNumComponentsFromName(arrayName, numValues); buffer = gl.createBuffer(); gl.bindBuffer(ARRAY_BUFFER, buffer); gl.bufferData(ARRAY_BUFFER, numBytes, array.drawType || STATIC_DRAW); } else { var typedArray = makeTypedArray(array, arrayName); buffer = createBufferFromTypedArray(gl, typedArray, undefined, array.drawType); type = typedArrays.getGLTypeForTypedArray(typedArray); normalization = array.normalize !== undefined ? array.normalize : getNormalizationForTypedArray(typedArray); numComponents = getNumComponents(array, arrayName); } attribs[attribName] = { buffer: buffer, numComponents: numComponents, type: type, normalize: normalization, stride: array.stride || 0, offset: array.offset || 0, divisor: array.divisor === undefined ? undefined : array.divisor, drawType: array.drawType }; } } }); gl.bindBuffer(ARRAY_BUFFER, null); return attribs; } /** * Sets the contents of a buffer attached to an attribInfo * * This is helper function to dynamically update a buffer. * * Let's say you make a bufferInfo * * var arrays = { * position: new Float32Array([0, 0, 0, 10, 0, 0, 0, 10, 0, 10, 10, 0]), * texcoord: new Float32Array([0, 0, 0, 1, 1, 0, 1, 1]), * normal: new Float32Array([0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1]), * indices: new Uint16Array([0, 1, 2, 1, 2, 3]), * }; * var bufferInfo = twgl.createBufferInfoFromArrays(gl, arrays); * * And you want to dynamically update the positions. You could do this * * // assuming arrays.position has already been updated with new data. * twgl.setAttribInfoBufferFromArray(gl, bufferInfo.attribs.position, arrays.position); * * @param {WebGLRenderingContext} gl * @param {AttribInfo} attribInfo The attribInfo who's buffer contents to set. NOTE: If you have an attribute prefix * the name of the attribute will include the prefix. * @param {ArraySpec} array Note: it is arguably inefficient to pass in anything but a typed array because anything * else will have to be converted to a typed array before it can be used by WebGL. During init time that * inefficiency is usually not important but if you're updating data dynamically best to be efficient. * @param {number} [offset] an optional offset into the buffer. This is only an offset into the WebGL buffer * not the array. To pass in an offset into the array itself use a typed array and create an `ArrayBufferView` * for the portion of the array you want to use. * * var someArray = new Float32Array(1000); // an array with 1000 floats * var someSubArray = new Float32Array(someArray.buffer, offsetInBytes, sizeInUnits); // a view into someArray * * Now you can pass `someSubArray` into setAttribInfoBufferFromArray` * @memberOf module:twgl/attributes */ function setAttribInfoBufferFromArray(gl, attribInfo, array, offset) { array = makeTypedArray(array); if (offset !== undefined) { gl.bindBuffer(ARRAY_BUFFER, attribInfo.buffer); gl.bufferSubData(ARRAY_BUFFER, offset, array); } else { setBufferFromTypedArray(gl, ARRAY_BUFFER, attribInfo.buffer, array, attribInfo.drawType); } } function getBytesPerValueForGLType(gl, type) { if (type === BYTE) return 1; // eslint-disable-line if (type === UNSIGNED_BYTE) return 1; // eslint-disable-line if (type === SHORT) return 2; // eslint-disable-line if (type === UNSIGNED_SHORT) return 2; // eslint-disable-line if (type === INT) return 4; // eslint-disable-line if (type === UNSIGNED_INT) return 4; // eslint-disable-line if (type === FLOAT) return 4; // eslint-disable-line return 0; } // Tries to get the number of elements from a set of arrays. var positionKeys = ['position', 'positions', 'a_position']; function getNumElementsFromNonIndexedArrays(arrays) { var key; var ii; for (ii = 0; ii < positionKeys.length; ++ii) { key = positionKeys[ii]; if (key in arrays) { break; } } if (ii === positionKeys.length) { key = Object.keys(arrays)[0]; } var array = arrays[key]; var length = getArray(array).length; var numComponents = getNumComponents(array, key); var numElements = length / numComponents; if (length % numComponents > 0) { throw new Error("numComponents ".concat(numComponents, " not correct for length ").concat(length)); } return numElements; } function getNumElementsFromAttributes(gl, attribs) { var key; var ii; for (ii = 0; ii < positionKeys.length; ++ii) { key = positionKeys[ii]; if (key in attribs) { break; } key = defaults.attribPrefix + key; if (key in attribs) { break; } } if (ii === positionKeys.length) { key = Object.keys(attribs)[0]; } var attrib = attribs[key]; gl.bindBuffer(ARRAY_BUFFER, attrib.buffer); var numBytes = gl.getBufferParameter(ARRAY_BUFFER, BUFFER_SIZE); gl.bindBuffer(ARRAY_BUFFER, null); var bytesPerValue = getBytesPerValueForGLType(gl, attrib.type); var totalElements = numBytes / bytesPerValue; var numComponents = attrib.numComponents || attrib.size; // TODO: check stride var numElements = totalElements / numComponents; if (numElements % 1 !== 0) { throw new Error("numComponents ".concat(numComponents, " not correct for length ").concat(length)); } return numElements; } /** * @typedef {Object} BufferInfo * @property {number} numElements The number of elements to pass to `gl.drawArrays` or `gl.drawElements`. * @property {number} [elementType] The type of indices `UNSIGNED_BYTE`, `UNSIGNED_SHORT` etc.. * @property {WebGLBuffer} [indices] The indices `ELEMENT_ARRAY_BUFFER` if any indices exist. * @property {Object.<string, module:twgl.AttribInfo>} [attribs] The attribs appropriate to call `setAttributes` * @memberOf module:twgl */ /** * Creates a BufferInfo from an object of arrays. * * This can be passed to {@link module:twgl.setBuffersAndAttributes} and to * {@link module:twgl:drawBufferInfo}. * * Given an object like * * var arrays = { * position: { numComponents: 3, data: [0, 0, 0, 10, 0, 0, 0, 10, 0, 10, 10, 0], }, * texcoord: { numComponents: 2, data: [0, 0, 0, 1, 1, 0, 1, 1], }, * normal: { numComponents: 3, data: [0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1], }, * indices: { numComponents: 3, data: [0, 1, 2, 1, 2, 3], }, * }; * * Creates an BufferInfo like this * * bufferInfo = { * numElements: 4, // or whatever the number of elements is * indices: WebGLBuffer, // this property will not exist if there are no indices * attribs: { * position: { buffer: WebGLBuffer, numComponents: 3, }, * normal: { buffer: WebGLBuffer, numComponents: 3, }, * texcoord: { buffer: WebGLBuffer, numComponents: 2, }, * }, * }; * * The properties of arrays can be JavaScript arrays in which case the number of components * will be guessed. * * var arrays = { * position: [0, 0, 0, 10, 0, 0, 0, 10, 0, 10, 10, 0], * texcoord: [0, 0, 0, 1, 1, 0, 1, 1], * normal: [0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1], * indices: [0, 1, 2, 1, 2, 3], * }; * * They can also be TypedArrays * * var arrays = { * position: new Float32Array([0, 0, 0, 10, 0, 0, 0, 10, 0, 10, 10, 0]), * texcoord: new Float32Array([0, 0, 0, 1, 1, 0, 1, 1]), * normal: new Float32Array([0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1]), * indices: new Uint16Array([0, 1, 2, 1, 2, 3]), * }; * * Or AugmentedTypedArrays * * var positions = createAugmentedTypedArray(3, 4); * var texcoords = createAugmentedTypedArray(2, 4); * var normals = createAugmentedTypedArray(3, 4); * var indices = createAugmentedTypedArray(3, 2, Uint16Array); * * positions.push([0, 0, 0, 10, 0, 0, 0, 10, 0, 10, 10, 0]); * texcoords.push([0, 0, 0, 1, 1, 0, 1, 1]); * normals.push([0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1]); * indices.push([0, 1, 2, 1, 2, 3]); * * var arrays = { * position: positions, * texcoord: texcoords, * normal: normals, * indices: indices, * }; * * For the last example it is equivalent to * * var bufferInfo = { * attribs: { * position: { numComponents: 3, buffer: gl.createBuffer(), }, * texcoord: { numComponents: 2, buffer: gl.createBuffer(), }, * normal: { numComponents: 3, buffer: gl.createBuffer(), }, * }, * indices: gl.createBuffer(), * numElements: 6, * }; * * gl.bindBuffer(gl.ARRAY_BUFFER, bufferInfo.attribs.position.buffer); * gl.bufferData(gl.ARRAY_BUFFER, arrays.position, gl.STATIC_DRAW); * gl.bindBuffer(gl.ARRAY_BUFFER, bufferInfo.attribs.texcoord.buffer); * gl.bufferData(gl.ARRAY_BUFFER, arrays.texcoord, gl.STATIC_DRAW); * gl.bindBuffer(gl.ARRAY_BUFFER, bufferInfo.attribs.normal.buffer); * gl.bufferData(gl.ARRAY_BUFFER, arrays.normal, gl.STATIC_DRAW); * gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, bufferInfo.indices); * gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, arrays.indices, gl.STATIC_DRAW); * * @param {WebGLRenderingContext} gl A WebGLRenderingContext * @param {module:twgl.Arrays} arrays Your data * @param {module:twgl.BufferInfo} [srcBufferInfo] An existing * buffer info to start from. WebGLBuffers etc specified * in the srcBufferInfo will be used in a new BufferInfo * with any arrays specified overriding the ones in * srcBufferInfo. * @return {module:twgl.BufferInfo} A BufferInfo * @memberOf module:twgl/attributes */ function createBufferInfoFromArrays(gl, arrays, srcBufferInfo) { var newAttribs = createAttribsFromArrays(gl, arrays); var bufferInfo = Object.assign({}, srcBufferInfo ? srcBufferInfo : {}); bufferInfo.attribs = Object.assign({}, srcBufferInfo ? srcBufferInfo.attribs : {}, newAttribs); var indices = arrays.indices; if (indices) { var newIndices = makeTypedArray(indices, "indices"); bufferInfo.indices = createBufferFromTypedArray(gl, newIndices, ELEMENT_ARRAY_BUFFER); bufferInfo.numElements = newIndices.length; bufferInfo.elementType = typedArrays.getGLTypeForTypedArray(newIndices); } else if (!bufferInfo.numElements) { bufferInfo.numElements = getNumElementsFromAttributes(gl, bufferInfo.attribs); } return bufferInfo; } /** * Creates a buffer from an array, typed array, or array spec * * Given something like this * * [1, 2, 3], * * or * * new Uint16Array([1,2,3]); * * or * * { * data: [1, 2, 3], * type: Uint8Array, * } * * returns a WebGLBuffer that contains the given data. * * @param {WebGLRenderingContext} gl A WebGLRenderingContext. * @param {module:twgl.ArraySpec} array an array, typed array, or array spec. * @param {string} arrayName name of array. Used to guess the type if type can not be derived otherwise. * @return {WebGLBuffer} a WebGLBuffer containing the data in array. * @memberOf module:twgl/attributes */ function createBufferFromArray(gl, array, arrayName) { var type = arrayName === "indices" ? ELEMENT_ARRAY_BUFFER : ARRAY_BUFFER; var typedArray = makeTypedArray(array, arrayName); return createBufferFromTypedArray(gl, typedArray, type); } /** * Creates buffers from arrays or typed arrays * * Given something like this * * var arrays = { * positions: [1, 2, 3], * normals: [0, 0, 1], * } * * returns something like * * buffers = { * positions: WebGLBuffer, * normals: WebGLBuffer, * } * * If the buffer is named 'indices' it will be made an ELEMENT_ARRAY_BUFFER. * * @param {WebGLRenderingContext} gl A WebGLRenderingContext. * @param {module:twgl.Arrays} arrays * @return {Object<string, WebGLBuffer>} returns an object with one WebGLBuffer per array * @memberOf module:twgl/attributes */ function createBuffersFromArrays(gl, arrays) { var buffers = {}; Object.keys(arrays).forEach(function (key) { buffers[key] = createBufferFromArray(gl, arrays[key], key); }); // Ugh! if (arrays.indices) { buffers.numElements = arrays.indices.length; buffers.elementType = typedArrays.getGLTypeForTypedArray(makeTypedArray(arrays.indices), 'indices'); } else { buffers.numElements = getNumElementsFromNonIndexedArrays(arrays); } return buffers; } /***/ }), /***/ "./src/draw.js": /*!*********************!*\ !*** ./src/draw.js ***! \*********************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; exports.__esModule = true; exports.drawBufferInfo = drawBufferInfo; exports.drawObjectList = drawObjectList; var programs = _interopRequireWildcard(__webpack_require__(/*! ./programs.js */ "./src/programs.js")); function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function _getRequireWildcardCache() { return cache; }; return cache; } function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; if (obj != null) { var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj["default"] = obj; if (cache) { cache.set(obj, newObj); } return newObj; } /* * Copyright 2019 Gregg Tavares * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ var TRIANGLES = 0x0004; var UNSIGNED_SHORT = 0x1403; /** * Drawing related functions * * For backward compatibility they are available at both `twgl.draw` and `twgl` * itself * * See {@link module:twgl} for core functions * * @module twgl/draw */ /** * Calls `gl.drawElements` or `gl.drawArrays`, whichever is appropriate * * normally you'd call `gl.drawElements` or `gl.drawArrays` yourself * but calling this means if you switch from indexed data to non-indexed * data you don't have to remember to update your draw call. * * @param {WebGLRenderingContext} gl A WebGLRenderingContext * @param {(module:twgl.BufferInfo|module:twgl.VertexArrayInfo)} bufferInfo A BufferInfo as returned from {@link module:twgl.createBufferInfoFromArrays} or * a VertexArrayInfo as returned from {@link module:twgl.createVertexArrayInfo} * @param {number} [type] eg (gl.TRIANGLES, gl.LINES, gl.POINTS, gl.TRIANGLE_STRIP, ...). Defaults to `gl.TRIANGLES` * @param {number} [count] An optional count. Defaults to bufferInfo.numElements * @param {number} [offset] An optional offset. Defaults to 0. * @param {number} [instanceCount] An optional instanceCount. if set then `drawArraysInstanced` or `drawElementsInstanced` will be called * @memberOf module:twgl/draw */ function drawBufferInfo(gl, bufferInfo, type, count, offset, instanceCount) { type = type === undefined ? TRIANGLES : type; var indices = bufferInfo.indices; var elementType = bufferInfo.elementType; var numElements = count === undefined ? bufferInfo.numElements : count; offset = offset === undefined ? 0 : offset; if (elementType || indices) { if (instanceCount !== undefined) { gl.drawElementsInstanced(type, numElements, elementType === undefined ? UNSIGNED_SHORT : bufferInfo.elementType, offset, instanceCount); } else { gl.drawElements(type, numElements, elementType === undefined ? UNSIGNED_SHORT : bufferInfo.elementType, offset); } } else { if (instanceCount !== undefined) { gl.drawArraysInstanced(type, offset, numElements, instanceCount); } else { gl.drawArrays(type, offset, numElements); } } } /** * A DrawObject is useful for putting objects in to an array and passing them to {@link module:twgl.drawObjectList}. * * You need either a `BufferInfo` or a `VertexArrayInfo`. * * @typedef {Object} DrawObject * @property {boolean} [active] whether or not to draw. Default = `true` (must be `false` to be not true). In other words `undefined` = `true` * @property {number} [type] type to draw eg. `gl.TRIANGLES`, `gl.LINES`, etc... * @property {module:twgl.ProgramInfo} programInfo A ProgramInfo as returned from {@link module:twgl.createProgramInfo} * @property {module:twgl.BufferInfo} [bufferInfo] A BufferInfo as returned from {@link module:twgl.createBufferInfoFromArrays} * @property {module:twgl.VertexArrayInfo} [vertexArrayInfo] A VertexArrayInfo as returned from {@link module:twgl.createVertexArrayInfo} * @property {Object<string, ?>} uniforms The values for the uniforms. * You can pass multiple objects by putting them in an array. For example * * var sharedUniforms = { * u_fogNear: 10, * u_projection: ... * ... * }; * * var localUniforms = { * u_world: ... * u_diffuseColor: ... * }; * * var drawObj = { * ... * uniforms: [sharedUniforms, localUniforms], * }; * * @property {number} [offset] the offset to pass to `gl.drawArrays` or `gl.drawElements`. Defaults to 0. * @property {number} [count] the count to pass to `gl.drawArrays` or `gl.drawElements`. Defaults to bufferInfo.numElements. * @property {number} [instanceCount] the number of instances. Defaults to undefined. * @memberOf module:twgl */ /** * Draws a list of objects * @param {WebGLRenderingContext} gl A WebGLRenderingContext * @param {DrawObject[]} objectsToDraw an array of objects to draw. * @memberOf module:twgl/draw */ function drawObjectList(gl, objectsToDraw) { var lastUsedProgramInfo = null; var lastUsedBufferInfo = null; objectsToDraw.forEach(function (object) { if (object.active === false) { return; } var programInfo = object.programInfo; var bufferInfo = object.vertexArrayInfo || object.bufferInfo; var bindBuffers = false; var type = object.type === undefined ? TRIANGLES : object.type; if (programInfo !== lastUsedProgramInfo) { lastUsedProgramInfo = programInfo; gl.useProgram(programInfo.program); // We have to rebind buffers when changing programs because we // only bind buffers the program uses. So if 2 programs use the same // bufferInfo but the 1st one uses only positions the when the // we switch to the 2nd one some of the attributes will not be on. bindBuffers = true; } // Setup all the needed attributes. if (bindBuffers || bufferInfo !== lastUsedBufferInfo) { if (lastUsedBufferInfo && lastUsedBufferInfo.vertexArrayObject && !bufferInfo.vertexArrayObject) { gl.bindVertexArray(null); } lastUsedBufferInfo = bufferInfo; programs.setBuffersAndAttributes(gl, programInfo, bufferInfo); } // Set the uniforms. programs.setUniforms(programInfo, object.uniforms); // Draw drawBufferInfo(gl, bufferInfo, type, object.count, object.offset, object.instanceCount); }); if (lastUsedBufferInfo && lastUsedBufferInfo.vertexArrayObject) { gl.bindVertexArray(null); } } /***/ }), /***/ "./src/framebuffers.js": /*!*****************************!*\ !*** ./src/framebuffers.js ***! \*****************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; exports.__esModule = true; exports.bindFramebufferInfo = bindFramebufferInfo; exports.createFramebufferInfo = createFramebufferInfo; exports.resizeFramebufferInfo = resizeFramebufferInfo; var textures = _interopRequireWildcard(__webpack_require__(/*! ./textures.js */ "./src/textures.js")); var helper = _interopRequireWildcard(__webpack_require__(/*! ./helper.js */ "./src/helper.js")); function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function _getRequireWildcardCache() { return cache; }; return cache; } function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; if (obj != null) { var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj["default"] = obj; if (cache) { cache.set(obj, newObj); } return newObj; } /* * Copyright 2019 Gregg Tavares * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ /** * Framebuffer related functions * * For backward compatibility they are available at both `twgl.framebuffer` and `twgl` * itself * * See {@link module:twgl} for core functions * * @module twgl/framebuffers */ // make sure we don't see a global gl var gl = undefined; /* eslint-disable-line */ /* lgtm [js/unused-local-variable] */ var FRAMEBUFFER = 0x8d40; var RENDERBUFFER = 0x8d41; var TEXTURE_2D = 0x0de1; var UNSIGNED_BYTE = 0x1401; /* PixelFormat */ var DEPTH_COMPONENT = 0x1902; var RGBA = 0x1908; /* Framebuffer Object. */ var RGBA4 = 0x8056; var RGB5_A1 = 0x8057; var RGB565 = 0x8D62; var DEPTH_COMPONENT16 = 0x81A5; var STENCIL_INDEX = 0x1901; var STENCIL_INDEX8 = 0x8D48; var DEPTH_STENCIL = 0x84F9; var COLOR_ATTACHMENT0 = 0x8CE0; var DEPTH_ATTACHMENT = 0x8D00; var STENCIL_ATTACHMENT = 0x8D20; var DEPTH_STENCIL_ATTACHMENT = 0x821A; /* TextureWrapMode */ var REPEAT = 0x2901; // eslint-disable-line var CLAMP_TO_EDGE = 0x812F; var MIRRORED_REPEAT = 0x8370; // eslint-disable-line /* TextureMagFilter */ var NEAREST = 0x2600; // eslint-disable-line var LINEAR = 0x2601; /* TextureMinFilter */ var NEAREST_MIPMAP_NEAREST = 0x2700; // eslint-disable-line var LINEAR_MIPMAP_NEAREST = 0x2701; // eslint-disable-line var NEAREST_MIPMAP_LINEAR = 0x2702; // eslint-disable-line var LINEAR_MIPMAP_LINEAR = 0x2703; // eslint-disable-line /** * The options for a framebuffer attachment. * * Note: For a `format` that is a texture include all the texture * options from {@link module:twgl.TextureOptions} for example * `min`, `mag`, `clamp`, etc... Note that unlike {@link module:twgl.TextureOptions} * `auto` defaults to `false` for attachment textures but `min` and `mag` default * to `gl.LINEAR` and `wrap` defaults to `CLAMP_TO_EDGE` * * @typedef {Object} AttachmentOptions * @property {number} [attach] The attachment point. Defaults * to `gl.COLOR_ATTACHMENT0 + ndx` unless type is a depth or stencil type * then it's gl.DEPTH_ATTACHMENT or `gl.DEPTH_STENCIL_ATTACHMENT` depending * on the format or attachment type. * @property {number} [format] The format. If one of `gl.RGBA4`, * `gl.RGB565`, `gl.RGB5_A1`, `gl.DEPTH_COMPONENT16`, * `gl.STENCIL_INDEX8` or `gl.DEPTH_STENCIL` then will create a * renderbuffer. Otherwise will create a texture. Default = `gl.RGBA` * @property {number} [type] The type. Used for texture. Default = `gl.UNSIGNED_BYTE`. * @property {number} [target] The texture target for `gl.framebufferTexture2D`. * Defaults to `gl.TEXTURE_2D`. Set to appropriate face for cube maps. * @property {number} [level] level for `gl.framebufferTexture2D`. Defaults to 0. * @property {number} [layer] layer for `gl.framebufferTextureLayer`. Defaults to undefined. * If set then `gl.framebufferTextureLayer` is called, if not then `gl.framebufferTexture2D` * @property {WebGLObject} [attachment] An existing renderbuffer or texture. * If provided will attach this Object. This allows you to share * attachments across framebuffers. * @memberOf module:twgl * @mixes module:twgl.TextureOptions */ var defaultAttachments = [{ format: RGBA, type: UNSIGNED_BYTE, min: LINEAR, wrap: CLAMP_TO_EDGE }, { format: DEPTH_STENCIL }]; var attachmentsByFormat = {}; attachmentsByFormat[DEPTH_STENCIL] = DEPTH_STENCIL_ATTACHMENT; attachmentsByFormat[STENCIL_INDEX] = STENCIL_ATTACHMENT; attachmentsByFormat[STENCIL_INDEX8] = STENCIL_ATTACHMENT; attachmentsByFormat[DEPTH_COMPONENT] = DEPTH_ATTACHMENT; attachmentsByFormat[DEPTH_COMPONENT16] = DEPTH_ATTACHMENT; function getAttachmentPointForFormat(format) { return attachmentsByFormat[format]; } var renderbufferFormats = {}; renderbufferFormats[RGBA4] = true; renderbufferFormats[RGB5_A1] = true; renderbufferFormats[RGB565] = true; renderbufferFormats[DEPTH_STENCIL] = true; renderbufferFormats[DEPTH_COMPONENT16] = true; renderbufferFormats[STENCIL_INDEX] = true; renderbufferFormats[STENCIL_INDEX8] = true; function isRenderbufferFormat(format) { return renderbufferFormats[format]; } /** * @typedef {Object} FramebufferInfo * @property {WebGLFramebuffer} framebuffer The WebGLFramebuffer for this framebufferInfo * @property {WebGLObject[]} attachments The created attachments in the same order as passed in to {@link module:twgl.createFramebufferInfo}. * @property {number} width The width of the framebuffer and its attachments * @property {number} height The width of the framebuffer and its attachments * @memberOf module:twgl */ /** * Creates a framebuffer and attachments. * * This returns a {@link module:twgl.FramebufferInfo} because it needs to return the attachments as well as the framebuffer. * * The simplest usage * * // create an RGBA/UNSIGNED_BYTE texture and DEPTH_STENCIL renderbuffer * const fbi = twgl.createFramebufferInfo(gl); * * More complex usage * * // create an RGB565 renderbuffer and a STENCIL_INDEX8 renderbuffer * const attachments = [ * { format: RGB565, mag: NEAREST }, * { format: STENCIL_INDEX8 }, * ] * const fbi = twgl.createFramebufferInfo(gl, attachments); * * Passing in a specific size * * const width = 256; * const height = 256; * const fbi = twgl.createFramebufferInfo(gl, attachments, width, height); * * **Note!!** It is up to you to check if the framebuffer is renderable by calling `gl.checkFramebufferStatus`. * [WebGL1 only guarantees 3 combinations of attachments work](https://www.khronos.org/registry/webgl/specs/latest/1.0/#6.6). * * @param {WebGLRenderingContext} gl the WebGLRenderingContext * @param {module:twgl.AttachmentOptions[]} [attachments] which attachments to create. If not provided the default is a framebuffer with an * `RGBA`, `UNSIGNED_BYTE` texture `COLOR_ATTACHMENT0` and a `DEPTH_STENCIL` renderbuffer `DEPTH_STENCIL_ATTACHMENT`. * @param {number} [width] the width for the attachments. Default = size of drawingBuffer * @param {number} [height] the height for the attachments. Default = size of drawingBuffer * @return {module:twgl.FramebufferInfo} the framebuffer and attachments. * @memberOf module:twgl/framebuffers */ function createFramebufferInfo(gl, attachments, width, height) { var target = FRAMEBUFFER; var fb = gl.createFramebuffer(); gl.bindFramebuffer(target, fb); width = width || gl.drawingBufferWidth; height = height || gl.drawingBufferHeight; attachments = attachments || defaultAttachments; var colorAttachmentCount = 0; var framebufferInfo = { framebuffer: fb, attachments: [], width: width, height: height }; attachments.forEach(function (attachmentOptions) { var attachment = attachmentOptions.attachment; var format = attachmentOptions.format; var attachmentPoint = getAttachmentPointForFormat(format); if (!attachmentPoint) { attachmentPoint = COLOR_ATTACHMENT0 + colorAttachmentCount++; } if (!attachment) { if (isRenderbufferFormat(format)) { attachment = gl.createRenderbuffer(); gl.bindRenderbuffer(RENDERBUFFER, attachment); gl.renderbufferStorage(RENDERBUFFER, format, width, height); } else { var textureOptions = Object.assign({}, attachmentOptions); textureOptions.width = width; textureOptions.height = height; if (textureOptions.auto === undefined) { textureOptions.auto = false; textureOptions.min = textureOptions.min || textureOptions.minMag || LINEAR; textureOptions.mag = textureOptions.mag || textureOptions.minMag || LINEAR; textureOptions.wrapS = textureOptions.wrapS || textureOptions.wrap || CLAMP_TO_EDGE; textureOptions.wrapT = textureOptions.wrapT || textureOptions.wrap || CLAMP_TO_EDGE; } attachment = textures.createTexture(gl, textureOptions); } } if (helper.isRenderbuffer(gl, attachment)) { gl.framebufferRenderbuffer(target, attachmentPoint, RENDERBUFFER, attachment); } else if (helper.isTexture(gl, attachment)) { if (attachmentOptions.layer !== undefined) { gl.framebufferTextureLayer(target, attachmentPoint, attachment, attachmentOptions.level || 0, attachmentOptions.layer); } else { gl.framebufferTexture2D(target, attachmentPoint, attachmentOptions.texTarget || TEXTURE_2D, attachment, attachmentOptions.level || 0); } } else { throw new Error('unknown attachment type'); } framebufferInfo.attachments.push(attachment); }); return framebufferInfo; } /** * Resizes the attachments of a framebuffer. * * You need to pass in the same `attachments` as you passed in {@link module:twgl.createFramebufferInfo} * because TWGL has no idea the format/type of each attachment. * * The simplest usage * * // create an RGBA/UNSIGNED_BYTE texture and DEPTH_STENCIL renderbuffer * const fbi = twgl.createFramebufferInfo(gl); * * ... * * function render() { * if (twgl.resizeCanvasToDisplaySize(gl.canvas)) { * // resize the attachments * twgl.resizeFramebufferInfo(gl, fbi); * } * * More complex usage * * // create an RGB565 renderbuffer and a STENCIL_INDEX8 renderbuffer * const attachments = [ * { format: RGB565, mag: NEAREST }, * { format: STENCIL_INDEX8 }, * ] * const fbi = twgl.createFramebufferInfo(gl, attachments); * * ... * * function render() { * if (twgl.resizeCanvasToDisplaySize(gl.canvas)) { * // resize the attachments to match * twgl.resizeFramebufferInfo(gl, fbi, attachments); * } * * @param {WebGLRenderingContext} gl the WebGLRenderingContext * @param {module:twgl.FramebufferInfo} framebufferInfo a framebufferInfo as returned from {@link module:twgl.createFramebufferInfo}. * @param {module:twgl.AttachmentOptions[]} [attachments] the same attachments options as passed to {@link module:twgl.createFramebufferInfo}. * @param {number} [width] the width for the attachments. Default = size of drawingBuffer * @param {number} [height] the height for the attachments. Default = size of drawingBuffer * @memberOf module:twgl/framebuffers */ function resizeFramebufferInfo(gl, framebufferInfo, attachments, width, height) { width = width || gl.drawingBufferWidth; height = height || gl.drawingBufferHeight; framebufferInfo.width = width; framebufferInfo.height = height; attachments = attachments || defaultAttachments; attachments.forEach(function (attachmentOptions, ndx) { var attachment = framebufferInfo.attachments[ndx]; var format = attachmentOptions.format; if (helper.isRenderbuffer(gl, attachment)) { gl.bindRenderbuffer(RENDERBUFFER, attachment); gl.renderbufferStorage(RENDERBUFFER, format, width, height); } else if (helper.isTexture(gl, attachment)) { textures.resizeTexture(gl, attachment, attachmentOptions, width, height); } else { throw new Error('unknown attachment type'); } }); } /** * Binds a framebuffer * * This function pretty much solely exists because I spent hours * trying to figure out why something I wrote wasn't working only * to realize I forget to set the viewport dimensions. * My hope is this function will fix that. * * It is effectively the same as * * gl.bindFramebuffer(gl.FRAMEBUFFER, someFramebufferInfo.framebuffer); * gl.viewport(0, 0, someFramebufferInfo.width, someFramebufferInfo.height); * * @param {WebGLRenderingContext} gl the WebGLRenderingContext * @param {module:twgl.FramebufferInfo|null} [framebufferInfo] a framebufferInfo as returned from {@link module:twgl.createFramebufferInfo}. * If falsy will bind the canvas. * @param {number} [target] The target. If not passed `gl.FRAMEBUFFER` will be used. * @memberOf module:twgl/framebuffers */ function bindFramebufferInfo(gl, framebufferInfo, target) { target = target || FRAMEBUFFER; if (framebufferInfo) { gl.bindFramebuffer(target, framebufferInfo.framebuffer); gl.viewport(0, 0, framebufferInfo.width, framebufferInfo.height); } else { gl.bindFramebuffer(target, null); gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight); } } /***/ }), /***/ "./src/helper.js": /*!***********************!*\ !*** ./src/helper.js ***! \***********************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; exports.__esModule = true; exports.copyExistingProperties = copyExistingProperties; exports.copyNamedProperties = copyNamedProperties; exports.error = error; exports.warn = warn; exports.isBuffer = isBuffer; exports.isRenderbuffer = isRenderbuffer; exports.isShader = isShader; exports.isTexture = isTexture; exports.isSampler = isSampler; /* * Copyright 2019 Gregg Tavares * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ /* eslint no-console: "off" */ /** * Copy named properties * * @param {string[]} names names of properties to copy * @param {object} src object to copy properties from * @param {object} dst object to copy properties to * @private */ function copyNamedProperties(names, src, dst) { names.forEach(function (name) { var value = src[name]; if (value !== undefined) { dst[name] = value; } }); } /** * Copies properties from source to dest only if a matching key is in dest * * @param {Object.<string, ?>} src the source * @param {Object.<string, ?>} dst the dest * @private */ function copyExistingProperties(src, dst) { Object.keys(dst).forEach(function (key) { if (dst.hasOwnProperty(key) && src.hasOwnProperty(key)) { /* eslint no-prototype-builtins: 0 */ dst[key] = src[key]; } }); } function error() { var _console; (_console = console).error.apply(_console, arguments); } function warn() { var _console2; (_console2 = console).warn.apply(_console2, arguments); } function isBuffer(gl, t) { return typeof WebGLBuffer !== 'undefined' && t instanceof WebGLBuffer; } function isRenderbuffer(gl, t) { return typeof WebGLRenderbuffer !== 'undefined' && t instanceof WebGLRenderbuffer; } function isShader(gl, t) { return typeof WebGLShader !== 'undefined' && t instanceof WebGLShader; } function isTexture(gl, t) { return typeof WebGLTexture !== 'undefined' && t instanceof WebGLTexture; } function isSampler(gl, t) { return typeof WebGLSampler !== 'undefined' && t instanceof WebGLSampler; } /***/ }), /***/ "./src/programs.js": /*!*************************!*\ !*** ./src/programs.js ***! \*************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; exports.__esModule = true; exports.createAttributeSetters = createAttributeSetters; exports.createProgram = createProgram; exports.createProgramFromScripts = createProgramFromScripts; exports.createProgramFromSources = createProgramFromSources; exports.createProgramInfo = createProgramInfo; exports.createProgramInfoFromProgram = createProgramInfoFromProgram; exports.createUniformSetters = createUniformSetters; exports.createUniformBlockSpecFromProgram = createUniformBlockSpecFromProgram; exports.createUniformBlockInfoFromProgram = createUniformBlockInfoFromProgram; exports.createUniformBlockInfo = createUniformBlockInfo; exports.createTransformFeedback = createTransformFeedback; exports.createTransformFeedbackInfo = createTransformFeedbackInfo; exports.bindTransformFeedbackInfo = bindTransformFeedbackInfo; exports.setAttributes = setAttributes; exports.setBuffersAndAttributes = setBuffersAndAttributes; exports.setUniforms = setUniforms; exports.setUniformBlock = setUniformBlock; exports.setBlockUniforms = setBlockUniforms; exports.bindUniformBlock = bindUniformBlock; exports.setUniformsAndBindTextures = void 0; var utils = _interopRequireWildcard(__webpack_require__(/*! ./utils.js */ "./src/utils.js")); var helper = _interopRequireWildcard(__webpack_require__(/*! ./helper.js */ "./src/helper.js")); function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function _getRequireWildcardCache() { return cache; }; return cache; } function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; if (obj != null) { var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj["default"] = obj; if (cache) { cache.set(obj, newObj); } return newObj; } /* * Copyright 2019 Gregg Tavares * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ /** * Low level shader program related functions * * You should generally not need to use these functions. They are provided * for those cases where you're doing something out of the ordinary * and you need lower level access. * * For backward compatibility they are available at both `twgl.programs` and `twgl` * itself * * See {@link module:twgl} for core functions * * @module twgl/programs */ var error = helper.error; var warn = helper.warn; function getElementById(id) { return typeof document !== 'undefined' && document.getElementById ? document.getElementById(id) : null; } var TEXTURE0 = 0x84c0; var DYNAMIC_DRAW = 0x88e8; var ARRAY_BUFFER = 0x8892; var ELEMENT_ARRAY_BUFFER = 0x8893; var UNIFORM_BUFFER = 0x8a11; var TRANSFORM_FEEDBACK_BUFFER = 0x8c8e; var TRANSFORM_FEEDBACK = 0x8e22; var COMPILE_STATUS = 0x8b81; var LINK_STATUS = 0x8b82; var FRAGMENT_SHADER = 0x8b30; var VERTEX_SHADER = 0x8b31; var SEPARATE_ATTRIBS = 0x8c8d; var ACTIVE_UNIFORMS = 0x8b86; var ACTIVE_ATTRIBUTES = 0x8b89; var TRANSFORM_FEEDBACK_VARYINGS = 0x8c83; var ACTIVE_UNIFORM_BLOCKS = 0x8a36; var UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER = 0x8a44; var UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER = 0x8a46; var UNIFORM_BLOCK_DATA_SIZE = 0x8a40; var UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES = 0x8a43; var FLOAT = 0x1406; var FLOAT_VEC2 = 0x8B50; var FLOAT_VEC3 = 0x8B51; var FLOAT_VEC4 = 0x8B52; var INT = 0x1404; var INT_VEC2 = 0x8B53; var INT_VEC3 = 0x8B54; var INT_VEC4 = 0x8B55; var BOOL = 0x8B56; var BOOL_VEC2 = 0x8B57; var BOOL_VEC3 = 0x8B58; var BOOL_VEC4 = 0x8B59; var FLOAT_MAT2 = 0x8B5A; var FLOAT_MAT3 = 0x8B5B; var FLOAT_MAT4 = 0x8B5C; var SAMPLER_2D = 0x8B5E; var SAMPLER_CUBE = 0x8B60; var SAMPLER_3D = 0x8B5F; var SAMPLER_2D_SHADOW = 0x8B62; var FLOAT_MAT2x3 = 0x8B65; var FLOAT_MAT2x4 = 0x8B66; var FLOAT_MAT3x2 = 0x8B67; var FLOAT_MAT3x4 = 0x8B68; var FLOAT_MAT4x2 = 0x8B69; var FLOAT_MAT4x3 = 0x8B6A; var SAMPLER_2D_ARRAY = 0x8DC1; var SAMPLER_2D_ARRAY_SHADOW = 0x8DC4; var SAMPLER_CUBE_SHADOW = 0x8DC5; var UNSIGNED_INT = 0x1405; var UNSIGNED_INT_VEC2 = 0x8DC6; var UNSIGNED_INT_VEC3 = 0x8DC7; var UNSIGNED_INT_VEC4 = 0x8DC8; var INT_SAMPLER_2D = 0x8DCA; var INT_SAMPLER_3D = 0x8DCB; var INT_SAMPLER_CUBE = 0x8DCC; var INT_SAMPLER_2D_ARRAY = 0x8DCF; var UNSIGNED_INT_SAMPLER_2D = 0x8DD2; var UNSIGNED_INT_SAMPLER_3D = 0x8DD3; var UNSIGNED_INT_SAMPLER_CUBE = 0x8DD4; var UNSIGNED_INT_SAMPLER_2D_ARRAY = 0x8DD7; var TEXTURE_2D = 0x0DE1; var TEXTURE_CUBE_MAP = 0x8513; var TEXTURE_3D = 0x806F; var TEXTURE_2D_ARRAY = 0x8C1A; var typeMap = {}; /** * Returns the corresponding bind point for a given sampler type */ function getBindPointForSamplerType(gl, type) { return typeMap[type].bindPoint; } // This kind of sucks! If you could compose functions as in `var fn = gl[name];` // this code could be a lot smaller but that is sadly really slow (T_T) function floatSetter(gl, location) { return function (v) { gl.uniform1f(location, v); }; } function floatArraySetter(gl, location) { return function (v) { gl.uniform1fv(location, v); }; } function floatVec2Setter(gl, location) { return function (v) { gl.uniform2fv(location, v); }; } function floatVec3Setter(gl, location) { return function (v) { gl.uniform3fv(location, v); }; } function floatVec4Setter(gl, location) { return function (v) { gl.uniform4fv(location, v); }; } function intSetter(gl, location) { return function (v) { gl.uniform1i(location, v); }; } function intArraySetter(gl, location) { return function (v) { gl.uniform1iv(location, v); }; } function intVec2Setter(gl, location) { return function (v) { gl.uniform2iv(location, v); }; } function intVec3Setter(gl, location) { return function (v) { gl.uniform3iv(location, v); }; } function intVec4Setter(gl, location) { return function (v) { gl.uniform4iv(location, v); }; } function uintSetter(gl, location) { return function (v) { gl.uniform1ui(location, v); }; } function uintArraySetter(gl, location) { return function (v) { gl.uniform1uiv(location, v); }; } function uintVec2Setter(gl, location) { return function (v) { gl.uniform2uiv(location, v); }; } function uintVec3Setter(gl, location) { return function (v) { gl.uniform3uiv(location, v); }; } function uintVec4Setter(gl, location) { return function (v) { gl.uniform4uiv(location, v); }; } function floatMat2Setter(gl, location) { return function (v) { gl.uniformMatrix2fv(location, false, v); }; } function floatMat3Setter(gl, location) { return function (v) { gl.uniformMatrix3fv(location, false, v); }; } function floatMat4Setter(gl, location) { return function (v) { gl.uniformMatrix4fv(location, false, v); }; } function floatMat23Setter(gl, location) { return function (v) { gl.uniformMatrix2x3fv(location, false, v); }; } function floatMat32Setter(gl, location) { return function (v) { gl.uniformMatrix3x2fv(location, false, v); }; } function floatMat24Setter(gl, location) { return function (v) { gl.uniformMatrix2x4fv(location, false, v); }; } function floatMat42Setter(gl, location) { return function (v) { gl.uniformMatrix4x2fv(location, false, v); }; } function floatMat34Setter(gl, location) { return function (v) { gl.uniformMatrix3x4fv(location, false, v); }; } function floatMat43Setter(gl, location) { return function (v) { gl.uniformMatrix4x3fv(location, false, v); }; } function samplerSetter(gl, type, unit, location) { var bindPoint = getBindPointForSamplerType(gl, type); return utils.isWebGL2(gl) ? function (textureOrPair) { var texture; var sampler; if (helper.isTexture(gl, textureOrPair)) { texture = textureOrPair; sampler = null; } else { texture = textureOrPair.texture; sampler = textureOrPair.sampler; } gl.uniform1i(location, unit); gl.activeTexture(TEXTURE0 + unit); gl.bindTexture(bindPoint, texture); gl.bindSampler(unit, sampler); } : function (texture) { gl.uniform1i(location, unit); gl.activeTexture(TEXTURE0 + unit); gl.bindTexture(bindPoint, texture); }; } function samplerArraySetter(gl, type, unit, location, size) { var bindPoint = getBindPointForSamplerType(gl, type); var units = new Int32Array(size); for (var ii = 0; ii < size; ++ii) { units[ii] = unit + ii; } return utils.isWebGL2(gl) ? function (textures) { gl.uniform1iv(location, units); textures.forEach(function (textureOrPair, index) { gl.activeTexture(TEXTURE0 + units[index]); var texture; var sampler; if (helper.isTexture(gl, textureOrPair)) { texture = textureOrPair; sampler = null; } else { texture = textureOrPair.texture; sampler = textureOrPair.sampler; } gl.bindSampler(unit, sampler); gl.bindTexture(bindPoint, texture); }); } : function (textures) { gl.uniform1iv(location, units); textures.forEach(function (texture, index) { gl.activeTexture(TEXTURE0 + units[index]); gl.bindTexture(bindPoint, texture); }); }; } typeMap[FLOAT] = { Type: Float32Array, size: 4, setter: floatSetter, arraySetter: floatArraySetter }; typeMap[FLOAT_VEC2] = { Type: Float32Array, size: 8, setter: floatVec2Setter }; typeMap[FLOAT_VEC3] = { Type: Float32Array, size: 12, setter: floatVec3Setter }; typeMap[FLOAT_VEC4] = { Type: Float32Array, size: 16, setter: floatVec4Setter }; typeMap[INT] = { Type: Int32Array, size: 4, setter: intSetter, arraySetter: intArraySetter }; typeMap[INT_VEC2] = { Type: Int32Array, size: 8, setter: intVec2Setter }; typeMap[INT_VEC3] = { Type: Int32Array, size: 12, setter: intVec3Setter }; typeMap[INT_VEC4] = { Type: Int32Array, size: 16, setter: intVec4Setter }; typeMap[UNSIGNED_INT] = { Type: Uint32Array, size: 4, setter: uintSetter, arraySetter: uintArraySetter }; typeMap[UNSIGNED_INT_VEC2] = { Type: Uint32Array, size: 8, setter: uintVec2Setter }; typeMap[UNSIGNED_INT_VEC3] = { Type: Uint32Array, size: 12, setter: uintVec3Setter }; typeMap[UNSIGNED_INT_VEC4] = { Type: Uint32Array, size: 16, setter: uintVec4Setter }; typeMap[BOOL] = { Type: Uint32Array, size: 4, setter: intSetter, arraySetter: intArraySetter }; typeMap[BOOL_VEC2] = { Type: Uint32Array, size: 8, setter: intVec2Setter }; typeMap[BOOL_VEC3] = { Type: Uint32Array, size: 12, setter: intVec3Setter }; typeMap[BOOL_VEC4] = { Type: Uint32Array, size: 16, setter: intVec4Setter }; typeMap[FLOAT_MAT2] = { Type: Float32Array, size: 16, setter: floatMat2Setter }; typeMap[FLOAT_MAT3] = { Type: Float32Array, size: 36, setter: floatMat3Setter }; typeMap[FLOAT_MAT4] = { Type: Float32Array, size: 64, setter: floatMat4Setter }; typeMap[FLOAT_MAT2x3] = { Type: Float32Array, size: 24, setter: floatMat23Setter }; typeMap[FLOAT_MAT2x4] = { Type: Float32Array, size: 32, setter: floatMat24Setter }; typeMap[FLOAT_MAT3x2] = { Type: Float32Array, size: 24, setter: floatMat32Setter }; typeMap[FLOAT_MAT3x4] = { Type: Float32Array, size: 48, setter: floatMat34Setter }; typeMap[FLOAT_MAT4x2] = { Type: Float32Array, size: 32, setter: floatMat42Setter }; typeMap[FLOAT_MAT4x3] = { Type: Float32Array, size: 48, setter: floatMat43Setter }; typeMap[SAMPLER_2D] = { Type: null, size: 0, setter: samplerSetter, arraySetter: samplerArraySetter, bindPoint: TEXTURE_2D }; typeMap[SAMPLER_CUBE] = { Type: null, size: 0, setter: samplerSetter, arraySetter: samplerArraySetter, bindPoint: TEXTURE_CUBE_MAP }; typeMap[SAMPLER_3D] = { Type: null, size: 0, setter: samplerSetter, arraySetter: samplerArraySetter, bindPoint: TEXTURE_3D }; typeMap[SAMPLER_2D_SHADOW] = { Type: null, size: 0, setter: samplerSetter, arraySetter: samplerArraySetter, bindPoint: TEXTURE_2D }; typeMap[SAMPLER_2D_ARRAY] = { Type: null, size: 0, setter: samplerSetter, arraySetter: samplerArraySetter, bindPoint: TEXTURE_2D_ARRAY }; typeMap[SAMPLER_2D_ARRAY_SHADOW] = { Type: null, size: 0, setter: samplerSetter, arraySetter: samplerArraySetter, bindPoint: TEXTURE_2D_ARRAY }; typeMap[SAMPLER_CUBE_SHADOW] = { Type: null, size: 0, setter: samplerSetter, arraySetter: samplerArraySetter, bindPoint: TEXTURE_CUBE_MAP }; typeMap[INT_SAMPLER_2D] = { Type: null, size: 0, setter: samplerSetter, arraySetter: samplerArraySetter, bindPoint: TEXTURE_2D }; typeMap[INT_SAMPLER_3D] = { Type: null, size: 0, setter: samplerSetter, arraySetter: samplerArraySetter, bindPoint: TEXTURE_3D }; typeMap[INT_SAMPLER_CUBE] = { Type: null, size: 0, setter: samplerSetter, arraySetter: samplerArraySetter, bindPoint: TEXTURE_CUBE_MAP }; typeMap[INT_SAMPLER_2D_ARRAY] = { Type: null, size: 0, setter: samplerSetter, arraySetter: samplerArraySetter, bindPoint: TEXTURE_2D_ARRAY }; typeMap[UNSIGNED_INT_SAMPLER_2D] = { Type: null, size: 0, setter: samplerSetter, arraySetter: samplerArraySetter, bindPoint: TEXTURE_2D }; typeMap[UNSIGNED_INT_SAMPLER_3D] = { Type: null, size: 0, setter: samplerSetter, arraySetter: samplerArraySetter, bindPoint: TEXTURE_3D }; typeMap[UNSIGNED_INT_SAMPLER_CUBE] = { Type: null, size: 0, setter: samplerSetter, arraySetter: samplerArraySetter, bindPoint: TEXTURE_CUBE_MAP }; typeMap[UNSIGNED_INT_SAMPLER_2D_ARRAY] = { Type: null, size: 0, setter: samplerSetter, arraySetter: samplerArraySetter, bindPoint: TEXTURE_2D_ARRAY }; function floatAttribSetter(gl, index) { return function (b) { if (b.value) { gl.disableVertexAttribArray(index); switch (b.value.length) { case 4: gl.vertexAttrib4fv(index, b.value); break; case 3: gl.vertexAttrib3fv(index, b.value); break; case 2: gl.vertexAttrib2fv(index, b.value); break; case 1: gl.vertexAttrib1fv(index, b.value); break; default: throw new Error('the length of a float constant value must be between 1 and 4!'); } } else { gl.bindBuffer(ARRAY_BUFFER, b.buffer); gl.enableVertexAttribArray(index); gl.vertexAttribPointer(index, b.numComponents || b.size, b.type || FLOAT, b.normalize || false, b.stride || 0, b.offset || 0); if (b.divisor !== undefined) { gl.vertexAttribDivisor(index, b.divisor); } } }; } function intAttribSetter(gl, index) { return function (b) { if (b.value) { gl.disableVertexAttribArray(index); if (b.value.length === 4) { gl.vertexAttrib4iv(index, b.value); } else { throw new Error('The length of an integer constant value must be 4!'); } } else { gl.bindBuffer(ARRAY_BUFFER, b.buffer); gl.enableVertexAttribArray(index); gl.vertexAttribIPointer(index, b.numComponents || b.size, b.type || INT, b.stride || 0, b.offset || 0); if (b.divisor !== undefined) { gl.vertexAttribDivisor(index, b.divisor); } } }; } function uintAttribSetter(gl, index) { return function (b) { if (b.value) { gl.disableVertexAttribArray(index); if (b.value.length === 4) { gl.vertexAttrib4uiv(index, b.value); } else { throw new Error('The length of an unsigned integer constant value must be 4!'); } } else { gl.bindBuffer(ARRAY_BUFFER, b.buffer); gl.enableVertexAttribArray(index); gl.vertexAttribIPointer(index, b.numComponents || b.size, b.type || UNSIGNED_INT, b.stride || 0, b.offset || 0); if (b.divisor !== undefined) { gl.vertexAttribDivisor(index, b.divisor); } } }; } function matAttribSetter(gl, index, typeInfo) { var defaultSize = typeInfo.size; var count = typeInfo.count; return function (b) { gl.bindBuffer(ARRAY_BUFFER, b.buffer); var numComponents = b.size || b.numComponents || defaultSize; var size = numComponents / count; var type = b.type || FLOAT; var typeInfo = typeMap[type]; var stride = typeInfo.size * numComponents; var normalize = b.normalize || false; var offset = b.offset || 0; var rowOffset = stride / count; for (var i = 0; i < count; ++i) { gl.enableVertexAttribArray(index + i); gl.vertexAttribPointer(index + i, size, type, normalize, stride, offset + rowOffset * i); if (b.divisor !== undefined) { gl.vertexAttribDivisor(index + i, b.divisor); } } }; } var attrTypeMap = {}; attrTypeMap[FLOAT] = { size: 4, setter: floatAttribSetter }; attrTypeMap[FLOAT_VEC2] = { size: 8, setter: floatAttribSetter }; attrTypeMap[FLOAT_VEC3] = { size: 12, setter: floatAttribSetter }; attrTypeMap[FLOAT_VEC4] = { size: 16, setter: floatAttribSetter }; attrTypeMap[INT] = { size: 4, setter: intAttribSetter }; attrTypeMap[INT_VEC2] = { size: 8, setter: intAttribSetter }; attrTypeMap[INT_VEC3] = { size: 12, setter: intAttribSetter }; attrTypeMap[INT_VEC4] = { size: 16, setter: intAttribSetter }; attrTypeMap[UNSIGNED_INT] = { size: 4, setter: uintAttribSetter }; attrTypeMap[UNSIGNED_INT_VEC2] = { size: 8, setter: uintAttribSetter }; attrTypeMap[UNSIGNED_INT_VEC3] = { size: 12, setter: uintAttribSetter }; attrTypeMap[UNSIGNED_INT_VEC4] = { size: 16, setter: uintAttribSetter }; attrTypeMap[BOOL] = { size: 4, setter: intAttribSetter }; attrTypeMap[BOOL_VEC2] = { size: 8, setter: intAttribSetter }; attrTypeMap[BOOL_VEC3] = { size: 12, setter: intAttribSetter }; attrTypeMap[BOOL_VEC4] = { size: 16, setter: intAttribSetter }; attrTypeMap[FLOAT_MAT2] = { size: 4, setter: matAttribSetter, count: 2 }; attrTypeMap[FLOAT_MAT3] = { size: 9, setter: matAttribSetter, count: 3 }; attrTypeMap[FLOAT_MAT4] = { size: 16, setter: matAttribSetter, count: 4 }; // make sure we don't see a global gl var gl = undefined; /* eslint-disable-line */ /* lgtm [js/unused-local-variable] */ /** * Error Callback * @callback ErrorCallback * @param {string} msg error message. * @param {number} [lineOffset] amount to add to line number * @memberOf module:twgl */ function addLineNumbers(src, lineOffset) { lineOffset = lineOffset || 0; ++lineOffset; return src.split("\n").map(function (line, ndx) { return ndx + lineOffset + ": " + line; }).join("\n"); } var spaceRE = /^[ \t]*\n/; /** * Loads a shader. * @param {WebGLRenderingContext} gl The WebGLRenderingContext to use. * @param {string} shaderSource The shader source. * @param {number} shaderType The type of shader. * @param {module:twgl.ErrorCallback} opt_errorCallback callback for errors. * @return {WebGLShader} The created shader. * @private */ function loadShader(gl, shaderSource, shaderType, opt_errorCallback) { var errFn = opt_errorCallback || error; // Create the shader object var shader = gl.createShader(shaderType); // Remove the first end of line because WebGL 2.0 requires // #version 300 es // as the first line. No whitespace allowed before that line // so // // <script> // #version 300 es // </script> // // Has one line before it which is invalid according to GLSL ES 3.00 // var lineOffset = 0; if (spaceRE.test(shaderSource)) { lineOffset = 1; shaderSource = shaderSource.replace(spaceRE, ''); } // Load the shader source gl.shaderSource(shader, shaderSource); // Compile the shader gl.compileShader(shader); // Check the compile status var compiled = gl.getShaderParameter(shader, COMPILE_STATUS); if (!compiled) { // Something went wrong during compilation; get the error var lastError = gl.getShaderInfoLog(shader); errFn(addLineNumbers(shaderSource, lineOffset) + "\n*** Error compiling shader: " + lastError); gl.deleteShader(shader); return null; } return shader; } /** * @typedef {Object} ProgramOptions * @property {function(string)} [errorCallback] callback for errors * @property {Object.<string,number>} [attribLocations] a attribute name to location map * @property {(module:twgl.BufferInfo|Object.<string,module:twgl.AttribInfo>|string[])} [transformFeedbackVaryings] If passed * a BufferInfo will use the attribs names inside. If passed an object of AttribInfos will use the names from that object. Otherwise * you can pass an array of names. * @property {number} [transformFeedbackMode] the mode to pass `gl.transformFeedbackVaryings`. Defaults to `SEPARATE_ATTRIBS`. * @memberOf module:twgl */ /** * Gets the program options based on all these optional arguments * @param {module:twgl.ProgramOptions|string[]} [opt_attribs] Options for the program or an array of attribs names. Locations will be assigned by index if not passed in * @param {number[]} [opt_locations] The locations for the. A parallel array to opt_attribs letting you assign locations. * @param {module:twgl.ErrorCallback} [opt_errorCallback] callback for errors. By default it just prints an error to the console * on error. If you want something else pass an callback. It's passed an error message. * @return {module:twgl.ProgramOptions} an instance of ProgramOptions based on the arguments passed in * @private */ function getProgramOptions(opt_attribs, opt_locations, opt_errorCallback) { var transformFeedbackVaryings; var transformFeedbackMode; if (typeof opt_locations === 'function') { opt_errorCallback = opt_locations; opt_locations = undefined; } if (typeof opt_attribs === 'function') { opt_errorCallback = opt_attribs; opt_attribs = undefined; } else if (opt_attribs && !Array.isArray(opt_attribs)) { // If we have an errorCallback we can just return this object // Otherwise we need to construct one with default errorCallback if (opt_attribs.errorCallback) { return opt_attribs; } var opt = opt_attribs; opt_errorCallback = opt.errorCallback; opt_attribs = opt.attribLocations; transformFeedbackVaryings = opt.transformFeedbackVaryings; transformFeedbackMode = opt.transformFeedbackMode; } var options = { errorCallback: opt_errorCallback || error, transformFeedbackVaryings: transformFeedbackVaryings, transformFeedbackMode: transformFeedbackMode }; if (opt_attribs) { var attribLocations = {}; if (Array.isArray(opt_attribs)) { opt_attribs.forEach(function (attrib, ndx) { attribLocations[attrib] = opt_locations ? opt_locations[ndx] : ndx; }); } else { attribLocations = opt_attribs; } options.attribLocations = attribLocations; } return options; } var defaultShaderType = ["VERTEX_SHADER", "FRAGMENT_SHADER"]; function getShaderTypeFromScriptType(gl, scriptType) { if (scriptType.indexOf("frag") >= 0) { return FRAGMENT_SHADER; } else if (scriptType.indexOf("vert") >= 0) { return VERTEX_SHADER; } return undefined; } function deleteShaders(gl, shaders) { shaders.forEach(function (shader) { gl.deleteShader(shader); }); } /** * Creates a program, attaches (and/or compiles) shaders, binds attrib locations, links the * program and calls useProgram. * * NOTE: There are 4 signatures for this function * * twgl.createProgram(gl, [vs, fs], options); * twgl.createProgram(gl, [vs, fs], opt_errFunc); * twgl.createProgram(gl, [vs, fs], opt_attribs, opt_errFunc); * twgl.createProgram(gl, [vs, fs], opt_attribs, opt_locations, opt_errFunc); * * @param {WebGLRenderingContext} gl The WebGLRenderingContext to use. * @param {WebGLShader[]|string[]} shaders The shaders to attach, or element ids for their source, or strings that contain their source * @param {module:twgl.ProgramOptions|string[]|module:twgl.ErrorCallback} [opt_attribs] Options for the program or an array of attribs names or an error callback. Locations will be assigned by index if not passed in * @param {number[]} [opt_locations|module:twgl.ErrorCallback] The locations for the. A parallel array to opt_attribs letting you assign locations or an error callback. * @param {module:twgl.ErrorCallback} [opt_errorCallback] callback for errors. By default it just prints an error to the console * on error. If you want something else pass an callback. It's passed an error message. * @return {WebGLProgram?} the created program or null if error. * @memberOf module:twgl/programs */ function createProgram(gl, shaders, opt_attribs, opt_locations, opt_errorCallback) { var progOptions = getProgramOptions(opt_attribs, opt_locations, opt_errorCallback); var realShaders = []; var newShaders = []; for (var ndx = 0; ndx < shaders.length; ++ndx) { var shader = shaders[ndx]; if (typeof shader === 'string') { var elem = getElementById(shader); var src = elem ? elem.text : shader; var type = gl[defaultShaderType[ndx]]; if (elem && elem.type) { type = getShaderTypeFromScriptType(gl, elem.type) || type; } shader = loadShader(gl, src, type, progOptions.errorCallback); newShaders.push(shader); } if (helper.isShader(gl, shader)) { realShaders.push(shader); } } if (realShaders.length !== shaders.length) { progOptions.errorCallback("not enough shaders for program"); deleteShaders(gl, newShaders); return null; } var program = gl.createProgram(); realShaders.forEach(function (shader) { gl.attachShader(program, shader); }); if (progOptions.attribLocations) { Object.keys(progOptions.attribLocations).forEach(function (attrib) { gl.bindAttribLocation(program, progOptions.attribLocations[attrib], attrib); }); } var varyings = progOptions.transformFeedbackVaryings; if (varyings) { if (varyings.attribs) { varyings = varyings.attribs; } if (!Array.isArray(varyings)) { varyings = Object.keys(varyings); } gl.transformFeedbackVaryings(program, varyings, progOptions.transformFeedbackMode || SEPARATE_ATTRIBS); } gl.linkProgram(program); // Check the link status var linked = gl.getProgramParameter(program, LINK_STATUS); if (!linked) { // something went wrong with the link var lastError = gl.getProgramInfoLog(program); progOptions.errorCallback("Error in program linking:" + lastError); gl.deleteProgram(program); deleteShaders(gl, newShaders); return null; } return program; } /** * Loads a shader from a script tag. * @param {WebGLRenderingContext} gl The WebGLRenderingContext to use. * @param {string} scriptId The id of the script tag. * @param {number} [opt_shaderType] The type of shader. If not passed in it will * be derived from the type of the script tag. * @param {module:twgl.ErrorCallback} [opt_errorCallback] callback for errors. * @return {WebGLShader?} The created shader or null if error. * @private */ function createShaderFromScript(gl, scriptId, opt_shaderType, opt_errorCallback) { var shaderSource = ""; var shaderScript = getElementById(scriptId); if (!shaderScript) { throw new Error("unknown script element: ".concat(scriptId)); } shaderSource = shaderScript.text; var shaderType = opt_shaderType || getShaderTypeFromScriptType(gl, shaderScript.type); if (!shaderType) { throw new Error('unknown shader type'); } return loadShader(gl, shaderSource, shaderType, opt_errorCallback); } /** * Creates a program from 2 script tags. * * NOTE: There are 4 signatures for this function * * twgl.createProgramFromScripts(gl, [vs, fs], opt_options); * twgl.createProgramFromScripts(gl, [vs, fs], opt_errFunc); * twgl.createProgramFromScripts(gl, [vs, fs], opt_attribs, opt_errFunc); * twgl.createProgramFromScripts(gl, [vs, fs], opt_attribs, opt_locations, opt_errFunc); * * @param {WebGLRenderingContext} gl The WebGLRenderingContext * to use. * @param {string[]} shaderScriptIds Array of ids of the script * tags for the shaders. The first is assumed to be the * vertex shader, the second the fragment shader. * @param {module:twgl.ProgramOptions|string[]|module:twgl.ErrorCallback} [opt_attribs] Options for the program or an array of attribs names or an error callback. Locations will be assigned by index if not passed in * @param {number[]} [opt_locations|module:twgl.ErrorCallback] The locations for the. A parallel array to opt_attribs letting you assign locations or an error callback. * @param {module:twgl.ErrorCallback} [opt_errorCallback] callback for errors. By default it just prints an error to the console * on error. If you want something else pass an callback. It's passed an error message. * @return {WebGLProgram?} the created program or null if error. * @memberOf module:twgl/programs */ function createProgramFromScripts(gl, shaderScriptIds, opt_attribs, opt_locations, opt_errorCallback) { var progOptions = getProgramOptions(opt_attribs, opt_locations, opt_errorCallback); var shaders = []; for (var ii = 0; ii < shaderScriptIds.length; ++ii) { var shader = createShaderFromScript(gl, shaderScriptIds[ii], gl[defaultShaderType[ii]], progOptions.errorCallback); if (!shader) { return null; } shaders.push(shader); } return createProgram(gl, shaders, progOptions); } /** * Creates a program from 2 sources. * * NOTE: There are 4 signatures for this function * * twgl.createProgramFromSource(gl, [vs, fs], opt_options); * twgl.createProgramFromSource(gl, [vs, fs], opt_errFunc); * twgl.createProgramFromSource(gl, [vs, fs], opt_attribs, opt_errFunc); * twgl.createProgramFromSource(gl, [vs, fs], opt_attribs, opt_locations, opt_errFunc); * * @param {WebGLRenderingContext} gl The WebGLRenderingContext * to use. * @param {string[]} shaderSources Array of sources for the * shaders. The first is assumed to be the vertex shader, * the second the fragment shader. * @param {module:twgl.ProgramOptions|string[]|module:twgl.ErrorCallback} [opt_attribs] Options for the program or an array of attribs names or an error callback. Locations will be assigned by index if not passed in * @param {number[]} [opt_locations|module:twgl.ErrorCallback] The locations for the. A parallel array to opt_attribs letting you assign locations or an error callback. * @param {module:twgl.ErrorCallback} [opt_errorCallback] callback for errors. By default it just prints an error to the console * on error. If you want something else pass an callback. It's passed an error message. * @return {WebGLProgram?} the created program or null if error. * @memberOf module:twgl/programs */ function createProgramFromSources(gl, shaderSources, opt_attribs, opt_locations, opt_errorCallback) { var progOptions = getProgramOptions(opt_attribs, opt_locations, opt_errorCallback); var shaders = []; for (var ii = 0; ii < shaderSources.length; ++ii) { var shader = loadShader(gl, shaderSources[ii], gl[defaultShaderType[ii]], progOptions.errorCallback); if (!shader) { return null; } shaders.push(shader); } return createProgram(gl, shaders, progOptions); } /** * Returns true if attribute/uniform is a reserved/built in * * It makes no sense to me why GL returns these because it's * illegal to call `gl.getUniformLocation` and `gl.getAttribLocation` * with names that start with `gl_` (and `webgl_` in WebGL) * * I can only assume they are there because they might count * when computing the number of uniforms/attributes used when you want to * know if you are near the limit. That doesn't really make sense * to me but the fact that these get returned are in the spec. * * @param {WebGLActiveInfo} info As returned from `gl.getActiveUniform` or * `gl.getActiveAttrib`. * @return {bool} true if it's reserved * @private */ function isBuiltIn(info) { var name = info.name; return name.startsWith("gl_") || name.startsWith("webgl_"); } /** * Creates setter functions for all uniforms of a shader * program. * * @see {@link module:twgl.setUniforms} * * @param {WebGLRenderingContext} gl The WebGLRenderingContext to use. * @param {WebGLProgram} program the program to create setters for. * @returns {Object.<string, function>} an object with a setter by name for each uniform * @memberOf module:twgl/programs */ function createUniformSetters(gl, program) { var textureUnit = 0; /** * Creates a setter for a uniform of the given program with it's * location embedded in the setter. * @param {WebGLProgram} program * @param {WebGLUniformInfo} uniformInfo * @returns {function} the created setter. */ function createUniformSetter(program, uniformInfo) { var location = gl.getUniformLocation(program, uniformInfo.name); var isArray = uniformInfo.size > 1 && uniformInfo.name.substr(-3) === "[0]"; var type = uniformInfo.type; var typeInfo = typeMap[type]; if (!typeInfo) { throw new Error("unknown type: 0x".concat(type.toString(16))); // we should never get here. } var setter; if (typeInfo.bindPoint) { // it's a sampler var unit = textureUnit; textureUnit += uniformInfo.size; if (isArray) { setter = typeInfo.arraySetter(gl, type, unit, location, uniformInfo.size); } else { setter = typeInfo.setter(gl, type, unit, location, uniformInfo.size); } } else { if (typeInfo.arraySetter && isArray) { setter = typeInfo.arraySetter(gl, location); } else { setter = typeInfo.setter(gl, location); } } setter.location = location; return setter; } var uniformSetters = {}; var numUniforms = gl.getProgramParameter(program, ACTIVE_UNIFORMS); for (var ii = 0; ii < numUniforms; ++ii) { var uniformInfo = gl.getActiveUniform(program, ii); if (isBuiltIn(uniformInfo)) { continue; } var name = uniformInfo.name; // remove the array suffix. if (name.substr(-3) === "[0]") { name = name.substr(0, name.length - 3); } var setter = createUniformSetter(program, uniformInfo); uniformSetters[name] = setter; } return uniformSetters; } /** * @typedef {Object} TransformFeedbackInfo * @property {number} index index of transform feedback * @property {number} type GL type * @property {number} size 1 - 4 * @memberOf module:twgl */ /** * Create TransformFeedbackInfo for passing to bindTransformFeedbackInfo. * @param {WebGLRenderingContext} gl The WebGLRenderingContext to use. * @param {WebGLProgram} program an existing WebGLProgram. * @return {Object<string, module:twgl.TransformFeedbackInfo>} * @memberOf module:twgl */ function createTransformFeedbackInfo(gl, program) { var info = {}; var numVaryings = gl.getProgramParameter(program, TRANSFORM_FEEDBACK_VARYINGS); for (var ii = 0; ii < numVaryings; ++ii) { var varying = gl.getTransformFeedbackVarying(program, ii); info[varying.name] = { index: ii, type: varying.type, size: varying.size }; } return info; } /** * Binds buffers for transform feedback. * * @param {WebGLRenderingContext} gl The WebGLRenderingContext to use. * @param {(module:twgl.ProgramInfo|Object<string, module:twgl.TransformFeedbackInfo>)} transformFeedbackInfo A ProgramInfo or TransformFeedbackInfo. * @param {(module:twgl.BufferInfo|Object<string, module:twgl.AttribInfo>)} [bufferInfo] A BufferInfo or set of AttribInfos. * @memberOf module:twgl */ function bindTransformFeedbackInfo(gl, transformFeedbackInfo, bufferInfo) { if (transformFeedbackInfo.transformFeedbackInfo) { transformFeedbackInfo = transformFeedbackInfo.transformFeedbackInfo; } if (bufferInfo.attribs) { bufferInfo = bufferInfo.attribs; } for (var name in bufferInfo) { var varying = transformFeedbackInfo[name]; if (varying) { var buf = bufferInfo[name]; if (buf.offset) { gl.bindBufferRange(TRANSFORM_FEEDBACK_BUFFER, varying.index, buf.buffer, buf.offset, buf.size); } else { gl.bindBufferBase(TRANSFORM_FEEDBACK_BUFFER, varying.index, buf.buffer); } } } } /** * Creates a transform feedback and sets the buffers * @param {WebGLRenderingContext} gl The WebGLRenderingContext to use. * @param {module:twgl.ProgramInfo} programInfo A ProgramInfo as returned from {@link module:twgl.createProgramInfo} * @param {(module:twgl.BufferInfo|Object<string, module:twgl.AttribInfo>)} [bufferInfo] A BufferInfo or set of AttribInfos. * @return {WebGLTransformFeedback} the created transform feedback * @memberOf module:twgl */ function createTransformFeedback(gl, programInfo, bufferInfo) { var tf = gl.createTransformFeedback(); gl.bindTransformFeedback(TRANSFORM_FEEDBACK, tf); gl.useProgram(programInfo.program); bindTransformFeedbackInfo(gl, programInfo, bufferInfo); gl.bindTransformFeedback(TRANSFORM_FEEDBACK, null); return tf; } /** * @typedef {Object} UniformData * @property {number} type The WebGL type enum for this uniform * @property {number} size The number of elements for this uniform * @property {number} blockNdx The block index this uniform appears in * @property {number} offset The byte offset in the block for this uniform's value * @memberOf module:twgl */ /** * The specification for one UniformBlockObject * * @typedef {Object} BlockSpec * @property {number} index The index of the block. * @property {number} size The size in bytes needed for the block * @property {number[]} uniformIndices The indices of the uniforms used by the block. These indices * correspond to entries in a UniformData array in the {@link module:twgl.UniformBlockSpec}. * @property {bool} usedByVertexShader Self explanatory * @property {bool} usedByFragmentShader Self explanatory * @property {bool} used Self explanatory * @memberOf module:twgl */ /** * A `UniformBlockSpec` represents the data needed to create and bind * UniformBlockObjects for a given program * * @typedef {Object} UniformBlockSpec * @property {Object.<string, module:twgl.BlockSpec> blockSpecs The BlockSpec for each block by block name * @property {UniformData[]} uniformData An array of data for each uniform by uniform index. * @memberOf module:twgl */ /** * Creates a UniformBlockSpec for the given program. * * A UniformBlockSpec represents the data needed to create and bind * UniformBlockObjects * * @param {WebGL2RenderingContext} gl A WebGL2 Rendering Context * @param {WebGLProgram} program A WebGLProgram for a successfully linked program * @return {module:twgl.UniformBlockSpec} The created UniformBlockSpec * @memberOf module:twgl/programs */ function createUniformBlockSpecFromProgram(gl, program) { var numUniforms = gl.getProgramParameter(program, ACTIVE_UNIFORMS); var uniformData = []; var uniformIndices = []; for (var ii = 0; ii < numUniforms; ++ii) { uniformIndices.push(ii); uniformData.push({}); var uniformInfo = gl.getActiveUniform(program, ii); if (isBuiltIn(uniformInfo)) { break; } // REMOVE [0]? uniformData[ii].name = uniformInfo.name; } [["UNIFORM_TYPE", "type"], ["UNIFORM_SIZE", "size"], // num elements ["UNIFORM_BLOCK_INDEX", "blockNdx"], ["UNIFORM_OFFSET", "offset"]].forEach(function (pair) { var pname = pair[0]; var key = pair[1]; gl.getActiveUniforms(program, uniformIndices, gl[pname]).forEach(function (value, ndx) { uniformData[ndx][key] = value; }); }); var blockSpecs = {}; var numUniformBlocks = gl.getProgramParameter(program, ACTIVE_UNIFORM_BLOCKS); for (var _ii = 0; _ii < numUniformBlocks; ++_ii) { var name = gl.getActiveUniformBlockName(program, _ii); var blockSpec = { index: _ii, usedByVertexShader: gl.getActiveUniformBlockParameter(program, _ii, UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER), usedByFragmentShader: gl.getActiveUniformBlockParameter(program, _ii, UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER), size: gl.getActiveUniformBlockParameter(program, _ii, UNIFORM_BLOCK_DATA_SIZE), uniformIndices: gl.getActiveUniformBlockParameter(program, _ii, UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES) }; blockSpec.used = blockSpec.usedByVertexShader || blockSpec.usedByFragmentShader; blockSpecs[name] = blockSpec; } return { blockSpecs: blockSpecs, uniformData: uniformData }; } var arraySuffixRE = /\[\d+\]\.$/; // better way to check? /** * Represents a UniformBlockObject including an ArrayBuffer with all the uniform values * and a corresponding WebGLBuffer to hold those values on the GPU * * @typedef {Object} UniformBlockInfo * @property {string} name The name of the block * @property {ArrayBuffer} array The array buffer that contains the uniform values * @property {Float32Array} asFloat A float view on the array buffer. This is useful * inspecting the contents of the buffer in the debugger. * @property {WebGLBuffer} buffer A WebGL buffer that will hold a copy of the uniform values for rendering. * @property {number} [offset] offset into buffer * @property {Object.<string, ArrayBufferView>} uniforms A uniform name to ArrayBufferView map. * each Uniform has a correctly typed `ArrayBufferView` into array at the correct offset * and length of that uniform. So for example a float uniform would have a 1 float `Float32Array` * view. A single mat4 would have a 16 element `Float32Array` view. An ivec2 would have an * `Int32Array` view, etc. * @memberOf module:twgl */ /** * Creates a `UniformBlockInfo` for the specified block * * Note: **If the blockName matches no existing blocks a warning is printed to the console and a dummy * `UniformBlockInfo` is returned**. This is because when debugging GLSL * it is common to comment out large portions of a shader or for example set * the final output to a constant. When that happens blocks get optimized out. * If this function did not create dummy blocks your code would crash when debugging. * * @param {WebGL2RenderingContext} gl A WebGL2RenderingContext * @param {WebGLProgram} program A WebGLProgram * @param {module:twgl.UniformBlockSpec} uniformBlockSpec. A UniformBlockSpec as returned * from {@link module:twgl.createUniformBlockSpecFromProgram}. * @param {string} blockName The name of the block. * @return {module:twgl.UniformBlockInfo} The created UniformBlockInfo * @memberOf module:twgl/programs */ function createUniformBlockInfoFromProgram(gl, program, uniformBlockSpec, blockName) { var blockSpecs = uniformBlockSpec.blockSpecs; var uniformData = uniformBlockSpec.uniformData; var blockSpec = blockSpecs[blockName]; if (!blockSpec) { warn("no uniform block object named:", blockName); return { name: blockName, uniforms: {} }; } var array = new ArrayBuffer(blockSpec.size); var buffer = gl.createBuffer(); var uniformBufferIndex = blockSpec.index; gl.bindBuffer(UNIFORM_BUFFER, buffer); gl.uniformBlockBinding(program, blockSpec.index, uniformBufferIndex); var prefix = blockName + "."; if (arraySuffixRE.test(prefix)) { prefix = prefix.replace(arraySuffixRE, "."); } var uniforms = {}; blockSpec.uniformIndices.forEach(function (uniformNdx) { var data = uniformData[uniformNdx]; var typeInfo = typeMap[data.type]; var Type = typeInfo.Type; var length = data.size * typeInfo.size; var name = data.name; if (name.substr(0, prefix.length) === prefix) { name = name.substr(prefix.length); } uniforms[name] = new Type(array, data.offset, length / Type.BYTES_PER_ELEMENT); }); return { name: blockName, array: array, asFloat: new Float32Array(array), // for debugging buffer: buffer, uniforms: uniforms }; } /** * Creates a `UniformBlockInfo` for the specified block * * Note: **If the blockName matches no existing blocks a warning is printed to the console and a dummy * `UniformBlockInfo` is returned**. This is because when debugging GLSL * it is common to comment out large portions of a shader or for example set * the final output to a constant. When that happens blocks get optimized out. * If this function did not create dummy blocks your code would crash when debugging. * * @param {WebGL2RenderingContext} gl A WebGL2RenderingContext * @param {module:twgl.ProgramInfo} programInfo a `ProgramInfo` * as returned from {@link module:twgl.createProgramInfo} * @param {string} blockName The name of the block. * @return {module:twgl.UniformBlockInfo} The created UniformBlockInfo * @memberOf module:twgl/programs */ function createUniformBlockInfo(gl, programInfo, blockName) { return createUniformBlockInfoFromProgram(gl, programInfo.program, programInfo.uniformBlockSpec, blockName); } /** * Binds a uniform block to the matching uniform block point. * Matches by blocks by name so blocks must have the same name not just the same * structure. * * If you have changed any values and you upload the values into the corresponding WebGLBuffer * call {@link module:twgl.setUniformBlock} instead. * * @param {WebGL2RenderingContext} gl A WebGL 2 rendering context. * @param {(module:twgl.ProgramInfo|module:twgl.UniformBlockSpec)} programInfo a `ProgramInfo` * as returned from {@link module:twgl.createProgramInfo} or or `UniformBlockSpec` as * returned from {@link module:twgl.createUniformBlockSpecFromProgram}. * @param {module:twgl.UniformBlockInfo} uniformBlockInfo a `UniformBlockInfo` as returned from * {@link module:twgl.createUniformBlockInfo}. * @return {bool} true if buffer was bound. If the programInfo has no block with the same block name * no buffer is bound. * @memberOf module:twgl/programs */ function bindUniformBlock(gl, programInfo, uniformBlockInfo) { var uniformBlockSpec = programInfo.uniformBlockSpec || programInfo; var blockSpec = uniformBlockSpec.blockSpecs[uniformBlockInfo.name]; if (blockSpec) { var bufferBindIndex = blockSpec.index; gl.bindBufferRange(UNIFORM_BUFFER, bufferBindIndex, uniformBlockInfo.buffer, uniformBlockInfo.offset || 0, uniformBlockInfo.array.byteLength); return true; } return false; } /** * Uploads the current uniform values to the corresponding WebGLBuffer * and binds that buffer to the program's corresponding bind point for the uniform block object. * * If you haven't changed any values and you only need to bind the uniform block object * call {@link module:twgl.bindUniformBlock} instead. * * @param {WebGL2RenderingContext} gl A WebGL 2 rendering context. * @param {(module:twgl.ProgramInfo|module:twgl.UniformBlockSpec)} programInfo a `ProgramInfo` * as returned from {@link module:twgl.createProgramInfo} or or `UniformBlockSpec` as * returned from {@link module:twgl.createUniformBlockSpecFromProgram}. * @param {module:twgl.UniformBlockInfo} uniformBlockInfo a `UniformBlockInfo` as returned from * {@link module:twgl.createUniformBlockInfo}. * @memberOf module:twgl/programs */ function setUniformBlock(gl, programInfo, uniformBlockInfo) { if (bindUniformBlock(gl, programInfo, uniformBlockInfo)) { gl.bufferData(UNIFORM_BUFFER, uniformBlockInfo.array, DYNAMIC_DRAW); } } /** * Sets values of a uniform block object * * @param {module:twgl.UniformBlockInfo} uniformBlockInfo A UniformBlockInfo as returned by {@link module:twgl.createUniformBlockInfo}. * @param {Object.<string, ?>} values A uniform name to value map where the value is correct for the given * type of uniform. So for example given a block like * * uniform SomeBlock { * float someFloat; * vec2 someVec2; * vec3 someVec3Array[2]; * int someInt; * } * * You can set the values of the uniform block with * * twgl.setBlockUniforms(someBlockInfo, { * someFloat: 12.3, * someVec2: [1, 2], * someVec3Array: [1, 2, 3, 4, 5, 6], * someInt: 5, * } * * Arrays can be JavaScript arrays or typed arrays * * Any name that doesn't match will be ignored * @memberOf module:twgl/programs */ function setBlockUniforms(uniformBlockInfo, values) { var uniforms = uniformBlockInfo.uniforms; for (var name in values) { var array = uniforms[name]; if (array) { var value = values[name]; if (value.length) { array.set(value); } else { array[0] = value; } } } } /** * Set uniforms and binds related textures. * * example: * * const programInfo = createProgramInfo( * gl, ["some-vs", "some-fs"]); * * const tex1 = gl.createTexture(); * const tex2 = gl.createTexture(); * * ... assume we setup the textures with data ... * * const uniforms = { * u_someSampler: tex1, * u_someOtherSampler: tex2, * u_someColor: [1,0,0,1], * u_somePosition: [0,1,1], * u_someMatrix: [ * 1,0,0,0, * 0,1,0,0, * 0,0,1,0, * 0,0,0,0, * ], * }; * * gl.useProgram(program); * * This will automatically bind the textures AND set the * uniforms. * * twgl.setUniforms(programInfo, uniforms); * * For the example above it is equivalent to * * var texUnit = 0; * gl.activeTexture(gl.TEXTURE0 + texUnit); * gl.bindTexture(gl.TEXTURE_2D, tex1); * gl.uniform1i(u_someSamplerLocation, texUnit++); * gl.activeTexture(gl.TEXTURE0 + texUnit); * gl.bindTexture(gl.TEXTURE_2D, tex2); * gl.uniform1i(u_someSamplerLocation, texUnit++); * gl.uniform4fv(u_someColorLocation, [1, 0, 0, 1]); * gl.uniform3fv(u_somePositionLocation, [0, 1, 1]); * gl.uniformMatrix4fv(u_someMatrix, false, [ * 1,0,0,0, * 0,1,0,0, * 0,0,1,0, * 0,0,0,0, * ]); * * Note it is perfectly reasonable to call `setUniforms` multiple times. For example * * const uniforms = { * u_someSampler: tex1, * u_someOtherSampler: tex2, * }; * * const moreUniforms { * u_someColor: [1,0,0,1], * u_somePosition: [0,1,1], * u_someMatrix: [ * 1,0,0,0, * 0,1,0,0, * 0,0,1,0, * 0,0,0,0, * ], * }; * * twgl.setUniforms(programInfo, uniforms); * twgl.setUniforms(programInfo, moreUniforms); * * You can also add WebGLSamplers to uniform samplers as in * * const uniforms = { * u_someSampler: { * texture: someWebGLTexture, * sampler: someWebGLSampler, * }, * }; * * In which case both the sampler and texture will be bound to the * same unit. * * @param {(module:twgl.ProgramInfo|Object.<string, function>)} setters a `ProgramInfo` as returned from `createProgramInfo` or the setters returned from * `createUniformSetters`. * @param {Object.<string, ?>} values an object with values for the * uniforms. * You can pass multiple objects by putting them in an array or by calling with more arguments.For example * * const sharedUniforms = { * u_fogNear: 10, * u_projection: ... * ... * }; * * const localUniforms = { * u_world: ... * u_diffuseColor: ... * }; * * twgl.setUniforms(programInfo, sharedUniforms, localUniforms); * * // is the same as * * twgl.setUniforms(programInfo, [sharedUniforms, localUniforms]); * * // is the same as * * twgl.setUniforms(programInfo, sharedUniforms); * twgl.setUniforms(programInfo, localUniforms}; * * @memberOf module:twgl/programs */ function setUniforms(setters, values) { // eslint-disable-line var actualSetters = setters.uniformSetters || setters; var numArgs = arguments.length; for (var aNdx = 1; aNdx < numArgs; ++aNdx) { var _values = arguments[aNdx]; if (Array.isArray(_values)) { var numValues = _values.length; for (var ii = 0; ii < numValues; ++ii) { setUniforms(actualSetters, _values[ii]); } } else { for (var name in _values) { var setter = actualSetters[name]; if (setter) { setter(_values[name]); } } } } } /** * Alias for `setUniforms` * @function * @param {(module:twgl.ProgramInfo|Object.<string, function>)} setters a `ProgramInfo` as returned from `createProgramInfo` or the setters returned from * `createUniformSetters`. * @param {Object.<string, ?>} values an object with values for the * @memberOf module:twgl/programs */ var setUniformsAndBindTextures = setUniforms; /** * Creates setter functions for all attributes of a shader * program. You can pass this to {@link module:twgl.setBuffersAndAttributes} to set all your buffers and attributes. * * @see {@link module:twgl.setAttributes} for example * @param {WebGLRenderingContext} gl The WebGLRenderingContext to use. * @param {WebGLProgram} program the program to create setters for. * @return {Object.<string, function>} an object with a setter for each attribute by name. * @memberOf module:twgl/programs */ exports.setUniformsAndBindTextures = setUniformsAndBindTextures; function createAttributeSetters(gl, program) { var attribSetters = {}; var numAttribs = gl.getProgramParameter(program, ACTIVE_ATTRIBUTES); for (var ii = 0; ii < numAttribs; ++ii) { var attribInfo = gl.getActiveAttrib(program, ii); if (isBuiltIn(attribInfo)) { continue; } var index = gl.getAttribLocation(program, attribInfo.name); var typeInfo = attrTypeMap[attribInfo.type]; var setter = typeInfo.setter(gl, index, typeInfo); setter.location = index; attribSetters[attribInfo.name] = setter; } return attribSetters; } /** * Sets attributes and binds buffers (deprecated... use {@link module:twgl.setBuffersAndAttributes}) * * Example: * * const program = createProgramFromScripts( * gl, ["some-vs", "some-fs"); * * const attribSetters = createAttributeSetters(program); * * const positionBuffer = gl.createBuffer(); * const texcoordBuffer = gl.createBuffer(); * * const attribs = { * a_position: {buffer: positionBuffer, numComponents: 3}, * a_texcoord: {buffer: texcoordBuffer, numComponents: 2}, * }; * * gl.useProgram(program); * * This will automatically bind the buffers AND set the * attributes. * * setAttributes(attribSetters, attribs); * * Properties of attribs. For each attrib you can add * properties: * * * type: the type of data in the buffer. Default = gl.FLOAT * * normalize: whether or not to normalize the data. Default = false * * stride: the stride. Default = 0 * * offset: offset into the buffer. Default = 0 * * divisor: the divisor for instances. Default = undefined * * For example if you had 3 value float positions, 2 value * float texcoord and 4 value uint8 colors you'd setup your * attribs like this * * const attribs = { * a_position: {buffer: positionBuffer, numComponents: 3}, * a_texcoord: {buffer: texcoordBuffer, numComponents: 2}, * a_color: { * buffer: colorBuffer, * numComponents: 4, * type: gl.UNSIGNED_BYTE, * normalize: true, * }, * }; * * @param {Object.<string, function>} setters Attribute setters as returned from createAttributeSetters * @param {Object.<string, module:twgl.AttribInfo>} buffers AttribInfos mapped by attribute name. * @memberOf module:twgl/programs * @deprecated use {@link module:twgl.setBuffersAndAttributes} */ function setAttributes(setters, buffers) { for (var name in buffers) { var setter = setters[name]; if (setter) { setter(buffers[name]); } } } /** * Sets attributes and buffers including the `ELEMENT_ARRAY_BUFFER` if appropriate * * Example: * * const programInfo = createProgramInfo( * gl, ["some-vs", "some-fs"); * * const arrays = { * position: { numComponents: 3, data: [0, 0, 0, 10, 0, 0, 0, 10, 0, 10, 10, 0], }, * texcoord: { numComponents: 2, data: [0, 0, 0, 1, 1, 0, 1, 1], }, * }; * * const bufferInfo = createBufferInfoFromArrays(gl, arrays); * * gl.useProgram(programInfo.program); * * This will automatically bind the buffers AND set the * attributes. * * setBuffersAndAttributes(gl, programInfo, bufferInfo); * * For the example above it is equivalent to * * gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); * gl.enableVertexAttribArray(a_positionLocation); * gl.vertexAttribPointer(a_positionLocation, 3, gl.FLOAT, false, 0, 0); * gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer); * gl.enableVertexAttribArray(a_texcoordLocation); * gl.vertexAttribPointer(a_texcoordLocation, 4, gl.FLOAT, false, 0, 0); * * @param {WebGLRenderingContext} gl A WebGLRenderingContext. * @param {(module:twgl.ProgramInfo|Object.<string, function>)} setters A `ProgramInfo` as returned from {@link module:twgl.createProgramInfo} or Attribute setters as returned from {@link module:twgl.createAttributeSetters} * @param {(module:twgl.BufferInfo|module:twgl.VertexArrayInfo)} buffers a `BufferInfo` as returned from {@link module:twgl.createBufferInfoFromArrays}. * or a `VertexArrayInfo` as returned from {@link module:twgl.createVertexArrayInfo} * @memberOf module:twgl/programs */ function setBuffersAndAttributes(gl, programInfo, buffers) { if (buffers.vertexArrayObject) { gl.bindVertexArray(buffers.vertexArrayObject); } else { setAttributes(programInfo.attribSetters || programInfo, buffers.attribs); if (buffers.indices) { gl.bindBuffer(ELEMENT_ARRAY_BUFFER, buffers.indices); } } } /** * @typedef {Object} ProgramInfo * @property {WebGLProgram} program A shader program * @property {Object<string, function>} uniformSetters object of setters as returned from createUniformSetters, * @property {Object<string, function>} attribSetters object of setters as returned from createAttribSetters, * @property {module:twgl.UniformBlockSpec} [uniformBlockSpace] a uniform block spec for making UniformBlockInfos with createUniformBlockInfo etc.. * @property {Object<string, module:twgl.TransformFeedbackInfo>} [transformFeedbackInfo] info for transform feedbacks * @memberOf module:twgl */ /** * Creates a ProgramInfo from an existing program. * * A ProgramInfo contains * * programInfo = { * program: WebGLProgram, * uniformSetters: object of setters as returned from createUniformSetters, * attribSetters: object of setters as returned from createAttribSetters, * } * * @param {WebGLRenderingContext} gl The WebGLRenderingContext * to use. * @param {WebGLProgram} program an existing WebGLProgram. * @return {module:twgl.ProgramInfo} The created ProgramInfo. * @memberOf module:twgl/programs */ function createProgramInfoFromProgram(gl, program) { var uniformSetters = createUniformSetters(gl, program); var attribSetters = createAttributeSetters(gl, program); var programInfo = { program: program, uniformSetters: uniformSetters, attribSetters: attribSetters }; if (utils.isWebGL2(gl)) { programInfo.uniformBlockSpec = createUniformBlockSpecFromProgram(gl, program); programInfo.transformFeedbackInfo = createTransformFeedbackInfo(gl, program); } return programInfo; } /** * Creates a ProgramInfo from 2 sources. * * A ProgramInfo contains * * programInfo = { * program: WebGLProgram, * uniformSetters: object of setters as returned from createUniformSetters, * attribSetters: object of setters as returned from createAttribSetters, * } * * NOTE: There are 4 signatures for this function * * twgl.createProgramInfo(gl, [vs, fs], options); * twgl.createProgramInfo(gl, [vs, fs], opt_errFunc); * twgl.createProgramInfo(gl, [vs, fs], opt_attribs, opt_errFunc); * twgl.createProgramInfo(gl, [vs, fs], opt_attribs, opt_locations, opt_errFunc); * * @param {WebGLRenderingContext} gl The WebGLRenderingContext * to use. * @param {string[]} shaderSources Array of sources for the * shaders or ids. The first is assumed to be the vertex shader, * the second the fragment shader. * @param {module:twgl.ProgramOptions|string[]|module:twgl.ErrorCallback} [opt_attribs] Options for the program or an array of attribs names or an error callback. Locations will be assigned by index if not passed in * @param {number[]} [opt_locations|module:twgl.ErrorCallback] The locations for the. A parallel array to opt_attribs letting you assign locations or an error callback. * @param {module:twgl.ErrorCallback} [opt_errorCallback] callback for errors. By default it just prints an error to the console * on error. If you want something else pass an callback. It's passed an error message. * @return {module:twgl.ProgramInfo?} The created ProgramInfo or null if it failed to link or compile * @memberOf module:twgl/programs */ function createProgramInfo(gl, shaderSources, opt_attribs, opt_locations, opt_errorCallback) { var progOptions = getProgramOptions(opt_attribs, opt_locations, opt_errorCallback); var good = true; shaderSources = shaderSources.map(function (source) { // Lets assume if there is no \n it's an id if (source.indexOf("\n") < 0) { var script = getElementById(source); if (!script) { progOptions.errorCallback("no element with id: " + source); good = false; } else { source = script.text; } } return source; }); if (!good) { return null; } var program = createProgramFromSources(gl, shaderSources, progOptions); if (!program) { return null; } return createProgramInfoFromProgram(gl, program); } /***/ }), /***/ "./src/textures.js": /*!*************************!*\ !*** ./src/textures.js ***! \*************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; exports.__esModule = true; exports.setTextureDefaults_ = setDefaults; exports.createSampler = createSampler; exports.createSamplers = createSamplers; exports.setSamplerParameters = setSamplerParameters; exports.createTexture = createTexture; exports.setEmptyTexture = setEmptyTexture; exports.setTextureFromArray = setTextureFromArray; exports.loadTextureFromUrl = loadTextureFromUrl; exports.setTextureFromElement = setTextureFromElement; exports.setTextureFilteringForSize = setTextureFilteringForSize; exports.setTextureParameters = setTextureParameters; exports.setDefaultTextureColor = setDefaultTextureColor; exports.createTextures = createTextures; exports.resizeTexture = resizeTexture; exports.canGenerateMipmap = canGenerateMipmap; exports.canFilter = canFilter; exports.getNumComponentsForFormat = getNumComponentsForFormat; exports.getBytesPerElementForInternalFormat = getBytesPerElementForInternalFormat; exports.getFormatAndTypeForInternalFormat = getFormatAndTypeForInternalFormat; var utils = _interopRequireWildcard(__webpack_require__(/*! ./utils.js */ "./src/utils.js")); var typedArrays = _interopRequireWildcard(__webpack_require__(/*! ./typedarrays.js */ "./src/typedarrays.js")); var helper = _interopRequireWildcard(__webpack_require__(/*! ./helper.js */ "./src/helper.js")); function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function _getRequireWildcardCache() { return cache; }; return cache; } function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; if (obj != null) { var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj["default"] = obj; if (cache) { cache.set(obj, newObj); } return newObj; } /* * Copyright 2019 Gregg Tavares * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ /** * Low level texture related functions * * You should generally not need to use these functions. They are provided * for those cases where you're doing something out of the ordinary * and you need lower level access. * * For backward compatibility they are available at both `twgl.textures` and `twgl` * itself * * See {@link module:twgl} for core functions * * @module twgl/textures */ // make sure we don't see a global gl var gl = undefined; /* eslint-disable-line */ /* lgtm [js/unused-local-variable] */ var defaults = { textureColor: new Uint8Array([128, 192, 255, 255]), textureOptions: {}, crossOrigin: undefined }; var isArrayBuffer = typedArrays.isArrayBuffer; // Should we make this on demand? var s_ctx; function getShared2DContext() { s_ctx = s_ctx || (typeof document !== 'undefined' && document.createElement ? document.createElement("canvas").getContext("2d") : null); return s_ctx; } // NOTE: Chrome supports 2D canvas in a Worker (behind flag as of v64 but // not only does Firefox NOT support it but Firefox freezes immediately // if you try to create one instead of just returning null and continuing. // : (global.OffscreenCanvas && (new global.OffscreenCanvas(1, 1)).getContext("2d")); // OffscreenCanvas may not support 2d // NOTE: We can maybe remove some of the need for the 2d canvas. In WebGL2 // we can use the various unpack settings. Otherwise we could try using // the ability of an ImageBitmap to be cut. Unfortunately cutting an ImageBitmap // is async and the current TWGL code expects a non-Async result though that // might not be a problem. ImageBitmap though is not available in Edge or Safari // as of 2018-01-02 /* PixelFormat */ var ALPHA = 0x1906; var RGB = 0x1907; var RGBA = 0x1908; var LUMINANCE = 0x1909; var LUMINANCE_ALPHA = 0x190A; var DEPTH_COMPONENT = 0x1902; var DEPTH_STENCIL = 0x84F9; /* TextureWrapMode */ // const REPEAT = 0x2901; // const MIRRORED_REPEAT = 0x8370; var CLAMP_TO_EDGE = 0x812f; /* TextureMagFilter */ var NEAREST = 0x2600; var LINEAR = 0x2601; /* TextureMinFilter */ // const NEAREST_MIPMAP_NEAREST = 0x2700; // const LINEAR_MIPMAP_NEAREST = 0x2701; // const NEAREST_MIPMAP_LINEAR = 0x2702; // const LINEAR_MIPMAP_LINEAR = 0x2703; /* Texture Target */ var TEXTURE_2D = 0x0de1; var TEXTURE_CUBE_MAP = 0x8513; var TEXTURE_3D = 0x806f; var TEXTURE_2D_ARRAY = 0x8c1a; /* Cubemap Targets */ var TEXTURE_CUBE_MAP_POSITIVE_X = 0x8515; var TEXTURE_CUBE_MAP_NEGATIVE_X = 0x8516; var TEXTURE_CUBE_MAP_POSITIVE_Y = 0x8517; var TEXTURE_CUBE_MAP_NEGATIVE_Y = 0x8518; var TEXTURE_CUBE_MAP_POSITIVE_Z = 0x8519; var TEXTURE_CUBE_MAP_NEGATIVE_Z = 0x851a; /* Texture Parameters */ var TEXTURE_MIN_FILTER = 0x2801; var TEXTURE_MAG_FILTER = 0x2800; var TEXTURE_WRAP_S = 0x2802; var TEXTURE_WRAP_T = 0x2803; var TEXTURE_WRAP_R = 0x8072; var TEXTURE_MIN_LOD = 0x813a; var TEXTURE_MAX_LOD = 0x813b; var TEXTURE_BASE_LEVEL = 0x813c; var TEXTURE_MAX_LEVEL = 0x813d; /* Pixel store */ var UNPACK_ALIGNMENT = 0x0cf5; var UNPACK_ROW_LENGTH = 0x0cf2; var UNPACK_IMAGE_HEIGHT = 0x806e; var UNPACK_SKIP_PIXELS = 0x0cf4; var UNPACK_SKIP_ROWS = 0x0cf3; var UNPACK_SKIP_IMAGES = 0x806d; var UNPACK_COLORSPACE_CONVERSION_WEBGL = 0x9243; var UNPACK_PREMULTIPLY_ALPHA_WEBGL = 0x9241; var UNPACK_FLIP_Y_WEBGL = 0x9240; var R8 = 0x8229; var R8_SNORM = 0x8F94; var R16F = 0x822D; var R32F = 0x822E; var R8UI = 0x8232; var R8I = 0x8231; var RG16UI = 0x823A; var RG16I = 0x8239; var RG32UI = 0x823C; var RG32I = 0x823B; var RG8 = 0x822B; var RG8_SNORM = 0x8F95; var RG16F = 0x822F; var RG32F = 0x8230; var RG8UI = 0x8238; var RG8I = 0x8237; var R16UI = 0x8234; var R16I = 0x8233; var R32UI = 0x8236; var R32I = 0x8235; var RGB8 = 0x8051; var SRGB8 = 0x8C41; var RGB565 = 0x8D62; var RGB8_SNORM = 0x8F96; var R11F_G11F_B10F = 0x8C3A; var RGB9_E5 = 0x8C3D; var RGB16F = 0x881B; var RGB32F = 0x8815; var RGB8UI = 0x8D7D; var RGB8I = 0x8D8F; var RGB16UI = 0x8D77; var RGB16I = 0x8D89; var RGB32UI = 0x8D71; var RGB32I = 0x8D83; var RGBA8 = 0x8058; var SRGB8_ALPHA8 = 0x8C43; var RGBA8_SNORM = 0x8F97; var RGB5_A1 = 0x8057; var RGBA4 = 0x8056; var RGB10_A2 = 0x8059; var RGBA16F = 0x881A; var RGBA32F = 0x8814; var RGBA8UI = 0x8D7C; var RGBA8I = 0x8D8E; var RGB10_A2UI = 0x906F; var RGBA16UI = 0x8D76; var RGBA16I = 0x8D88; var RGBA32I = 0x8D82; var RGBA32UI = 0x8D70; var DEPTH_COMPONENT16 = 0x81A5; var DEPTH_COMPONENT24 = 0x81A6; var DEPTH_COMPONENT32F = 0x8CAC; var DEPTH32F_STENCIL8 = 0x8CAD; var DEPTH24_STENCIL8 = 0x88F0; /* DataType */ var BYTE = 0x1400; var UNSIGNED_BYTE = 0x1401; var SHORT = 0x1402; var UNSIGNED_SHORT = 0x1403; var INT = 0x1404; var UNSIGNED_INT = 0x1405; var FLOAT = 0x1406; var UNSIGNED_SHORT_4_4_4_4 = 0x8033; var UNSIGNED_SHORT_5_5_5_1 = 0x8034; var UNSIGNED_SHORT_5_6_5 = 0x8363; var HALF_FLOAT = 0x140B; var HALF_FLOAT_OES = 0x8D61; // Thanks Khronos for making this different >:( var UNSIGNED_INT_2_10_10_10_REV = 0x8368; var UNSIGNED_INT_10F_11F_11F_REV = 0x8C3B; var UNSIGNED_INT_5_9_9_9_REV = 0x8C3E; var FLOAT_32_UNSIGNED_INT_24_8_REV = 0x8DAD; var UNSIGNED_INT_24_8 = 0x84FA; var RG = 0x8227; var RG_INTEGER = 0x8228; var RED = 0x1903; var RED_INTEGER = 0x8D94; var RGB_INTEGER = 0x8D98; var RGBA_INTEGER = 0x8D99; var formatInfo = {}; { // NOTE: this is named `numColorComponents` vs `numComponents` so we can let Uglify mangle // the name. var f = formatInfo; f[ALPHA] = { numColorComponents: 1 }; f[LUMINANCE] = { numColorComponents: 1 }; f[LUMINANCE_ALPHA] = { numColorComponents: 2 }; f[RGB] = { numColorComponents: 3 }; f[RGBA] = { numColorComponents: 4 }; f[RED] = { numColorComponents: 1 }; f[RED_INTEGER] = { numColorComponents: 1 }; f[RG] = { numColorComponents: 2 }; f[RG_INTEGER] = { numColorComponents: 2 }; f[RGB] = { numColorComponents: 3 }; f[RGB_INTEGER] = { numColorComponents: 3 }; f[RGBA] = { numColorComponents: 4 }; f[RGBA_INTEGER] = { numColorComponents: 4 }; f[DEPTH_COMPONENT] = { numColorComponents: 1 }; f[DEPTH_STENCIL] = { numColorComponents: 2 }; } /** * @typedef {Object} TextureFormatDetails * @property {number} textureFormat format to pass texImage2D and similar functions. * @property {boolean} colorRenderable true if you can render to this format of texture. * @property {boolean} textureFilterable true if you can filter the texture, false if you can ony use `NEAREST`. * @property {number[]} type Array of possible types you can pass to texImage2D and similar function * @property {Object.<number,number>} bytesPerElementMap A map of types to bytes per element * @private */ var s_textureInternalFormatInfo; function getTextureInternalFormatInfo(internalFormat) { if (!s_textureInternalFormatInfo) { // NOTE: these properties need unique names so we can let Uglify mangle the name. var t = {}; // unsized formats t[ALPHA] = { textureFormat: ALPHA, colorRenderable: true, textureFilterable: true, bytesPerElement: [1, 2, 2, 4], type: [UNSIGNED_BYTE, HALF_FLOAT, HALF_FLOAT_OES, FLOAT] }; t[LUMINANCE] = { textureFormat: LUMINANCE, colorRenderable: true, textureFilterable: true, bytesPerElement: [1, 2, 2, 4], type: [UNSIGNED_BYTE, HALF_FLOAT, HALF_FLOAT_OES, FLOAT] }; t[LUMINANCE_ALPHA] = { textureFormat: LUMINANCE_ALPHA, colorRenderable: true, textureFilterable: true, bytesPerElement: [2, 4, 4, 8], type: [UNSIGNED_BYTE, HALF_FLOAT, HALF_FLOAT_OES, FLOAT] }; t[RGB] = { textureFormat: RGB, colorRenderable: true, textureFilterable: true, bytesPerElement: [3, 6, 6, 12, 2], type: [UNSIGNED_BYTE, HALF_FLOAT, HALF_FLOAT_OES, FLOAT, UNSIGNED_SHORT_5_6_5] }; t[RGBA] = { textureFormat: RGBA, colorRenderable: true, textureFilterable: true, bytesPerElement: [4, 8, 8, 16, 2, 2], type: [UNSIGNED_BYTE, HALF_FLOAT, HALF_FLOAT_OES, FLOAT, UNSIGNED_SHORT_4_4_4_4, UNSIGNED_SHORT_5_5_5_1] }; // sized formats t[R8] = { textureFormat: RED, colorRenderable: true, textureFilterable: true, bytesPerElement: [1], type: [UNSIGNED_BYTE] }; t[R8_SNORM] = { textureFormat: RED, colorRenderable: false, textureFilterable: true, bytesPerElement: [1], type: [BYTE] }; t[R16F] = { textureFormat: RED, colorRenderable: false, textureFilterable: true, bytesPerElement: [4, 2], type: [FLOAT, HALF_FLOAT] }; t[R32F] = { textureFormat: RED, colorRenderable: false, textureFilterable: false, bytesPerElement: [4], type: [FLOAT] }; t[R8UI] = { textureFormat: RED_INTEGER, colorRenderable: true, textureFilterable: false, bytesPerElement: [1], type: [UNSIGNED_BYTE] }; t[R8I] = { textureFormat: RED_INTEGER, colorRenderable: true, textureFilterable: false, bytesPerElement: [1], type: [BYTE] }; t[R16UI] = { textureFormat: RED_INTEGER, colorRenderable: true, textureFilterable: false, bytesPerElement: [2], type: [UNSIGNED_SHORT] }; t[R16I] = { textureFormat: RED_INTEGER, colorRenderable: true, textureFilterable: false, bytesPerElement: [2], type: [SHORT] }; t[R32UI] = { textureFormat: RED_INTEGER, colorRenderable: true, textureFilterable: false, bytesPerElement: [4], type: [UNSIGNED_INT] }; t[R32I] = { textureFormat: RED_INTEGER, colorRenderable: true, textureFilterable: false, bytesPerElement: [4], type: [INT] }; t[RG8] = { textureFormat: RG, colorRenderable: true, textureFilterable: true, bytesPerElement: [2], type: [UNSIGNED_BYTE] }; t[RG8_SNORM] = { textureFormat: RG, colorRenderable: false, textureFilterable: true, bytesPerElement: [2], type: [BYTE] }; t[RG16F] = { textureFormat: RG, colorRenderable: false, textureFilterable: true, bytesPerElement: [8, 4], type: [FLOAT, HALF_FLOAT] }; t[RG32F] = { textureFormat: RG, colorRenderable: false, textureFilterable: false, bytesPerElement: [8], type: [FLOAT] }; t[RG8UI] = { textureFormat: RG_INTEGER, colorRenderable: true, textureFilterable: false, bytesPerElement: [2], type: [UNSIGNED_BYTE] }; t[RG8I] = { textureFormat: RG_INTEGER, colorRenderable: true, textureFilterable: false, bytesPerElement: [2], type: [BYTE] }; t[RG16UI] = { textureFormat: RG_INTEGER, colorRenderable: true, textureFilterable: false, bytesPerElement: [4], type: [UNSIGNED_SHORT] }; t[RG16I] = { textureFormat: RG_INTEGER, colorRenderable: true, textureFilterable: false, bytesPerElement: [4], type: [SHORT] }; t[RG32UI] = { textureFormat: RG_INTEGER, colorRenderable: true, textureFilterable: false, bytesPerElement: [8], type: [UNSIGNED_INT] }; t[RG32I] = { textureFormat: RG_INTEGER, colorRenderable: true, textureFilterable: false, bytesPerElement: [8], type: [INT] }; t[RGB8] = { textureFormat: RGB, colorRenderable: true, textureFilterable: true, bytesPerElement: [3], type: [UNSIGNED_BYTE] }; t[SRGB8] = { textureFormat: RGB, colorRenderable: false, textureFilterable: true, bytesPerElement: [3], type: [UNSIGNED_BYTE] }; t[RGB565] = { textureFormat: RGB, colorRenderable: true, textureFilterable: true, bytesPerElement: [3, 2], type: [UNSIGNED_BYTE, UNSIGNED_SHORT_5_6_5] }; t[RGB8_SNORM] = { textureFormat: RGB, colorRenderable: false, textureFilterable: true, bytesPerElement: [3], type: [BYTE] }; t[R11F_G11F_B10F] = { textureFormat: RGB, colorRenderable: false, textureFilterable: true, bytesPerElement: [12, 6, 4], type: [FLOAT, HALF_FLOAT, UNSIGNED_INT_10F_11F_11F_REV] }; t[RGB9_E5] = { textureFormat: RGB, colorRenderable: false, textureFilterable: true, bytesPerElement: [12, 6, 4], type: [FLOAT, HALF_FLOAT, UNSIGNED_INT_5_9_9_9_REV] }; t[RGB16F] = { textureFormat: RGB, colorRenderable: false, textureFilterable: true, bytesPerElement: [12, 6], type: [FLOAT, HALF_FLOAT] }; t[RGB32F] = { textureFormat: RGB, colorRenderable: false, textureFilterable: false, bytesPerElement: [12], type: [FLOAT] }; t[RGB8UI] = { textureFormat: RGB_INTEGER, colorRenderable: false, textureFilterable: false, bytesPerElement: [3], type: [UNSIGNED_BYTE] }; t[RGB8I] = { textureFormat: RGB_INTEGER, colorRenderable: false, textureFilterable: false, bytesPerElement: [3], type: [BYTE] }; t[RGB16UI] = { textureFormat: RGB_INTEGER, colorRenderable: false, textureFilterable: false, bytesPerElement: [6], type: [UNSIGNED_SHORT] }; t[RGB16I] = { textureFormat: RGB_INTEGER, colorRenderable: false, textureFilterable: false, bytesPerElement: [6], type: [SHORT] }; t[RGB32UI] = { textureFormat: RGB_INTEGER, colorRenderable: false, textureFilterable: false, bytesPerElement: [12], type: [UNSIGNED_INT] }; t[RGB32I] = { textureFormat: RGB_INTEGER, colorRenderable: false, textureFilterable: false, bytesPerElement: [12], type: [INT] }; t[RGBA8] = { textureFormat: RGBA, colorRenderable: true, textureFilterable: true, bytesPerElement: [4], type: [UNSIGNED_BYTE] }; t[SRGB8_ALPHA8] = { textureFormat: RGBA, colorRenderable: true, textureFilterable: true, bytesPerElement: [4], type: [UNSIGNED_BYTE] }; t[RGBA8_SNORM] = { textureFormat: RGBA, colorRenderable: false, textureFilterable: true, bytesPerElement: [4], type: [BYTE] }; t[RGB5_A1] = { textureFormat: RGBA, colorRenderable: true, textureFilterable: true, bytesPerElement: [4, 2, 4], type: [UNSIGNED_BYTE, UNSIGNED_SHORT_5_5_5_1, UNSIGNED_INT_2_10_10_10_REV] }; t[RGBA4] = { textureFormat: RGBA, colorRenderable: true, textureFilterable: true, bytesPerElement: [4, 2], type: [UNSIGNED_BYTE, UNSIGNED_SHORT_4_4_4_4] }; t[RGB10_A2] = { textureFormat: RGBA, colorRenderable: true, textureFilterable: true, bytesPerElement: [4], type: [UNSIGNED_INT_2_10_10_10_REV] }; t[RGBA16F] = { textureFormat: RGBA, colorRenderable: false, textureFilterable: true, bytesPerElement: [16, 8], type: [FLOAT, HALF_FLOAT] }; t[RGBA32F] = { textureFormat: RGBA, colorRenderable: false, textureFilterable: false, bytesPerElement: [16], type: [FLOAT] }; t[RGBA8UI] = { textureFormat: RGBA_INTEGER, colorRenderable: true, textureFilterable: false, bytesPerElement: [4], type: [UNSIGNED_BYTE] }; t[RGBA8I] = { textureFormat: RGBA_INTEGER, colorRenderable: true, textureFilterable: false, bytesPerElement: [4], type: [BYTE] }; t[RGB10_A2UI] = { textureFormat: RGBA_INTEGER, colorRenderable: true, textureFilterable: false, bytesPerElement: [4], type: [UNSIGNED_INT_2_10_10_10_REV] }; t[RGBA16UI] = { textureFormat: RGBA_INTEGER, colorRenderable: true, textureFilterable: false, bytesPerElement: [8], type: [UNSIGNED_SHORT] }; t[RGBA16I] = { textureFormat: RGBA_INTEGER, colorRenderable: true, textureFilterable: false, bytesPerElement: [8], type: [SHORT] }; t[RGBA32I] = { textureFormat: RGBA_INTEGER, colorRenderable: true, textureFilterable: false, bytesPerElement: [16], type: [INT] }; t[RGBA32UI] = { textureFormat: RGBA_INTEGER, colorRenderable: true, textureFilterable: false, bytesPerElement: [16], type: [UNSIGNED_INT] }; // Sized Internal t[DEPTH_COMPONENT16] = { textureFormat: DEPTH_COMPONENT, colorRenderable: true, textureFilterable: false, bytesPerElement: [2, 4], type: [UNSIGNED_SHORT, UNSIGNED_INT] }; t[DEPTH_COMPONENT24] = { textureFormat: DEPTH_COMPONENT, colorRenderable: true, textureFilterable: false, bytesPerElement: [4], type: [UNSIGNED_INT] }; t[DEPTH_COMPONENT32F] = { textureFormat: DEPTH_COMPONENT, colorRenderable: true, textureFilterable: false, bytesPerElement: [4], type: [FLOAT] }; t[DEPTH24_STENCIL8] = { textureFormat: DEPTH_STENCIL, colorRenderable: true, textureFilterable: false, bytesPerElement: [4], type: [UNSIGNED_INT_24_8] }; t[DEPTH32F_STENCIL8] = { textureFormat: DEPTH_STENCIL, colorRenderable: true, textureFilterable: false, bytesPerElement: [4], type: [FLOAT_32_UNSIGNED_INT_24_8_REV] }; Object.keys(t).forEach(function (internalFormat) { var info = t[internalFormat]; info.bytesPerElementMap = {}; info.bytesPerElement.forEach(function (bytesPerElement, ndx) { var type = info.type[ndx]; info.bytesPerElementMap[type] = bytesPerElement; }); }); s_textureInternalFormatInfo = t; } return s_textureInternalFormatInfo[internalFormat]; } /** * Gets the number of bytes per element for a given internalFormat / type * @param {number} internalFormat The internalFormat parameter from texImage2D etc.. * @param {number} type The type parameter for texImage2D etc.. * @return {number} the number of bytes per element for the given internalFormat, type combo * @memberOf module:twgl/textures */ function getBytesPerElementForInternalFormat(internalFormat, type) { var info = getTextureInternalFormatInfo(internalFormat); if (!info) { throw "unknown internal format"; } var bytesPerElement = info.bytesPerElementMap[type]; if (bytesPerElement === undefined) { throw "unknown internal format"; } return bytesPerElement; } /** * Info related to a specific texture internalFormat as returned * from {@link module:twgl/textures.getFormatAndTypeForInternalFormat}. * * @typedef {Object} TextureFormatInfo * @property {number} format Format to pass to texImage2D and related functions * @property {number} type Type to pass to texImage2D and related functions * @memberOf module:twgl/textures */ /** * Gets the format and type for a given internalFormat * * @param {number} internalFormat The internal format * @return {module:twgl/textures.TextureFormatInfo} the corresponding format and type, * @memberOf module:twgl/textures */ function getFormatAndTypeForInternalFormat(internalFormat) { var info = getTextureInternalFormatInfo(internalFormat); if (!info) { throw "unknown internal format"; } return { format: info.textureFormat, type: info.type[0] }; } /** * Returns true if value is power of 2 * @param {number} value number to check. * @return true if value is power of 2 * @private */ function isPowerOf2(value) { return (value & value - 1) === 0; } /** * Gets whether or not we can generate mips for the given * internal format. * * @param {WebGLRenderingContext} gl the WebGLRenderingContext * @param {number} width The width parameter from texImage2D etc.. * @param {number} height The height parameter from texImage2D etc.. * @param {number} internalFormat The internalFormat parameter from texImage2D etc.. * @return {boolean} true if we can generate mips * @memberOf module:twgl/textures */ function canGenerateMipmap(gl, width, height, internalFormat) { if (!utils.isWebGL2(gl)) { return isPowerOf2(width) && isPowerOf2(height); } var info = getTextureInternalFormatInfo(internalFormat); if (!info) { throw "unknown internal format"; } return info.colorRenderable && info.textureFilterable; } /** * Gets whether or not we can generate mips for the given format * @param {number} internalFormat The internalFormat parameter from texImage2D etc.. * @return {boolean} true if we can generate mips * @memberOf module:twgl/textures */ function canFilter(internalFormat) { var info = getTextureInternalFormatInfo(internalFormat); if (!info) { throw "unknown internal format"; } return info.textureFilterable; } /** * Gets the number of components for a given image format. * @param {number} format the format. * @return {number} the number of components for the format. * @memberOf module:twgl/textures */ function getNumComponentsForFormat(format) { var info = formatInfo[format]; if (!info) { throw "unknown format: " + format; } return info.numColorComponents; } /** * Gets the texture type for a given array type. * @param {WebGLRenderingContext} gl the WebGLRenderingContext * @return {number} the gl texture type * @private */ function getTextureTypeForArrayType(gl, src, defaultType) { if (isArrayBuffer(src)) { return typedArrays.getGLTypeForTypedArray(src); } return defaultType || UNSIGNED_BYTE; } function guessDimensions(gl, target, width, height, numElements) { if (numElements % 1 !== 0) { throw "can't guess dimensions"; } if (!width && !height) { var size = Math.sqrt(numElements / (target === TEXTURE_CUBE_MAP ? 6 : 1)); if (size % 1 === 0) { width = size; height = size; } else { width = numElements; height = 1; } } else if (!height) { height = numElements / width; if (height % 1) { throw "can't guess dimensions"; } } else if (!width) { width = numElements / height; if (width % 1) { throw "can't guess dimensions"; } } return { width: width, height: height }; } /** * Sets the default texture color. * * The default texture color is used when loading textures from * urls. Because the URL will be loaded async we'd like to be * able to use the texture immediately. By putting a 1x1 pixel * color in the texture we can start using the texture before * the URL has loaded. * * @param {number[]} color Array of 4 values in the range 0 to 1 * @deprecated see {@link module:twgl.setDefaults} * @memberOf module:twgl/textures */ function setDefaultTextureColor(color) { defaults.textureColor = new Uint8Array([color[0] * 255, color[1] * 255, color[2] * 255, color[3] * 255]); } function setDefaults(newDefaults) { helper.copyExistingProperties(newDefaults, defaults); if (newDefaults.textureColor) { setDefaultTextureColor(newDefaults.textureColor); } } /** * A function to generate the source for a texture. * @callback TextureFunc * @param {WebGLRenderingContext} gl A WebGLRenderingContext * @param {module:twgl.TextureOptions} options the texture options * @return {*} Returns any of the things documented for `src` for {@link module:twgl.TextureOptions}. * @memberOf module:twgl */ /** * Texture options passed to most texture functions. Each function will use whatever options * are appropriate for its needs. This lets you pass the same options to all functions. * * Note: A `TexImageSource` is defined in the WebGL spec as a `HTMLImageElement`, `HTMLVideoElement`, * `HTMLCanvasElement`, `ImageBitmap`, or `ImageData`. * * @typedef {Object} TextureOptions * @property {number} [target] the type of texture `gl.TEXTURE_2D` or `gl.TEXTURE_CUBE_MAP`. Defaults to `gl.TEXTURE_2D`. * @property {number} [level] the mip level to affect. Defaults to 0. Note, if set auto will be considered false unless explicitly set to true. * @property {number} [width] the width of the texture. Only used if src is an array or typed array or null. * @property {number} [height] the height of a texture. Only used if src is an array or typed array or null. * @property {number} [depth] the depth of a texture. Only used if src is an array or type array or null and target is `TEXTURE_3D` . * @property {number} [min] the min filter setting (eg. `gl.LINEAR`). Defaults to `gl.NEAREST_MIPMAP_LINEAR` * or if texture is not a power of 2 on both dimensions then defaults to `gl.LINEAR`. * @property {number} [mag] the mag filter setting (eg. `gl.LINEAR`). Defaults to `gl.LINEAR` * @property {number} [minMag] both the min and mag filter settings. * @property {number} [internalFormat] internal format for texture. Defaults to `gl.RGBA` * @property {number} [format] format for texture. Defaults to `gl.RGBA`. * @property {number} [type] type for texture. Defaults to `gl.UNSIGNED_BYTE` unless `src` is ArrayBufferView. If `src` * is ArrayBufferView defaults to type that matches ArrayBufferView type. * @property {number} [wrap] Texture wrapping for both S and T (and R if TEXTURE_3D or WebGLSampler). Defaults to `gl.REPEAT` for 2D unless src is WebGL1 and src not npot and `gl.CLAMP_TO_EDGE` for cube * @property {number} [wrapS] Texture wrapping for S. Defaults to `gl.REPEAT` and `gl.CLAMP_TO_EDGE` for cube. If set takes precedence over `wrap`. * @property {number} [wrapT] Texture wrapping for T. Defaults to `gl.REPEAT` and `gl.CLAMP_TO_EDGE` for cube. If set takes precedence over `wrap`. * @property {number} [wrapR] Texture wrapping for R. Defaults to `gl.REPEAT` and `gl.CLAMP_TO_EDGE` for cube. If set takes precedence over `wrap`. * @property {number} [minLod] TEXTURE_MIN_LOD setting * @property {number} [maxLod] TEXTURE_MAX_LOD setting * @property {number} [baseLevel] TEXTURE_BASE_LEVEL setting * @property {number} [maxLevel] TEXTURE_MAX_LEVEL setting * @property {number} [unpackAlignment] The `gl.UNPACK_ALIGNMENT` used when uploading an array. Defaults to 1. * @property {number[]|ArrayBufferView} [color] Color to initialize this texture with if loading an image asynchronously. * The default use a blue 1x1 pixel texture. You can set another default by calling `twgl.setDefaults` * or you can set an individual texture's initial color by setting this property. Example: `[1, .5, .5, 1]` = pink * @property {number} [premultiplyAlpha] Whether or not to premultiply alpha. Defaults to whatever the current setting is. * This lets you set it once before calling `twgl.createTexture` or `twgl.createTextures` and only override * the current setting for specific textures. * @property {number} [flipY] Whether or not to flip the texture vertically on upload. Defaults to whatever the current setting is. * This lets you set it once before calling `twgl.createTexture` or `twgl.createTextures` and only override * the current setting for specific textures. * @property {number} [colorspaceConversion] Whether or not to let the browser do colorspace conversion of the texture on upload. Defaults to whatever the current setting is. * This lets you set it once before calling `twgl.createTexture` or `twgl.createTextures` and only override * the current setting for specific textures. * @property {boolean} [auto] If `undefined` or `true`, in WebGL1, texture filtering is set automatically for non-power of 2 images and * mips are generated for power of 2 images. In WebGL2 mips are generated if they can be. Note: if `level` is set above * then then `auto` is assumed to be `false` unless explicity set to `true`. * @property {number[]} [cubeFaceOrder] The order that cube faces are pulled out of an img or set of images. The default is * * [gl.TEXTURE_CUBE_MAP_POSITIVE_X, * gl.TEXTURE_CUBE_MAP_NEGATIVE_X, * gl.TEXTURE_CUBE_MAP_POSITIVE_Y, * gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, * gl.TEXTURE_CUBE_MAP_POSITIVE_Z, * gl.TEXTURE_CUBE_MAP_NEGATIVE_Z] * * @property {(number[]|ArrayBufferView|TexImageSource|TexImageSource[]|string|string[]|module:twgl.TextureFunc)} [src] source for texture * * If `string` then it's assumed to be a URL to an image. The image will be downloaded async. A usable * 1x1 pixel texture will be returned immediately. The texture will be updated once the image has downloaded. * If `target` is `gl.TEXTURE_CUBE_MAP` will attempt to divide image into 6 square pieces. 1x6, 6x1, 3x2, 2x3. * The pieces will be uploaded in `cubeFaceOrder` * * If `string[]` or `TexImageSource[]` and target is `gl.TEXTURE_CUBE_MAP` then it must have 6 entries, one for each face of a cube map. * * If `string[]` or `TexImageSource[]` and target is `gl.TEXTURE_2D_ARRAY` then each entry is a slice of the a 2d array texture * and will be scaled to the specified width and height OR to the size of the first image that loads. * * If `TexImageSource` then it wil be used immediately to create the contents of the texture. Examples `HTMLImageElement`, * `HTMLCanvasElement`, `HTMLVideoElement`. * * If `number[]` or `ArrayBufferView` it's assumed to be data for a texture. If `width` or `height` is * not specified it is guessed as follows. First the number of elements is computed by `src.length / numComponents` * where `numComponents` is derived from `format`. If `target` is `gl.TEXTURE_CUBE_MAP` then `numElements` is divided * by 6. Then * * * If neither `width` nor `height` are specified and `sqrt(numElements)` is an integer then width and height * are set to `sqrt(numElements)`. Otherwise `width = numElements` and `height = 1`. * * * If only one of `width` or `height` is specified then the other equals `numElements / specifiedDimension`. * * If `number[]` will be converted to `type`. * * If `src` is a function it will be called with a `WebGLRenderingContext` and these options. * Whatever it returns is subject to these rules. So it can return a string url, an `HTMLElement` * an array etc... * * If `src` is undefined then an empty texture will be created of size `width` by `height`. * * @property {string} [crossOrigin] What to set the crossOrigin property of images when they are downloaded. * default: undefined. Also see {@link module:twgl.setDefaults}. * * @memberOf module:twgl */ // NOTE: While querying GL is considered slow it's not remotely as slow // as uploading a texture. On top of that you're unlikely to call this in // a perf critical loop. Even if upload a texture every frame that's unlikely // to be more than 1 or 2 textures a frame. In other words, the benefits of // making the API easy to use outweigh any supposed perf benefits // // Also note I get that having one global of these is bad practice. // As long as it's used correctly it means no garbage which probably // doesn't matter when dealing with textures but old habits die hard. var lastPackState = {}; /** * Saves any packing state that will be set based on the options. * @param {module:twgl.TextureOptions} options A TextureOptions object with whatever parameters you want set. * @param {WebGLRenderingContext} gl the WebGLRenderingContext * @private */ function savePackState(gl, options) { if (options.colorspaceConversion !== undefined) { lastPackState.colorspaceConversion = gl.getParameter(UNPACK_COLORSPACE_CONVERSION_WEBGL); gl.pixelStorei(UNPACK_COLORSPACE_CONVERSION_WEBGL, options.colorspaceConversion); } if (options.premultiplyAlpha !== undefined) { lastPackState.premultiplyAlpha = gl.getParameter(UNPACK_PREMULTIPLY_ALPHA_WEBGL); gl.pixelStorei(UNPACK_PREMULTIPLY_ALPHA_WEBGL, options.premultiplyAlpha); } if (options.flipY !== undefined) { lastPackState.flipY = gl.getParameter(UNPACK_FLIP_Y_WEBGL); gl.pixelStorei(UNPACK_FLIP_Y_WEBGL, options.flipY); } } /** * Restores any packing state that was set based on the options. * @param {module:twgl.TextureOptions} options A TextureOptions object with whatever parameters you want set. * @param {WebGLRenderingContext} gl the WebGLRenderingContext * @private */ function restorePackState(gl, options) { if (options.colorspaceConversion !== undefined) { gl.pixelStorei(UNPACK_COLORSPACE_CONVERSION_WEBGL, lastPackState.colorspaceConversion); } if (options.premultiplyAlpha !== undefined) { gl.pixelStorei(UNPACK_PREMULTIPLY_ALPHA_WEBGL, lastPackState.premultiplyAlpha); } if (options.flipY !== undefined) { gl.pixelStorei(UNPACK_FLIP_Y_WEBGL, lastPackState.flipY); } } /** * Saves state related to data size * @param {WebGLRenderingContext} gl the WebGLRenderingContext * @private */ function saveSkipState(gl) { lastPackState.unpackAlignment = gl.getParameter(UNPACK_ALIGNMENT); if (utils.isWebGL2(gl)) { lastPackState.unpackRowLength = gl.getParameter(UNPACK_ROW_LENGTH); lastPackState.unpackImageHeight = gl.getParameter(UNPACK_IMAGE_HEIGHT); lastPackState.unpackSkipPixels = gl.getParameter(UNPACK_SKIP_PIXELS); lastPackState.unpackSkipRows = gl.getParameter(UNPACK_SKIP_ROWS); lastPackState.unpackSkipImages = gl.getParameter(UNPACK_SKIP_IMAGES); } } /** * Restores state related to data size * @param {WebGLRenderingContext} gl the WebGLRenderingContext * @private */ function restoreSkipState(gl) { gl.pixelStorei(UNPACK_ALIGNMENT, lastPackState.unpackAlignment); if (utils.isWebGL2(gl)) { gl.pixelStorei(UNPACK_ROW_LENGTH, lastPackState.unpackRowLength); gl.pixelStorei(UNPACK_IMAGE_HEIGHT, lastPackState.unpackImageHeight); gl.pixelStorei(UNPACK_SKIP_PIXELS, lastPackState.unpackSkipPixels); gl.pixelStorei(UNPACK_SKIP_ROWS, lastPackState.unpackSkipRows); gl.pixelStorei(UNPACK_SKIP_IMAGES, lastPackState.unpackSkipImages); } } /** * Sets the parameters of a texture or sampler * @param {WebGLRenderingContext} gl the WebGLRenderingContext * @param {number|WebGLSampler} target texture target or sampler * @param {function()} parameteriFn texParameteri or samplerParameteri fn * @param {WebGLTexture} tex the WebGLTexture to set parameters for * @param {module:twgl.TextureOptions} options A TextureOptions object with whatever parameters you want set. * This is often the same options you passed in when you created the texture. * @private */ function setTextureSamplerParameters(gl, target, parameteriFn, options) { if (options.minMag) { parameteriFn.call(gl, target, TEXTURE_MIN_FILTER, options.minMag); parameteriFn.call(gl, target, TEXTURE_MAG_FILTER, options.minMag); } if (options.min) { parameteriFn.call(gl, target, TEXTURE_MIN_FILTER, options.min); } if (options.mag) { parameteriFn.call(gl, target, TEXTURE_MAG_FILTER, options.mag); } if (options.wrap) { parameteriFn.call(gl, target, TEXTURE_WRAP_S, options.wrap); parameteriFn.call(gl, target, TEXTURE_WRAP_T, options.wrap); if (target === TEXTURE_3D || helper.isSampler(gl, target)) { parameteriFn.call(gl, target, TEXTURE_WRAP_R, options.wrap); } } if (options.wrapR) { parameteriFn.call(gl, target, TEXTURE_WRAP_R, options.wrapR); } if (options.wrapS) { parameteriFn.call(gl, target, TEXTURE_WRAP_S, options.wrapS); } if (options.wrapT) { parameteriFn.call(gl, target, TEXTURE_WRAP_T, options.wrapT); } if (options.minLod) { parameteriFn.call(gl, target, TEXTURE_MIN_LOD, options.minLod); } if (options.maxLod) { parameteriFn.call(gl, target, TEXTURE_MAX_LOD, options.maxLod); } if (options.baseLevel) { parameteriFn.call(gl, target, TEXTURE_BASE_LEVEL, options.baseLevel); } if (options.maxLevel) { parameteriFn.call(gl, target, TEXTURE_MAX_LEVEL, options.maxLevel); } } /** * Sets the texture parameters of a texture. * @param {WebGLRenderingContext} gl the WebGLRenderingContext * @param {WebGLTexture} tex the WebGLTexture to set parameters for * @param {module:twgl.TextureOptions} options A TextureOptions object with whatever parameters you want set. * This is often the same options you passed in when you created the texture. * @memberOf module:twgl/textures */ function setTextureParameters(gl, tex, options) { var target = options.target || TEXTURE_2D; gl.bindTexture(target, tex); setTextureSamplerParameters(gl, target, gl.texParameteri, options); } /** * Sets the sampler parameters of a sampler. * @param {WebGLRenderingContext} gl the WebGLRenderingContext * @param {WebGLSampler} sampler the WebGLSampler to set parameters for * @param {module:twgl.TextureOptions} options A TextureOptions object with whatever parameters you want set. * @memberOf module:twgl/textures */ function setSamplerParameters(gl, sampler, options) { setTextureSamplerParameters(gl, sampler, gl.samplerParameteri, options); } /** * Creates a new sampler object and sets parameters. * * Example: * * const sampler = twgl.createSampler(gl, { * minMag: gl.NEAREST, // sets both TEXTURE_MIN_FILTER and TEXTURE_MAG_FILTER * wrap: gl.CLAMP_TO_NEAREST, // sets both TEXTURE_WRAP_S and TEXTURE_WRAP_T and TEXTURE_WRAP_R * }); * * @param {WebGLRenderingContext} gl the WebGLRenderingContext * @param {Object.<string,module:twgl.TextureOptions>} options A object of TextureOptions one per sampler. * @return {Object.<string,WebGLSampler>} the created samplers by name * @private */ function createSampler(gl, options) { var sampler = gl.createSampler(); setSamplerParameters(gl, sampler, options); return sampler; } /** * Creates a multiple sampler objects and sets parameters on each. * * Example: * * const samplers = twgl.createSamplers(gl, { * nearest: { * minMag: gl.NEAREST, * }, * nearestClampS: { * minMag: gl.NEAREST, * wrapS: gl.CLAMP_TO_NEAREST, * }, * linear: { * minMag: gl.LINEAR, * }, * nearestClamp: { * minMag: gl.NEAREST, * wrap: gl.CLAMP_TO_EDGE, * }, * linearClamp: { * minMag: gl.LINEAR, * wrap: gl.CLAMP_TO_EDGE, * }, * linearClampT: { * minMag: gl.LINEAR, * wrapT: gl.CLAMP_TO_EDGE, * }, * }); * * @param {WebGLRenderingContext} gl the WebGLRenderingContext * @param {module:twgl.TextureOptions} [options] A TextureOptions object with whatever parameters you want set on the sampler * @private */ function createSamplers(gl, samplerOptions) { var samplers = {}; Object.keys(samplerOptions).forEach(function (name) { samplers[name] = createSampler(gl, samplerOptions[name]); }); return samplers; } /** * Makes a 1x1 pixel * If no color is passed in uses the default color which can be set by calling `setDefaultTextureColor`. * @param {(number[]|ArrayBufferView)} [color] The color using 0-1 values * @return {Uint8Array} Unit8Array with color. * @private */ function make1Pixel(color) { color = color || defaults.textureColor; if (isArrayBuffer(color)) { return color; } return new Uint8Array([color[0] * 255, color[1] * 255, color[2] * 255, color[3] * 255]); } /** * Sets filtering or generates mips for texture based on width or height * If width or height is not passed in uses `options.width` and//or `options.height` * * @param {WebGLRenderingContext} gl the WebGLRenderingContext * @param {WebGLTexture} tex the WebGLTexture to set parameters for * @param {module:twgl.TextureOptions} [options] A TextureOptions object with whatever parameters you want set. * This is often the same options you passed in when you created the texture. * @param {number} [width] width of texture * @param {number} [height] height of texture * @param {number} [internalFormat] The internalFormat parameter from texImage2D etc.. * @memberOf module:twgl/textures */ function setTextureFilteringForSize(gl, tex, options, width, height, internalFormat) { options = options || defaults.textureOptions; internalFormat = internalFormat || RGBA; var target = options.target || TEXTURE_2D; width = width || options.width; height = height || options.height; gl.bindTexture(target, tex); if (canGenerateMipmap(gl, width, height, internalFormat)) { gl.generateMipmap(target); } else { var filtering = canFilter(internalFormat) ? LINEAR : NEAREST; gl.texParameteri(target, TEXTURE_MIN_FILTER, filtering); gl.texParameteri(target, TEXTURE_MAG_FILTER, filtering); gl.texParameteri(target, TEXTURE_WRAP_S, CLAMP_TO_EDGE); gl.texParameteri(target, TEXTURE_WRAP_T, CLAMP_TO_EDGE); } } function shouldAutomaticallySetTextureFilteringForSize(options) { return options.auto === true || options.auto === undefined && options.level === undefined; } /** * Gets an array of cubemap face enums * @param {WebGLRenderingContext} gl the WebGLRenderingContext * @param {module:twgl.TextureOptions} options A TextureOptions object with whatever parameters you want set. * This is often the same options you passed in when you created the texture. * @return {number[]} cubemap face enums * @private */ function getCubeFaceOrder(gl, options) { options = options || {}; return options.cubeFaceOrder || [TEXTURE_CUBE_MAP_POSITIVE_X, TEXTURE_CUBE_MAP_NEGATIVE_X, TEXTURE_CUBE_MAP_POSITIVE_Y, TEXTURE_CUBE_MAP_NEGATIVE_Y, TEXTURE_CUBE_MAP_POSITIVE_Z, TEXTURE_CUBE_MAP_NEGATIVE_Z]; } /** * @typedef {Object} FaceInfo * @property {number} face gl enum for texImage2D * @property {number} ndx face index (0 - 5) into source data * @ignore */ /** * Gets an array of FaceInfos * There's a bug in some NVidia drivers that will crash the driver if * `gl.TEXTURE_CUBE_MAP_POSITIVE_X` is not uploaded first. So, we take * the user's desired order from his faces to WebGL and make sure we * do the faces in WebGL order * * @param {WebGLRenderingContext} gl the WebGLRenderingContext * @param {module:twgl.TextureOptions} options A TextureOptions object with whatever parameters you want set. * @return {FaceInfo[]} cubemap face infos. Arguably the `face` property of each element is redundant but * it's needed internally to sort the array of `ndx` properties by `face`. * @private */ function getCubeFacesWithNdx(gl, options) { var faces = getCubeFaceOrder(gl, options); // work around bug in NVidia drivers. We have to upload the first face first else the driver crashes :( var facesWithNdx = faces.map(function (face, ndx) { return { face: face, ndx: ndx }; }); facesWithNdx.sort(function (a, b) { return a.face - b.face; }); return facesWithNdx; } /** * Set a texture from the contents of an element. Will also set * texture filtering or generate mips based on the dimensions of the element * unless `options.auto === false`. If `target === gl.TEXTURE_CUBE_MAP` will * attempt to slice image into 1x6, 2x3, 3x2, or 6x1 images, one for each face. * @param {WebGLRenderingContext} gl the WebGLRenderingContext * @param {WebGLTexture} tex the WebGLTexture to set parameters for * @param {HTMLElement} element a canvas, img, or video element. * @param {module:twgl.TextureOptions} [options] A TextureOptions object with whatever parameters you want set. * This is often the same options you passed in when you created the texture. * @memberOf module:twgl/textures * @kind function */ function setTextureFromElement(gl, tex, element, options) { options = options || defaults.textureOptions; var target = options.target || TEXTURE_2D; var level = options.level || 0; var width = element.width; var height = element.height; var internalFormat = options.internalFormat || options.format || RGBA; var formatType = getFormatAndTypeForInternalFormat(internalFormat); var format = options.format || formatType.format; var type = options.type || formatType.type; savePackState(gl, options); gl.bindTexture(target, tex); if (target === TEXTURE_CUBE_MAP) { // guess the parts var imgWidth = element.width; var imgHeight = element.height; var size; var slices; if (imgWidth / 6 === imgHeight) { // It's 6x1 size = imgHeight; slices = [0, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0]; } else if (imgHeight / 6 === imgWidth) { // It's 1x6 size = imgWidth; slices = [0, 0, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5]; } else if (imgWidth / 3 === imgHeight / 2) { // It's 3x2 size = imgWidth / 3; slices = [0, 0, 1, 0, 2, 0, 0, 1, 1, 1, 2, 1]; } else if (imgWidth / 2 === imgHeight / 3) { // It's 2x3 size = imgWidth / 2; slices = [0, 0, 1, 0, 0, 1, 1, 1, 0, 2, 1, 2]; } else { throw "can't figure out cube map from element: " + (element.src ? element.src : element.nodeName); } var ctx = getShared2DContext(); if (ctx) { ctx.canvas.width = size; ctx.canvas.height = size; width = size; height = size; getCubeFacesWithNdx(gl, options).forEach(function (f) { var xOffset = slices[f.ndx * 2 + 0] * size; var yOffset = slices[f.ndx * 2 + 1] * size; ctx.drawImage(element, xOffset, yOffset, size, size, 0, 0, size, size); gl.texImage2D(f.face, level, internalFormat, format, type, ctx.canvas); }); // Free up the canvas memory ctx.canvas.width = 1; ctx.canvas.height = 1; } else if (typeof createImageBitmap !== 'undefined') { // NOTE: It seems like we should prefer ImageBitmap because unlike canvas it's // note lossy? (alpha is not premultiplied? although I'm not sure what width = size; height = size; getCubeFacesWithNdx(gl, options).forEach(function (f) { var xOffset = slices[f.ndx * 2 + 0] * size; var yOffset = slices[f.ndx * 2 + 1] * size; // We can't easily use a default texture color here as it would have to match // the type across all faces where as with a 2D one there's only one face // so we're replacing everything all at once. It also has to be the correct size. // On the other hand we need all faces to be the same size so as one face loads // the rest match else the texture will be un-renderable. gl.texImage2D(f.face, level, internalFormat, size, size, 0, format, type, null); createImageBitmap(element, xOffset, yOffset, size, size, { premultiplyAlpha: 'none', colorSpaceConversion: 'none' }).then(function (imageBitmap) { savePackState(gl, options); gl.bindTexture(target, tex); gl.texImage2D(f.face, level, internalFormat, format, type, imageBitmap); restorePackState(gl, options); if (shouldAutomaticallySetTextureFilteringForSize(options)) { setTextureFilteringForSize(gl, tex, options, width, height, internalFormat); } }); }); } } else if (target === TEXTURE_3D || target === TEXTURE_2D_ARRAY) { var smallest = Math.min(element.width, element.height); var largest = Math.max(element.width, element.height); var depth = largest / smallest; if (depth % 1 !== 0) { throw "can not compute 3D dimensions of element"; } var xMult = element.width === largest ? 1 : 0; var yMult = element.height === largest ? 1 : 0; saveSkipState(gl); gl.pixelStorei(UNPACK_ALIGNMENT, 1); gl.pixelStorei(UNPACK_ROW_LENGTH, element.width); gl.pixelStorei(UNPACK_IMAGE_HEIGHT, 0); gl.pixelStorei(UNPACK_SKIP_IMAGES, 0); gl.texImage3D(target, level, internalFormat, smallest, smallest, smallest, 0, format, type, null); for (var d = 0; d < depth; ++d) { var srcX = d * smallest * xMult; var srcY = d * smallest * yMult; gl.pixelStorei(UNPACK_SKIP_PIXELS, srcX); gl.pixelStorei(UNPACK_SKIP_ROWS, srcY); gl.texSubImage3D(target, level, 0, 0, d, smallest, smallest, 1, format, type, element); } restoreSkipState(gl); } else { gl.texImage2D(target, level, internalFormat, format, type, element); } restorePackState(gl, options); if (shouldAutomaticallySetTextureFilteringForSize(options)) { setTextureFilteringForSize(gl, tex, options, width, height, internalFormat); } setTextureParameters(gl, tex, options); } function noop() {} /** * Checks whether the url's origin is the same so that we can set the `crossOrigin` * @param {string} url url to image * @returns {boolean} true if the window's origin is the same as image's url * @private */ function urlIsSameOrigin(url) { if (typeof document !== 'undefined') { // for IE really var a = document.createElement('a'); a.href = url; return a.hostname === location.hostname && a.port === location.port && a.protocol === location.protocol; } else { var localOrigin = new URL(location.href).origin; var urlOrigin = new URL(url, location.href).origin; return urlOrigin === localOrigin; } } function setToAnonymousIfUndefinedAndURLIsNotSameOrigin(url, crossOrigin) { return crossOrigin === undefined && !urlIsSameOrigin(url) ? 'anonymous' : crossOrigin; } /** * Loads an image * @param {string} url url to image * @param {string} crossOrigin * @param {function(err, img)} [callback] a callback that's passed an error and the image. The error will be non-null * if there was an error * @return {HTMLImageElement} the image being loaded. * @private */ function loadImage(url, crossOrigin, callback) { callback = callback || noop; var img; crossOrigin = crossOrigin !== undefined ? crossOrigin : defaults.crossOrigin; crossOrigin = setToAnonymousIfUndefinedAndURLIsNotSameOrigin(url, crossOrigin); if (typeof Image !== 'undefined') { img = new Image(); if (crossOrigin !== undefined) { img.crossOrigin = crossOrigin; } var clearEventHandlers = function clearEventHandlers() { img.removeEventListener('error', onError); // eslint-disable-line img.removeEventListener('load', onLoad); // eslint-disable-line img = null; }; var onError = function onError() { var msg = "couldn't load image: " + url; helper.error(msg); callback(msg, img); clearEventHandlers(); }; var onLoad = function onLoad() { callback(null, img); clearEventHandlers(); }; img.addEventListener('error', onError); img.addEventListener('load', onLoad); img.src = url; return img; } else if (typeof ImageBitmap !== 'undefined') { var err; var bm; var cb = function cb() { callback(err, bm); }; var options = {}; if (crossOrigin) { options.mode = 'cors'; // TODO: not sure how to translate image.crossOrigin } fetch(url, options).then(function (response) { if (!response.ok) { throw response; } return response.blob(); }).then(function (blob) { return createImageBitmap(blob, { premultiplyAlpha: 'none', colorSpaceConversion: 'none' }); }).then(function (bitmap) { // not sure if this works. We don't want // to catch the user's error. So, call // the callback in a timeout so we're // not in this scope inside the promise. bm = bitmap; setTimeout(cb); })["catch"](function (e) { err = e; setTimeout(cb); }); img = null; } return img; } /** * check if object is a TexImageSource * * @param {Object} obj Object to test * @return {boolean} true if object is a TexImageSource * @private */ function isTexImageSource(obj) { return typeof ImageBitmap !== 'undefined' && obj instanceof ImageBitmap || typeof ImageData !== 'undefined' && obj instanceof ImageData || typeof HTMLElement !== 'undefined' && obj instanceof HTMLElement; } /** * if obj is an TexImageSource then just * uses it otherwise if obj is a string * then load it first. * * @param {string|TexImageSource} obj * @param {string} crossOrigin * @param {function(err, img)} [callback] a callback that's passed an error and the image. The error will be non-null * if there was an error * @private */ function loadAndUseImage(obj, crossOrigin, callback) { if (isTexImageSource(obj)) { setTimeout(function () { callback(null, obj); }); return obj; } return loadImage(obj, crossOrigin, callback); } /** * Sets a texture to a 1x1 pixel color. If `options.color === false` is nothing happens. If it's not set * the default texture color is used which can be set by calling `setDefaultTextureColor`. * @param {WebGLRenderingContext} gl the WebGLRenderingContext * @param {WebGLTexture} tex the WebGLTexture to set parameters for * @param {module:twgl.TextureOptions} [options] A TextureOptions object with whatever parameters you want set. * This is often the same options you passed in when you created the texture. * @memberOf module:twgl/textures */ function setTextureTo1PixelColor(gl, tex, options) { options = options || defaults.textureOptions; var target = options.target || TEXTURE_2D; gl.bindTexture(target, tex); if (options.color === false) { return; } // Assume it's a URL // Put 1x1 pixels in texture. That makes it renderable immediately regardless of filtering. var color = make1Pixel(options.color); if (target === TEXTURE_CUBE_MAP) { for (var ii = 0; ii < 6; ++ii) { gl.texImage2D(TEXTURE_CUBE_MAP_POSITIVE_X + ii, 0, RGBA, 1, 1, 0, RGBA, UNSIGNED_BYTE, color); } } else if (target === TEXTURE_3D || target === TEXTURE_2D_ARRAY) { gl.texImage3D(target, 0, RGBA, 1, 1, 1, 0, RGBA, UNSIGNED_BYTE, color); } else { gl.texImage2D(target, 0, RGBA, 1, 1, 0, RGBA, UNSIGNED_BYTE, color); } } /** * The src image(s) used to create a texture. * * When you call {@link module:twgl.createTexture} or {@link module:twgl.createTextures} * you can pass in urls for images to load into the textures. If it's a single url * then this will be a single HTMLImageElement. If it's an array of urls used for a cubemap * this will be a corresponding array of images for the cubemap. * * @typedef {HTMLImageElement|HTMLImageElement[]} TextureSrc * @memberOf module:twgl */ /** * A callback for when an image finished downloading and been uploaded into a texture * @callback TextureReadyCallback * @param {*} err If truthy there was an error. * @param {WebGLTexture} texture the texture. * @param {module:twgl.TextureSrc} source image(s) used to as the src for the texture * @memberOf module:twgl */ /** * A callback for when all images have finished downloading and been uploaded into their respective textures * @callback TexturesReadyCallback * @param {*} err If truthy there was an error. * @param {Object.<string, WebGLTexture>} textures the created textures by name. Same as returned by {@link module:twgl.createTextures}. * @param {Object.<string, module:twgl.TextureSrc>} sources the image(s) used for the texture by name. * @memberOf module:twgl */ /** * A callback for when an image finished downloading and been uploaded into a texture * @callback CubemapReadyCallback * @param {*} err If truthy there was an error. * @param {WebGLTexture} tex the texture. * @param {HTMLImageElement[]} imgs the images for each face. * @memberOf module:twgl */ /** * A callback for when an image finished downloading and been uploaded into a texture * @callback ThreeDReadyCallback * @param {*} err If truthy there was an error. * @param {WebGLTexture} tex the texture. * @param {HTMLImageElement[]} imgs the images for each slice. * @memberOf module:twgl */ /** * Loads a texture from an image from a Url as specified in `options.src` * If `options.color !== false` will set the texture to a 1x1 pixel color so that the texture is * immediately useable. It will be updated with the contents of the image once the image has finished * downloading. Filtering options will be set as appropriate for image unless `options.auto === false`. * @param {WebGLRenderingContext} gl the WebGLRenderingContext * @param {WebGLTexture} tex the WebGLTexture to set parameters for * @param {module:twgl.TextureOptions} [options] A TextureOptions object with whatever parameters you want set. * @param {module:twgl.TextureReadyCallback} [callback] A function to be called when the image has finished loading. err will * be non null if there was an error. * @return {HTMLImageElement} the image being downloaded. * @memberOf module:twgl/textures */ function loadTextureFromUrl(gl, tex, options, callback) { callback = callback || noop; options = options || defaults.textureOptions; setTextureTo1PixelColor(gl, tex, options); // Because it's async we need to copy the options. options = Object.assign({}, options); var img = loadAndUseImage(options.src, options.crossOrigin, function (err, img) { if (err) { callback(err, tex, img); } else { setTextureFromElement(gl, tex, img, options); callback(null, tex, img); } }); return img; } /** * Loads a cubemap from 6 urls or TexImageSources as specified in `options.src`. Will set the cubemap to a 1x1 pixel color * so that it is usable immediately unless `option.color === false`. * @param {WebGLRenderingContext} gl the WebGLRenderingContext * @param {WebGLTexture} tex the WebGLTexture to set parameters for * @param {module:twgl.TextureOptions} options A TextureOptions object with whatever parameters you want set. * @param {module:twgl.CubemapReadyCallback} [callback] A function to be called when all the images have finished loading. err will * be non null if there was an error. * @memberOf module:twgl/textures */ function loadCubemapFromUrls(gl, tex, options, callback) { callback = callback || noop; var urls = options.src; if (urls.length !== 6) { throw "there must be 6 urls for a cubemap"; } var level = options.level || 0; var internalFormat = options.internalFormat || options.format || RGBA; var formatType = getFormatAndTypeForInternalFormat(internalFormat); var format = options.format || formatType.format; var type = options.type || UNSIGNED_BYTE; var target = options.target || TEXTURE_2D; if (target !== TEXTURE_CUBE_MAP) { throw "target must be TEXTURE_CUBE_MAP"; } setTextureTo1PixelColor(gl, tex, options); // Because it's async we need to copy the options. options = Object.assign({}, options); var numToLoad = 6; var errors = []; var faces = getCubeFaceOrder(gl, options); var imgs; // eslint-disable-line function uploadImg(faceTarget) { return function (err, img) { --numToLoad; if (err) { errors.push(err); } else { if (img.width !== img.height) { errors.push("cubemap face img is not a square: " + img.src); } else { savePackState(gl, options); gl.bindTexture(target, tex); // So assuming this is the first image we now have one face that's img sized // and 5 faces that are 1x1 pixel so size the other faces if (numToLoad === 5) { // use the default order getCubeFaceOrder(gl).forEach(function (otherTarget) { // Should we re-use the same face or a color? gl.texImage2D(otherTarget, level, internalFormat, format, type, img); }); } else { gl.texImage2D(faceTarget, level, internalFormat, format, type, img); } restorePackState(gl, options); if (shouldAutomaticallySetTextureFilteringForSize(options)) { gl.generateMipmap(target); } } } if (numToLoad === 0) { callback(errors.length ? errors : undefined, tex, imgs); } }; } imgs = urls.map(function (url, ndx) { return loadAndUseImage(url, options.crossOrigin, uploadImg(faces[ndx])); }); } /** * Loads a 2d array or 3d texture from urls OR TexImageSources as specified in `options.src`. * Will set the texture to a 1x1 pixel color * so that it is usable immediately unless `option.color === false`. * * If the width and height is not specified the width and height of the first * image loaded will be used. Note that since images are loaded async * which image downloads first is unknown. * * If an image is not the same size as the width and height it will be scaled * to that width and height. * * @param {WebGLRenderingContext} gl the WebGLRenderingContext * @param {WebGLTexture} tex the WebGLTexture to set parameters for * @param {module:twgl.TextureOptions} options A TextureOptions object with whatever parameters you want set. * @param {module:twgl.ThreeDReadyCallback} [callback] A function to be called when all the images have finished loading. err will * be non null if there was an error. * @memberOf module:twgl/textures */ function loadSlicesFromUrls(gl, tex, options, callback) { callback = callback || noop; var urls = options.src; var internalFormat = options.internalFormat || options.format || RGBA; var formatType = getFormatAndTypeForInternalFormat(internalFormat); var format = options.format || formatType.format; var type = options.type || UNSIGNED_BYTE; var target = options.target || TEXTURE_2D_ARRAY; if (target !== TEXTURE_3D && target !== TEXTURE_2D_ARRAY) { throw "target must be TEXTURE_3D or TEXTURE_2D_ARRAY"; } setTextureTo1PixelColor(gl, tex, options); // Because it's async we need to copy the options. options = Object.assign({}, options); var numToLoad = urls.length; var errors = []; var imgs; // eslint-disable-line var level = options.level || 0; var width = options.width; var height = options.height; var depth = urls.length; var firstImage = true; function uploadImg(slice) { return function (err, img) { --numToLoad; if (err) { errors.push(err); } else { savePackState(gl, options); gl.bindTexture(target, tex); if (firstImage) { firstImage = false; width = options.width || img.width; height = options.height || img.height; gl.texImage3D(target, level, internalFormat, width, height, depth, 0, format, type, null); // put it in every slice otherwise some slices will be 0,0,0,0 for (var s = 0; s < depth; ++s) { gl.texSubImage3D(target, level, 0, 0, s, width, height, 1, format, type, img); } } else { var src = img; var ctx; if (img.width !== width || img.height !== height) { // Size the image to fix ctx = getShared2DContext(); src = ctx.canvas; ctx.canvas.width = width; ctx.canvas.height = height; ctx.drawImage(img, 0, 0, width, height); } gl.texSubImage3D(target, level, 0, 0, slice, width, height, 1, format, type, src); // free the canvas memory if (ctx && src === ctx.canvas) { ctx.canvas.width = 0; ctx.canvas.height = 0; } } restorePackState(gl, options); if (shouldAutomaticallySetTextureFilteringForSize(options)) { gl.generateMipmap(target); } } if (numToLoad === 0) { callback(errors.length ? errors : undefined, tex, imgs); } }; } imgs = urls.map(function (url, ndx) { return loadAndUseImage(url, options.crossOrigin, uploadImg(ndx)); }); } /** * Sets a texture from an array or typed array. If the width or height is not provided will attempt to * guess the size. See {@link module:twgl.TextureOptions}. * @param {WebGLRenderingContext} gl the WebGLRenderingContext * @param {WebGLTexture} tex the WebGLTexture to set parameters for * @param {(number[]|ArrayBufferView)} src An array or typed arry with texture data. * @param {module:twgl.TextureOptions} [options] A TextureOptions object with whatever parameters you want set. * This is often the same options you passed in when you created the texture. * @memberOf module:twgl/textures */ function setTextureFromArray(gl, tex, src, options) { options = options || defaults.textureOptions; var target = options.target || TEXTURE_2D; gl.bindTexture(target, tex); var width = options.width; var height = options.height; var depth = options.depth; var level = options.level || 0; var internalFormat = options.internalFormat || options.format || RGBA; var formatType = getFormatAndTypeForInternalFormat(internalFormat); var format = options.format || formatType.format; var type = options.type || getTextureTypeForArrayType(gl, src, formatType.type); if (!isArrayBuffer(src)) { var Type = typedArrays.getTypedArrayTypeForGLType(type); src = new Type(src); } else if (src instanceof Uint8ClampedArray) { src = new Uint8Array(src.buffer); } var bytesPerElement = getBytesPerElementForInternalFormat(internalFormat, type); var numElements = src.byteLength / bytesPerElement; // TODO: check UNPACK_ALIGNMENT? if (numElements % 1) { throw "length wrong size for format: " + utils.glEnumToString(gl, format); } var dimensions; if (target === TEXTURE_3D || target === TEXTURE_2D_ARRAY) { if (!width && !height && !depth) { var size = Math.cbrt(numElements); if (size % 1 !== 0) { throw "can't guess cube size of array of numElements: " + numElements; } width = size; height = size; depth = size; } else if (width && (!height || !depth)) { dimensions = guessDimensions(gl, target, height, depth, numElements / width); height = dimensions.width; depth = dimensions.height; } else if (height && (!width || !depth)) { dimensions = guessDimensions(gl, target, width, depth, numElements / height); width = dimensions.width; depth = dimensions.height; } else { dimensions = guessDimensions(gl, target, width, height, numElements / depth); width = dimensions.width; height = dimensions.height; } } else { dimensions = guessDimensions(gl, target, width, height, numElements); width = dimensions.width; height = dimensions.height; } saveSkipState(gl); gl.pixelStorei(UNPACK_ALIGNMENT, options.unpackAlignment || 1); savePackState(gl, options); if (target === TEXTURE_CUBE_MAP) { var elementsPerElement = bytesPerElement / src.BYTES_PER_ELEMENT; var faceSize = numElements / 6 * elementsPerElement; getCubeFacesWithNdx(gl, options).forEach(function (f) { var offset = faceSize * f.ndx; var data = src.subarray(offset, offset + faceSize); gl.texImage2D(f.face, level, internalFormat, width, height, 0, format, type, data); }); } else if (target === TEXTURE_3D || target === TEXTURE_2D_ARRAY) { gl.texImage3D(target, level, internalFormat, width, height, depth, 0, format, type, src); } else { gl.texImage2D(target, level, internalFormat, width, height, 0, format, type, src); } restorePackState(gl, options); restoreSkipState(gl); return { width: width, height: height, depth: depth, type: type }; } /** * Sets a texture with no contents of a certain size. In other words calls `gl.texImage2D` with `null`. * You must set `options.width` and `options.height`. * @param {WebGLRenderingContext} gl the WebGLRenderingContext * @param {WebGLTexture} tex the WebGLTexture to set parameters for * @param {module:twgl.TextureOptions} options A TextureOptions object with whatever parameters you want set. * @memberOf module:twgl/textures */ function setEmptyTexture(gl, tex, options) { var target = options.target || TEXTURE_2D; gl.bindTexture(target, tex); var level = options.level || 0; var internalFormat = options.internalFormat || options.format || RGBA; var formatType = getFormatAndTypeForInternalFormat(internalFormat); var format = options.format || formatType.format; var type = options.type || formatType.type; savePackState(gl, options); if (target === TEXTURE_CUBE_MAP) { for (var ii = 0; ii < 6; ++ii) { gl.texImage2D(TEXTURE_CUBE_MAP_POSITIVE_X + ii, level, internalFormat, options.width, options.height, 0, format, type, null); } } else if (target === TEXTURE_3D || target === TEXTURE_2D_ARRAY) { gl.texImage3D(target, level, internalFormat, options.width, options.height, options.depth, 0, format, type, null); } else { gl.texImage2D(target, level, internalFormat, options.width, options.height, 0, format, type, null); } restorePackState(gl, options); } /** * Creates a texture based on the options passed in. * @param {WebGLRenderingContext} gl the WebGLRenderingContext * @param {module:twgl.TextureOptions} [options] A TextureOptions object with whatever parameters you want set. * @param {module:twgl.TextureReadyCallback} [callback] A callback called when an image has been downloaded and uploaded to the texture. * @return {WebGLTexture} the created texture. * @memberOf module:twgl/textures */ function createTexture(gl, options, callback) { callback = callback || noop; options = options || defaults.textureOptions; var tex = gl.createTexture(); var target = options.target || TEXTURE_2D; var width = options.width || 1; var height = options.height || 1; var internalFormat = options.internalFormat || RGBA; gl.bindTexture(target, tex); if (target === TEXTURE_CUBE_MAP) { // this should have been the default for cubemaps :( gl.texParameteri(target, TEXTURE_WRAP_S, CLAMP_TO_EDGE); gl.texParameteri(target, TEXTURE_WRAP_T, CLAMP_TO_EDGE); } var src = options.src; if (src) { if (typeof src === "function") { src = src(gl, options); } if (typeof src === "string") { loadTextureFromUrl(gl, tex, options, callback); } else if (isArrayBuffer(src) || Array.isArray(src) && (typeof src[0] === 'number' || Array.isArray(src[0]) || isArrayBuffer(src[0]))) { var dimensions = setTextureFromArray(gl, tex, src, options); width = dimensions.width; height = dimensions.height; } else if (Array.isArray(src) && (typeof src[0] === 'string' || isTexImageSource(src[0]))) { if (target === TEXTURE_CUBE_MAP) { loadCubemapFromUrls(gl, tex, options, callback); } else { loadSlicesFromUrls(gl, tex, options, callback); } } else if (isTexImageSource(src)) { setTextureFromElement(gl, tex, src, options); width = src.width; height = src.height; } else { throw "unsupported src type"; } } else { setEmptyTexture(gl, tex, options); } if (shouldAutomaticallySetTextureFilteringForSize(options)) { setTextureFilteringForSize(gl, tex, options, width, height, internalFormat); } setTextureParameters(gl, tex, options); return tex; } /** * Resizes a texture based on the options passed in. * * Note: This is not a generic resize anything function. * It's mostly used by {@link module:twgl.resizeFramebufferInfo} * It will use `options.src` if it exists to try to determine a `type` * otherwise it will assume `gl.UNSIGNED_BYTE`. No data is provided * for the texture. Texture parameters will be set accordingly * * @param {WebGLRenderingContext} gl the WebGLRenderingContext * @param {WebGLTexture} tex the texture to resize * @param {module:twgl.TextureOptions} options A TextureOptions object with whatever parameters you want set. * @param {number} [width] the new width. If not passed in will use `options.width` * @param {number} [height] the new height. If not passed in will use `options.height` * @param {number} [depth] the new depth. If not passed in will use `options.depth` * @memberOf module:twgl/textures */ function resizeTexture(gl, tex, options, width, height, depth) { width = width || options.width; height = height || options.height; depth = depth || options.depth; var target = options.target || TEXTURE_2D; gl.bindTexture(target, tex); var level = options.level || 0; var internalFormat = options.internalFormat || options.format || RGBA; var formatType = getFormatAndTypeForInternalFormat(internalFormat); var format = options.format || formatType.format; var type; var src = options.src; if (!src) { type = options.type || formatType.type; } else if (isArrayBuffer(src) || Array.isArray(src) && typeof src[0] === 'number') { type = options.type || getTextureTypeForArrayType(gl, src, formatType.type); } else { type = options.type || formatType.type; } if (target === TEXTURE_CUBE_MAP) { for (var ii = 0; ii < 6; ++ii) { gl.texImage2D(TEXTURE_CUBE_MAP_POSITIVE_X + ii, level, internalFormat, width, height, 0, format, type, null); } } else if (target === TEXTURE_3D || target === TEXTURE_2D_ARRAY) { gl.texImage3D(target, level, internalFormat, width, height, depth, 0, format, type, null); } else { gl.texImage2D(target, level, internalFormat, width, height, 0, format, type, null); } } /** * Check if a src is an async request. * if src is a string we're going to download an image * if src is an array of strings we're going to download cubemap images * @param {*} src The src from a TextureOptions * @returns {bool} true if src is async. * @private */ function isAsyncSrc(src) { return typeof src === 'string' || Array.isArray(src) && typeof src[0] === 'string'; } /** * Creates a bunch of textures based on the passed in options. * * Example: * * const textures = twgl.createTextures(gl, { * // a power of 2 image * hftIcon: { src: "images/hft-icon-16.png", mag: gl.NEAREST }, * // a non-power of 2 image * clover: { src: "images/clover.jpg" }, * // From a canvas * fromCanvas: { src: ctx.canvas }, * // A cubemap from 6 images * yokohama: { * target: gl.TEXTURE_CUBE_MAP, * src: [ * 'images/yokohama/posx.jpg', * 'images/yokohama/negx.jpg', * 'images/yokohama/posy.jpg', * 'images/yokohama/negy.jpg', * 'images/yokohama/posz.jpg', * 'images/yokohama/negz.jpg', * ], * }, * // A cubemap from 1 image (can be 1x6, 2x3, 3x2, 6x1) * goldengate: { * target: gl.TEXTURE_CUBE_MAP, * src: 'images/goldengate.jpg', * }, * // A 2x2 pixel texture from a JavaScript array * checker: { * mag: gl.NEAREST, * min: gl.LINEAR, * src: [ * 255,255,255,255, * 192,192,192,255, * 192,192,192,255, * 255,255,255,255, * ], * }, * // a 1x2 pixel texture from a typed array. * stripe: { * mag: gl.NEAREST, * min: gl.LINEAR, * format: gl.LUMINANCE, * src: new Uint8Array([ * 255, * 128, * 255, * 128, * 255, * 128, * 255, * 128, * ]), * width: 1, * }, * }); * * Now * * * `textures.hftIcon` will be a 2d texture * * `textures.clover` will be a 2d texture * * `textures.fromCanvas` will be a 2d texture * * `textures.yohohama` will be a cubemap texture * * `textures.goldengate` will be a cubemap texture * * `textures.checker` will be a 2d texture * * `textures.stripe` will be a 2d texture * * @param {WebGLRenderingContext} gl the WebGLRenderingContext * @param {Object.<string,module:twgl.TextureOptions>} options A object of TextureOptions one per texture. * @param {module:twgl.TexturesReadyCallback} [callback] A callback called when all textures have been downloaded. * @return {Object.<string,WebGLTexture>} the created textures by name * @memberOf module:twgl/textures */ function createTextures(gl, textureOptions, callback) { callback = callback || noop; var numDownloading = 0; var errors = []; var textures = {}; var images = {}; function callCallbackIfReady() { if (numDownloading === 0) { setTimeout(function () { callback(errors.length ? errors : undefined, textures, images); }, 0); } } Object.keys(textureOptions).forEach(function (name) { var options = textureOptions[name]; var onLoadFn; if (isAsyncSrc(options.src)) { onLoadFn = function onLoadFn(err, tex, img) { images[name] = img; --numDownloading; if (err) { errors.push(err); } callCallbackIfReady(); }; ++numDownloading; } textures[name] = createTexture(gl, options, onLoadFn); }); // queue the callback if there are no images to download. // We do this because if your code is structured to wait for // images to download but then you comment out all the async // images your code would break. callCallbackIfReady(); return textures; } /***/ }), /***/ "./src/twgl-base.js": /*!**************************!*\ !*** ./src/twgl-base.js ***! \**************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; exports.__esModule = true; var _twgl = __webpack_require__(/*! ./twgl.js */ "./src/twgl.js"); Object.keys(_twgl).forEach(function (key) { if (key === "default" || key === "__esModule") return; exports[key] = _twgl[key]; }); /***/ }), /***/ "./src/twgl.js": /*!*********************!*\ !*** ./src/twgl.js ***! \*********************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; exports.__esModule = true; var _exportNames = { addExtensionsToContext: true, getContext: true, getWebGLContext: true, resizeCanvasToDisplaySize: true, setDefaults: true, attributes: true, textures: true, utils: true, draw: true, framebuffers: true, programs: true, typedarrays: true, vertexArrays: true }; exports.addExtensionsToContext = addExtensionsToContext; exports.getContext = getContext; exports.getWebGLContext = getWebGLContext; exports.resizeCanvasToDisplaySize = resizeCanvasToDisplaySize; exports.setDefaults = setDefaults; exports.vertexArrays = exports.typedarrays = exports.programs = exports.framebuffers = exports.draw = exports.utils = exports.textures = exports.attributes = void 0; var attributes = _interopRequireWildcard(__webpack_require__(/*! ./attributes.js */ "./src/attributes.js")); exports.attributes = attributes; Object.keys(attributes).forEach(function (key) { if (key === "default" || key === "__esModule") return; if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return; exports[key] = attributes[key]; }); var textures = _interopRequireWildcard(__webpack_require__(/*! ./textures.js */ "./src/textures.js")); exports.textures = textures; Object.keys(textures).forEach(function (key) { if (key === "default" || key === "__esModule") return; if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return; exports[key] = textures[key]; }); var helper = _interopRequireWildcard(__webpack_require__(/*! ./helper.js */ "./src/helper.js")); var utils = _interopRequireWildcard(__webpack_require__(/*! ./utils.js */ "./src/utils.js")); exports.utils = utils; Object.keys(utils).forEach(function (key) { if (key === "default" || key === "__esModule") return; if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return; exports[key] = utils[key]; }); var draw = _interopRequireWildcard(__webpack_require__(/*! ./draw.js */ "./src/draw.js")); exports.draw = draw; Object.keys(draw).forEach(function (key) { if (key === "default" || key === "__esModule") return; if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return; exports[key] = draw[key]; }); var framebuffers = _interopRequireWildcard(__webpack_require__(/*! ./framebuffers.js */ "./src/framebuffers.js")); exports.framebuffers = framebuffers; Object.keys(framebuffers).forEach(function (key) { if (key === "default" || key === "__esModule") return; if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return; exports[key] = framebuffers[key]; }); var programs = _interopRequireWildcard(__webpack_require__(/*! ./programs.js */ "./src/programs.js")); exports.programs = programs; Object.keys(programs).forEach(function (key) { if (key === "default" || key === "__esModule") return; if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return; exports[key] = programs[key]; }); var typedarrays = _interopRequireWildcard(__webpack_require__(/*! ./typedarrays.js */ "./src/typedarrays.js")); exports.typedarrays = typedarrays; Object.keys(typedarrays).forEach(function (key) { if (key === "default" || key === "__esModule") return; if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return; exports[key] = typedarrays[key]; }); var vertexArrays = _interopRequireWildcard(__webpack_require__(/*! ./vertex-arrays.js */ "./src/vertex-arrays.js")); exports.vertexArrays = vertexArrays; Object.keys(vertexArrays).forEach(function (key) { if (key === "default" || key === "__esModule") return; if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return; exports[key] = vertexArrays[key]; }); function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function _getRequireWildcardCache() { return cache; }; return cache; } function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; if (obj != null) { var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj["default"] = obj; if (cache) { cache.set(obj, newObj); } return newObj; } /* * Copyright 2019 Gregg Tavares * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ /** * The main TWGL module. * * For most use cases you shouldn't need anything outside this module. * Exceptions between the stuff added to twgl-full (v3, m4, primitives) * * @module twgl * @borrows module:twgl/attributes.setAttribInfoBufferFromArray as setAttribInfoBufferFromArray * @borrows module:twgl/attributes.createBufferInfoFromArrays as createBufferInfoFromArrays * @borrows module:twgl/attributes.createVertexArrayInfo as createVertexArrayInfo * @borrows module:twgl/draw.drawBufferInfo as drawBufferInfo * @borrows module:twgl/draw.drawObjectList as drawObjectList * @borrows module:twgl/framebuffers.createFramebufferInfo as createFramebufferInfo * @borrows module:twgl/framebuffers.resizeFramebufferInfo as resizeFramebufferInfo * @borrows module:twgl/framebuffers.bindFramebufferInfo as bindFramebufferInfo * @borrows module:twgl/programs.createProgramInfo as createProgramInfo * @borrows module:twgl/programs.createUniformBlockInfo as createUniformBlockInfo * @borrows module:twgl/programs.bindUniformBlock as bindUniformBlock * @borrows module:twgl/programs.setUniformBlock as setUniformBlock * @borrows module:twgl/programs.setBlockUniforms as setBlockUniforms * @borrows module:twgl/programs.setUniforms as setUniforms * @borrows module:twgl/programs.setBuffersAndAttributes as setBuffersAndAttributes * @borrows module:twgl/textures.setTextureFromArray as setTextureFromArray * @borrows module:twgl/textures.createTexture as createTexture * @borrows module:twgl/textures.resizeTexture as resizeTexture * @borrows module:twgl/textures.createTextures as createTextures */ // make sure we don't see a global gl var gl = undefined; /* eslint-disable-line */ /* lgtm [js/unused-local-variable] */ var defaults = { addExtensionsToContext: true }; /** * Various default settings for twgl. * * Note: You can call this any number of times. Example: * * twgl.setDefaults({ textureColor: [1, 0, 0, 1] }); * twgl.setDefaults({ attribPrefix: 'a_' }); * * is equivalent to * * twgl.setDefaults({ * textureColor: [1, 0, 0, 1], * attribPrefix: 'a_', * }); * * @typedef {Object} Defaults * @property {string} [attribPrefix] The prefix to stick on attributes * * When writing shaders I prefer to name attributes with `a_`, uniforms with `u_` and varyings with `v_` * as it makes it clear where they came from. But, when building geometry I prefer using un-prefixed names. * * In other words I'll create arrays of geometry like this * * const arrays = { * position: ... * normal: ... * texcoord: ... * }; * * But need those mapped to attributes and my attributes start with `a_`. * * Default: `""` * * @property {number[]} [textureColor] Array of 4 values in the range 0 to 1 * * The default texture color is used when loading textures from * urls. Because the URL will be loaded async we'd like to be * able to use the texture immediately. By putting a 1x1 pixel * color in the texture we can start using the texture before * the URL has loaded. * * Default: `[0.5, 0.75, 1, 1]` * * @property {string} [crossOrigin] * * If not undefined sets the crossOrigin attribute on images * that twgl creates when downloading images for textures. * * Also see {@link module:twgl.TextureOptions}. * * @property {bool} [addExtensionsToContext] * * If true, then, when twgl will try to add any supported WebGL extensions * directly to the context under their normal GL names. For example * if ANGLE_instances_arrays exists then twgl would enable it, * add the functions `vertexAttribDivisor`, `drawArraysInstanced`, * `drawElementsInstanced`, and the constant `VERTEX_ATTRIB_ARRAY_DIVISOR` * to the `WebGLRenderingContext`. * * @memberOf module:twgl */ /** * Sets various defaults for twgl. * * In the interest of terseness which is kind of the point * of twgl I've integrated a few of the older functions here * * @param {module:twgl.Defaults} newDefaults The default settings. * @memberOf module:twgl */ function setDefaults(newDefaults) { helper.copyExistingProperties(newDefaults, defaults); attributes.setAttributeDefaults_(newDefaults); // eslint-disable-line textures.setTextureDefaults_(newDefaults); // eslint-disable-line } var prefixRE = /^(.*?)_/; function addExtensionToContext(gl, extensionName) { utils.glEnumToString(gl, 0); var ext = gl.getExtension(extensionName); if (ext) { var enums = {}; var fnSuffix = prefixRE.exec(extensionName)[1]; var enumSuffix = '_' + fnSuffix; for (var key in ext) { var value = ext[key]; var isFunc = typeof value === 'function'; var suffix = isFunc ? fnSuffix : enumSuffix; var name = key; // examples of where this is not true are WEBGL_compressed_texture_s3tc // and WEBGL_compressed_texture_pvrtc if (key.endsWith(suffix)) { name = key.substring(0, key.length - suffix.length); } if (gl[name] !== undefined) { if (!isFunc && gl[name] !== value) { helper.warn(name, gl[name], value, key); } } else { if (isFunc) { gl[name] = function (origFn) { return function () { return origFn.apply(ext, arguments); }; }(value); } else { gl[name] = value; enums[name] = value; } } } // pass the modified enums to glEnumToString enums.constructor = { name: ext.constructor.name }; utils.glEnumToString(enums, 0); } return ext; } /* * If you're wondering why the code doesn't just iterate * over all extensions using `gl.getExtensions` is that it's possible * some future extension is incompatible with this code. Rather than * have thing suddenly break it seems better to manually add to this * list. * */ var supportedExtensions = ['ANGLE_instanced_arrays', 'EXT_blend_minmax', 'EXT_color_buffer_float', 'EXT_color_buffer_half_float', 'EXT_disjoint_timer_query', 'EXT_disjoint_timer_query_webgl2', 'EXT_frag_depth', 'EXT_sRGB', 'EXT_shader_texture_lod', 'EXT_texture_filter_anisotropic', 'OES_element_index_uint', 'OES_standard_derivatives', 'OES_texture_float', 'OES_texture_float_linear', 'OES_texture_half_float', 'OES_texture_half_float_linear', 'OES_vertex_array_object', 'WEBGL_color_buffer_float', 'WEBGL_compressed_texture_atc', 'WEBGL_compressed_texture_etc1', 'WEBGL_compressed_texture_pvrtc', 'WEBGL_compressed_texture_s3tc', 'WEBGL_compressed_texture_s3tc_srgb', 'WEBGL_depth_texture', 'WEBGL_draw_buffers']; /** * Attempts to enable all of the following extensions * and add their functions and constants to the * `WebGLRenderingContext` using their normal non-extension like names. * * ANGLE_instanced_arrays * EXT_blend_minmax * EXT_color_buffer_float * EXT_color_buffer_half_float * EXT_disjoint_timer_query * EXT_disjoint_timer_query_webgl2 * EXT_frag_depth * EXT_sRGB * EXT_shader_texture_lod * EXT_texture_filter_anisotropic * OES_element_index_uint * OES_standard_derivatives * OES_texture_float * OES_texture_float_linear * OES_texture_half_float * OES_texture_half_float_linear * OES_vertex_array_object * WEBGL_color_buffer_float * WEBGL_compressed_texture_atc * WEBGL_compressed_texture_etc1 * WEBGL_compressed_texture_pvrtc * WEBGL_compressed_texture_s3tc * WEBGL_compressed_texture_s3tc_srgb * WEBGL_depth_texture * WEBGL_draw_buffers * * For example if `ANGLE_instanced_arrays` exists then the functions * `drawArraysInstanced`, `drawElementsInstanced`, `vertexAttribDivisor` * and the constant `VERTEX_ATTRIB_ARRAY_DIVISOR` are added to the * `WebGLRenderingContext`. * * Note that if you want to know if the extension exists you should * probably call `gl.getExtension` for each extension. Alternatively * you can check for the existence of the functions or constants that * are expected to be added. For example * * if (gl.drawBuffers) { * // Either WEBGL_draw_buffers was enabled OR you're running in WebGL2 * .... * * @param {WebGLRenderingContext} gl A WebGLRenderingContext * @memberOf module:twgl */ function addExtensionsToContext(gl) { for (var ii = 0; ii < supportedExtensions.length; ++ii) { addExtensionToContext(gl, supportedExtensions[ii]); } } /** * Creates a webgl context. * @param {HTMLCanvasElement} canvas The canvas tag to get * context from. If one is not passed in one will be * created. * @return {WebGLRenderingContext} The created context. * @private */ function create3DContext(canvas, opt_attribs) { var names = ["webgl", "experimental-webgl"]; var context = null; for (var ii = 0; ii < names.length; ++ii) { context = canvas.getContext(names[ii], opt_attribs); if (context) { if (defaults.addExtensionsToContext) { addExtensionsToContext(context); } break; } } return context; } /** * Gets a WebGL1 context. * * Note: Will attempt to enable Vertex Array Objects * and add WebGL2 entry points. (unless you first set defaults with * `twgl.setDefaults({enableVertexArrayObjects: false})`; * * @param {HTMLCanvasElement} canvas a canvas element. * @param {WebGLContextAttributes} [opt_attribs] optional webgl context creation attributes * @return {WebGLRenderingContext} The created context. * @memberOf module:twgl */ function getWebGLContext(canvas, opt_attribs) { var gl = create3DContext(canvas, opt_attribs); return gl; } /** * Creates a webgl context. * * Will return a WebGL2 context if possible. * * You can check if it's WebGL2 with * * twgl.isWebGL2(gl); * * @param {HTMLCanvasElement} canvas The canvas tag to get * context from. If one is not passed in one will be * created. * @return {WebGLRenderingContext} The created context. */ function createContext(canvas, opt_attribs) { var names = ["webgl2", "webgl", "experimental-webgl"]; var context = null; for (var ii = 0; ii < names.length; ++ii) { context = canvas.getContext(names[ii], opt_attribs); if (context) { if (defaults.addExtensionsToContext) { addExtensionsToContext(context); } break; } } return context; } /** * Gets a WebGL context. Will create a WebGL2 context if possible. * * You can check if it's WebGL2 with * * function isWebGL2(gl) { * return gl.getParameter(gl.VERSION).indexOf("WebGL 2.0 ") == 0; * } * * Note: For a WebGL1 context will attempt to enable Vertex Array Objects * and add WebGL2 entry points. (unless you first set defaults with * `twgl.setDefaults({enableVertexArrayObjects: false})`; * * @param {HTMLCanvasElement} canvas a canvas element. * @param {WebGLContextAttributes} [opt_attribs] optional webgl context creation attributes * @return {WebGLRenderingContext} The created context. * @memberOf module:twgl */ function getContext(canvas, opt_attribs) { var gl = createContext(canvas, opt_attribs); return gl; } /** * Resize a canvas to match the size it's displayed. * @param {HTMLCanvasElement} canvas The canvas to resize. * @param {number} [multiplier] So you can pass in `window.devicePixelRatio` or other scale value if you want to. * @return {boolean} true if the canvas was resized. * @memberOf module:twgl */ function resizeCanvasToDisplaySize(canvas, multiplier) { multiplier = multiplier || 1; multiplier = Math.max(0, multiplier); var width = canvas.clientWidth * multiplier | 0; var height = canvas.clientHeight * multiplier | 0; if (canvas.width !== width || canvas.height !== height) { canvas.width = width; canvas.height = height; return true; } return false; } /***/ }), /***/ "./src/typedarrays.js": /*!****************************!*\ !*** ./src/typedarrays.js ***! \****************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; exports.__esModule = true; exports.getGLTypeForTypedArray = getGLTypeForTypedArray; exports.getGLTypeForTypedArrayType = getGLTypeForTypedArrayType; exports.getTypedArrayTypeForGLType = getTypedArrayTypeForGLType; exports.isArrayBuffer = void 0; /* * Copyright 2019 Gregg Tavares * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ /** * Low level shader typed array related functions * * You should generally not need to use these functions. They are provided * for those cases where you're doing something out of the ordinary * and you need lower level access. * * For backward compatibility they are available at both `twgl.typedArray` and `twgl` * itself * * See {@link module:twgl} for core functions * * @module twgl/typedArray */ // make sure we don't see a global gl var gl = undefined; /* eslint-disable-line */ /* lgtm [js/unused-local-variable] */ /* DataType */ var BYTE = 0x1400; var UNSIGNED_BYTE = 0x1401; var SHORT = 0x1402; var UNSIGNED_SHORT = 0x1403; var INT = 0x1404; var UNSIGNED_INT = 0x1405; var FLOAT = 0x1406; var UNSIGNED_SHORT_4_4_4_4 = 0x8033; var UNSIGNED_SHORT_5_5_5_1 = 0x8034; var UNSIGNED_SHORT_5_6_5 = 0x8363; var HALF_FLOAT = 0x140B; var UNSIGNED_INT_2_10_10_10_REV = 0x8368; var UNSIGNED_INT_10F_11F_11F_REV = 0x8C3B; var UNSIGNED_INT_5_9_9_9_REV = 0x8C3E; var FLOAT_32_UNSIGNED_INT_24_8_REV = 0x8DAD; var UNSIGNED_INT_24_8 = 0x84FA; var glTypeToTypedArray = {}; { var tt = glTypeToTypedArray; tt[BYTE] = Int8Array; tt[UNSIGNED_BYTE] = Uint8Array; tt[SHORT] = Int16Array; tt[UNSIGNED_SHORT] = Uint16Array; tt[INT] = Int32Array; tt[UNSIGNED_INT] = Uint32Array; tt[FLOAT] = Float32Array; tt[UNSIGNED_SHORT_4_4_4_4] = Uint16Array; tt[UNSIGNED_SHORT_5_5_5_1] = Uint16Array; tt[UNSIGNED_SHORT_5_6_5] = Uint16Array; tt[HALF_FLOAT] = Uint16Array; tt[UNSIGNED_INT_2_10_10_10_REV] = Uint32Array; tt[UNSIGNED_INT_10F_11F_11F_REV] = Uint32Array; tt[UNSIGNED_INT_5_9_9_9_REV] = Uint32Array; tt[FLOAT_32_UNSIGNED_INT_24_8_REV] = Uint32Array; tt[UNSIGNED_INT_24_8] = Uint32Array; } /** * Get the GL type for a typedArray * @param {ArrayBufferView} typedArray a typedArray * @return {number} the GL type for array. For example pass in an `Int8Array` and `gl.BYTE` will * be returned. Pass in a `Uint32Array` and `gl.UNSIGNED_INT` will be returned * @memberOf module:twgl/typedArray */ function getGLTypeForTypedArray(typedArray) { if (typedArray instanceof Int8Array) { return BYTE; } // eslint-disable-line if (typedArray instanceof Uint8Array) { return UNSIGNED_BYTE; } // eslint-disable-line if (typedArray instanceof Uint8ClampedArray) { return UNSIGNED_BYTE; } // eslint-disable-line if (typedArray instanceof Int16Array) { return SHORT; } // eslint-disable-line if (typedArray instanceof Uint16Array) { return UNSIGNED_SHORT; } // eslint-disable-line if (typedArray instanceof Int32Array) { return INT; } // eslint-disable-line if (typedArray instanceof Uint32Array) { return UNSIGNED_INT; } // eslint-disable-line if (typedArray instanceof Float32Array) { return FLOAT; } // eslint-disable-line throw new Error('unsupported typed array type'); } /** * Get the GL type for a typedArray type * @param {ArrayBufferView} typedArrayType a typedArray constructor * @return {number} the GL type for type. For example pass in `Int8Array` and `gl.BYTE` will * be returned. Pass in `Uint32Array` and `gl.UNSIGNED_INT` will be returned * @memberOf module:twgl/typedArray */ function getGLTypeForTypedArrayType(typedArrayType) { if (typedArrayType === Int8Array) { return BYTE; } // eslint-disable-line if (typedArrayType === Uint8Array) { return UNSIGNED_BYTE; } // eslint-disable-line if (typedArrayType === Uint8ClampedArray) { return UNSIGNED_BYTE; } // eslint-disable-line if (typedArrayType === Int16Array) { return SHORT; } // eslint-disable-line if (typedArrayType === Uint16Array) { return UNSIGNED_SHORT; } // eslint-disable-line if (typedArrayType === Int32Array) { return INT; } // eslint-disable-line if (typedArrayType === Uint32Array) { return UNSIGNED_INT; } // eslint-disable-line if (typedArrayType === Float32Array) { return FLOAT; } // eslint-disable-line throw new Error('unsupported typed array type'); } /** * Get the typed array constructor for a given GL type * @param {number} type the GL type. (eg: `gl.UNSIGNED_INT`) * @return {function} the constructor for a the corresponding typed array. (eg. `Uint32Array`). * @memberOf module:twgl/typedArray */ function getTypedArrayTypeForGLType(type) { var CTOR = glTypeToTypedArray[type]; if (!CTOR) { throw new Error('unknown gl type'); } return CTOR; } var isArrayBuffer = typeof SharedArrayBuffer !== 'undefined' ? function isArrayBufferOrSharedArrayBuffer(a) { return a && a.buffer && (a.buffer instanceof ArrayBuffer || a.buffer instanceof SharedArrayBuffer); } : function isArrayBuffer(a) { return a && a.buffer && a.buffer instanceof ArrayBuffer; }; exports.isArrayBuffer = isArrayBuffer; /***/ }), /***/ "./src/utils.js": /*!**********************!*\ !*** ./src/utils.js ***! \**********************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; exports.__esModule = true; exports.isWebGL1 = isWebGL1; exports.isWebGL2 = isWebGL2; exports.glEnumToString = void 0; /* * Copyright 2019 Gregg Tavares * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ /** * Gets the gl version as a number * @param {WebGLRenderingContext} gl A WebGLRenderingContext * @return {number} version of gl * @private */ //function getVersionAsNumber(gl) { // return parseFloat(gl.getParameter(gl.VERSION).substr(6)); //} /** * Check if context is WebGL 2.0 * @param {WebGLRenderingContext} gl A WebGLRenderingContext * @return {bool} true if it's WebGL 2.0 * @memberOf module:twgl */ function isWebGL2(gl) { // This is the correct check but it's slow // return gl.getParameter(gl.VERSION).indexOf("WebGL 2.0") === 0; // This might also be the correct check but I'm assuming it's slow-ish // return gl instanceof WebGL2RenderingContext; return !!gl.texStorage2D; } /** * Check if context is WebGL 1.0 * @param {WebGLRenderingContext} gl A WebGLRenderingContext * @return {bool} true if it's WebGL 1.0 * @memberOf module:twgl */ function isWebGL1(gl) { // This is the correct check but it's slow // const version = getVersionAsNumber(gl); // return version <= 1.0 && version > 0.0; // because as of 2016/5 Edge returns 0.96 // This might also be the correct check but I'm assuming it's slow-ish // return gl instanceof WebGLRenderingContext; return !gl.texStorage2D; } /** * Gets a string for WebGL enum * * Note: Several enums are the same. Without more * context (which function) it's impossible to always * give the correct enum. As it is, for matching values * it gives all enums. Checking the WebGL2RenderingContext * that means * * 0 = ZERO | POINT | NONE | NO_ERROR * 1 = ONE | LINES | SYNC_FLUSH_COMMANDS_BIT * 32777 = BLEND_EQUATION_RGB | BLEND_EQUATION_RGB * 36662 = COPY_READ_BUFFER | COPY_READ_BUFFER_BINDING * 36663 = COPY_WRITE_BUFFER | COPY_WRITE_BUFFER_BINDING * 36006 = FRAMEBUFFER_BINDING | DRAW_FRAMEBUFFER_BINDING * * It's also not useful for bits really unless you pass in individual bits. * In other words * * const bits = gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT; * twgl.glEnumToString(gl, bits); // not going to work * * Note that some enums only exist on extensions. If you * want them to show up you need to pass the extension at least * once. For example * * const ext = gl.getExtension('WEBGL_compressed_texture_s3tc'); * if (ext) { * twgl.glEnumToString(ext, 0); // just prime the function * * ..later.. * * const internalFormat = ext.COMPRESSED_RGB_S3TC_DXT1_EXT; * console.log(twgl.glEnumToString(gl, internalFormat)); * * Notice I didn't have to pass the extension the second time. This means * you can have place that generically gets an enum for texture formats for example. * and as long as you primed the function with the extensions * * If you're using `twgl.addExtensionsToContext` to enable your extensions * then twgl will automatically get the extension's enums. * * @param {WebGLRenderingContext} gl A WebGLRenderingContext or any extension object * @param {number} value the value of the enum you want to look up. * @return {string} enum string or hex value * @memberOf module:twgl * @function glEnumToString */ var glEnumToString = function () { var haveEnumsForType = {}; var enums = {}; function addEnums(gl) { var type = gl.constructor.name; if (!haveEnumsForType[type]) { for (var key in gl) { if (typeof gl[key] === 'number') { var existing = enums[gl[key]]; enums[gl[key]] = existing ? "".concat(existing, " | ").concat(key) : key; } } haveEnumsForType[type] = true; } } return function glEnumToString(gl, value) { addEnums(gl); return enums[value] || "0x" + value.toString(16); }; }(); exports.glEnumToString = glEnumToString; /***/ }), /***/ "./src/vertex-arrays.js": /*!******************************!*\ !*** ./src/vertex-arrays.js ***! \******************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; exports.__esModule = true; exports.createVertexArrayInfo = createVertexArrayInfo; exports.createVAOAndSetAttributes = createVAOAndSetAttributes; exports.createVAOFromBufferInfo = createVAOFromBufferInfo; var programs = _interopRequireWildcard(__webpack_require__(/*! ./programs.js */ "./src/programs.js")); function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function _getRequireWildcardCache() { return cache; }; return cache; } function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; if (obj != null) { var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj["default"] = obj; if (cache) { cache.set(obj, newObj); } return newObj; } /* * Copyright 2019 Gregg Tavares * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ /** * vertex array object related functions * * You should generally not need to use these functions. They are provided * for those cases where you're doing something out of the ordinary * and you need lower level access. * * For backward compatibility they are available at both `twgl.attributes` and `twgl` * itself * * See {@link module:twgl} for core functions * * @module twgl/vertexArrays */ var ELEMENT_ARRAY_BUFFER = 0x8893; /** * @typedef {Object} VertexArrayInfo * @property {number} numElements The number of elements to pass to `gl.drawArrays` or `gl.drawElements`. * @property {number} [elementType] The type of indices `UNSIGNED_BYTE`, `UNSIGNED_SHORT` etc.. * @property {WebGLVertexArrayObject} [vertexArrayObject] a vertex array object * @memberOf module:twgl */ /** * Creates a VertexArrayInfo from a BufferInfo and one or more ProgramInfos * * This can be passed to {@link module:twgl.setBuffersAndAttributes} and to * {@link module:twgl:drawBufferInfo}. * * > **IMPORTANT:** Vertex Array Objects are **not** a direct analog for a BufferInfo. Vertex Array Objects * assign buffers to specific attributes at creation time. That means they can only be used with programs * who's attributes use the same attribute locations for the same purposes. * * > Bind your attribute locations by passing an array of attribute names to {@link module:twgl.createProgramInfo} * or use WebGL 2's GLSL ES 3's `layout(location = <num>)` to make sure locations match. * * also * * > **IMPORTANT:** After calling twgl.setBuffersAndAttribute with a BufferInfo that uses a Vertex Array Object * that Vertex Array Object will be bound. That means **ANY MANIPULATION OF ELEMENT_ARRAY_BUFFER or ATTRIBUTES** * will affect the Vertex Array Object state. * * > Call `gl.bindVertexArray(null)` to get back manipulating the global attributes and ELEMENT_ARRAY_BUFFER. * * @param {WebGLRenderingContext} gl A WebGLRenderingContext * @param {module:twgl.ProgramInfo|module:twgl.ProgramInfo[]} programInfo a programInfo or array of programInfos * @param {module:twgl.BufferInfo} bufferInfo BufferInfo as returned from createBufferInfoFromArrays etc... * * You need to make sure every attribute that will be used is bound. So for example assume shader 1 * uses attributes A, B, C and shader 2 uses attributes A, B, D. If you only pass in the programInfo * for shader 1 then only attributes A, B, and C will have their attributes set because TWGL doesn't * now attribute D's location. * * So, you can pass in both shader 1 and shader 2's programInfo * * @return {module:twgl.VertexArrayInfo} The created VertexArrayInfo * * @memberOf module:twgl/vertexArrays */ function createVertexArrayInfo(gl, programInfos, bufferInfo) { var vao = gl.createVertexArray(); gl.bindVertexArray(vao); if (!programInfos.length) { programInfos = [programInfos]; } programInfos.forEach(function (programInfo) { programs.setBuffersAndAttributes(gl, programInfo, bufferInfo); }); gl.bindVertexArray(null); return { numElements: bufferInfo.numElements, elementType: bufferInfo.elementType, vertexArrayObject: vao }; } /** * Creates a vertex array object and then sets the attributes on it * * @param {WebGLRenderingContext} gl The WebGLRenderingContext to use. * @param {Object.<string, function>} setters Attribute setters as returned from createAttributeSetters * @param {Object.<string, module:twgl.AttribInfo>} attribs AttribInfos mapped by attribute name. * @param {WebGLBuffer} [indices] an optional ELEMENT_ARRAY_BUFFER of indices * @memberOf module:twgl/vertexArrays */ function createVAOAndSetAttributes(gl, setters, attribs, indices) { var vao = gl.createVertexArray(); gl.bindVertexArray(vao); programs.setAttributes(setters, attribs); if (indices) { gl.bindBuffer(ELEMENT_ARRAY_BUFFER, indices); } // We unbind this because otherwise any change to ELEMENT_ARRAY_BUFFER // like when creating buffers for other stuff will mess up this VAO's binding gl.bindVertexArray(null); return vao; } /** * Creates a vertex array object and then sets the attributes * on it * * @param {WebGLRenderingContext} gl The WebGLRenderingContext * to use. * @param {Object.<string, function>| module:twgl.ProgramInfo} programInfo as returned from createProgramInfo or Attribute setters as returned from createAttributeSetters * @param {module:twgl.BufferInfo} bufferInfo BufferInfo as returned from createBufferInfoFromArrays etc... * @param {WebGLBuffer} [indices] an optional ELEMENT_ARRAY_BUFFER of indices * @memberOf module:twgl/vertexArrays */ function createVAOFromBufferInfo(gl, programInfo, bufferInfo) { return createVAOAndSetAttributes(gl, programInfo.attribSetters || programInfo, bufferInfo.attribs, bufferInfo.indices); } /***/ }) /******/ }); }); //# sourceMappingURL=twgl.js.map