File: /home/mirz4654/.trash/waves/blocks/twgl/twgl.js
/*!
* @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