@openhps/core
Version:
Open Hybrid Positioning System - Core component
822 lines (749 loc) • 21 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.WebGLUniforms = void 0;
var _CubeTexture = require("../../textures/CubeTexture.js");
var _Texture = require("../../textures/Texture.js");
var _DataArrayTexture = require("../../textures/DataArrayTexture.js");
var _Data3DTexture = require("../../textures/Data3DTexture.js");
var _DepthTexture = require("../../textures/DepthTexture.js");
var _constants = require("../../constants.js");
/**
* Uniforms of a program.
* Those form a tree structure with a special top-level container for the root,
* which you get by calling 'new WebGLUniforms( gl, program )'.
*
*
* Properties of inner nodes including the top-level container:
*
* .seq - array of nested uniforms
* .map - nested uniforms by name
*
*
* Methods of all nodes except the top-level container:
*
* .setValue( gl, value, [textures] )
*
* uploads a uniform value(s)
* the 'textures' parameter is needed for sampler uniforms
*
*
* Static methods of the top-level container (textures factorizations):
*
* .upload( gl, seq, values, textures )
*
* sets uniforms in 'seq' to 'values[id].value'
*
* .seqWithValue( seq, values ) : filteredSeq
*
* filters 'seq' entries with corresponding entry in values
*
*
* Methods of the top-level container (textures factorizations):
*
* .setValue( gl, name, value, textures )
*
* sets uniform with name 'name' to 'value'
*
* .setOptional( gl, obj, prop )
*
* like .set for an optional property of the object
*
*/
const emptyTexture = /*@__PURE__*/new _Texture.Texture();
const emptyShadowTexture = /*@__PURE__*/new _DepthTexture.DepthTexture(1, 1);
const emptyArrayTexture = /*@__PURE__*/new _DataArrayTexture.DataArrayTexture();
const empty3dTexture = /*@__PURE__*/new _Data3DTexture.Data3DTexture();
const emptyCubeTexture = /*@__PURE__*/new _CubeTexture.CubeTexture();
// --- Utilities ---
// Array Caches (provide typed arrays for temporary by size)
const arrayCacheF32 = [];
const arrayCacheI32 = [];
// Float32Array caches used for uploading Matrix uniforms
const mat4array = new Float32Array(16);
const mat3array = new Float32Array(9);
const mat2array = new Float32Array(4);
// Flattening for arrays of vectors and matrices
function flatten(array, nBlocks, blockSize) {
const firstElem = array[0];
if (firstElem <= 0 || firstElem > 0) return array;
// unoptimized: ! isNaN( firstElem )
// see http://jacksondunstan.com/articles/983
const n = nBlocks * blockSize;
let r = arrayCacheF32[n];
if (r === undefined) {
r = new Float32Array(n);
arrayCacheF32[n] = r;
}
if (nBlocks !== 0) {
firstElem.toArray(r, 0);
for (let i = 1, offset = 0; i !== nBlocks; ++i) {
offset += blockSize;
array[i].toArray(r, offset);
}
}
return r;
}
function arraysEqual(a, b) {
if (a.length !== b.length) return false;
for (let i = 0, l = a.length; i < l; i++) {
if (a[i] !== b[i]) return false;
}
return true;
}
function copyArray(a, b) {
for (let i = 0, l = b.length; i < l; i++) {
a[i] = b[i];
}
}
// Texture unit allocation
function allocTexUnits(textures, n) {
let r = arrayCacheI32[n];
if (r === undefined) {
r = new Int32Array(n);
arrayCacheI32[n] = r;
}
for (let i = 0; i !== n; ++i) {
r[i] = textures.allocateTextureUnit();
}
return r;
}
// --- Setters ---
// Note: Defining these methods externally, because they come in a bunch
// and this way their names minify.
// Single scalar
function setValueV1f(gl, v) {
const cache = this.cache;
if (cache[0] === v) return;
gl.uniform1f(this.addr, v);
cache[0] = v;
}
// Single float vector (from flat array or THREE.VectorN)
function setValueV2f(gl, v) {
const cache = this.cache;
if (v.x !== undefined) {
if (cache[0] !== v.x || cache[1] !== v.y) {
gl.uniform2f(this.addr, v.x, v.y);
cache[0] = v.x;
cache[1] = v.y;
}
} else {
if (arraysEqual(cache, v)) return;
gl.uniform2fv(this.addr, v);
copyArray(cache, v);
}
}
function setValueV3f(gl, v) {
const cache = this.cache;
if (v.x !== undefined) {
if (cache[0] !== v.x || cache[1] !== v.y || cache[2] !== v.z) {
gl.uniform3f(this.addr, v.x, v.y, v.z);
cache[0] = v.x;
cache[1] = v.y;
cache[2] = v.z;
}
} else if (v.r !== undefined) {
if (cache[0] !== v.r || cache[1] !== v.g || cache[2] !== v.b) {
gl.uniform3f(this.addr, v.r, v.g, v.b);
cache[0] = v.r;
cache[1] = v.g;
cache[2] = v.b;
}
} else {
if (arraysEqual(cache, v)) return;
gl.uniform3fv(this.addr, v);
copyArray(cache, v);
}
}
function setValueV4f(gl, v) {
const cache = this.cache;
if (v.x !== undefined) {
if (cache[0] !== v.x || cache[1] !== v.y || cache[2] !== v.z || cache[3] !== v.w) {
gl.uniform4f(this.addr, v.x, v.y, v.z, v.w);
cache[0] = v.x;
cache[1] = v.y;
cache[2] = v.z;
cache[3] = v.w;
}
} else {
if (arraysEqual(cache, v)) return;
gl.uniform4fv(this.addr, v);
copyArray(cache, v);
}
}
// Single matrix (from flat array or THREE.MatrixN)
function setValueM2(gl, v) {
const cache = this.cache;
const elements = v.elements;
if (elements === undefined) {
if (arraysEqual(cache, v)) return;
gl.uniformMatrix2fv(this.addr, false, v);
copyArray(cache, v);
} else {
if (arraysEqual(cache, elements)) return;
mat2array.set(elements);
gl.uniformMatrix2fv(this.addr, false, mat2array);
copyArray(cache, elements);
}
}
function setValueM3(gl, v) {
const cache = this.cache;
const elements = v.elements;
if (elements === undefined) {
if (arraysEqual(cache, v)) return;
gl.uniformMatrix3fv(this.addr, false, v);
copyArray(cache, v);
} else {
if (arraysEqual(cache, elements)) return;
mat3array.set(elements);
gl.uniformMatrix3fv(this.addr, false, mat3array);
copyArray(cache, elements);
}
}
function setValueM4(gl, v) {
const cache = this.cache;
const elements = v.elements;
if (elements === undefined) {
if (arraysEqual(cache, v)) return;
gl.uniformMatrix4fv(this.addr, false, v);
copyArray(cache, v);
} else {
if (arraysEqual(cache, elements)) return;
mat4array.set(elements);
gl.uniformMatrix4fv(this.addr, false, mat4array);
copyArray(cache, elements);
}
}
// Single integer / boolean
function setValueV1i(gl, v) {
const cache = this.cache;
if (cache[0] === v) return;
gl.uniform1i(this.addr, v);
cache[0] = v;
}
// Single integer / boolean vector (from flat array or THREE.VectorN)
function setValueV2i(gl, v) {
const cache = this.cache;
if (v.x !== undefined) {
if (cache[0] !== v.x || cache[1] !== v.y) {
gl.uniform2i(this.addr, v.x, v.y);
cache[0] = v.x;
cache[1] = v.y;
}
} else {
if (arraysEqual(cache, v)) return;
gl.uniform2iv(this.addr, v);
copyArray(cache, v);
}
}
function setValueV3i(gl, v) {
const cache = this.cache;
if (v.x !== undefined) {
if (cache[0] !== v.x || cache[1] !== v.y || cache[2] !== v.z) {
gl.uniform3i(this.addr, v.x, v.y, v.z);
cache[0] = v.x;
cache[1] = v.y;
cache[2] = v.z;
}
} else {
if (arraysEqual(cache, v)) return;
gl.uniform3iv(this.addr, v);
copyArray(cache, v);
}
}
function setValueV4i(gl, v) {
const cache = this.cache;
if (v.x !== undefined) {
if (cache[0] !== v.x || cache[1] !== v.y || cache[2] !== v.z || cache[3] !== v.w) {
gl.uniform4i(this.addr, v.x, v.y, v.z, v.w);
cache[0] = v.x;
cache[1] = v.y;
cache[2] = v.z;
cache[3] = v.w;
}
} else {
if (arraysEqual(cache, v)) return;
gl.uniform4iv(this.addr, v);
copyArray(cache, v);
}
}
// Single unsigned integer
function setValueV1ui(gl, v) {
const cache = this.cache;
if (cache[0] === v) return;
gl.uniform1ui(this.addr, v);
cache[0] = v;
}
// Single unsigned integer vector (from flat array or THREE.VectorN)
function setValueV2ui(gl, v) {
const cache = this.cache;
if (v.x !== undefined) {
if (cache[0] !== v.x || cache[1] !== v.y) {
gl.uniform2ui(this.addr, v.x, v.y);
cache[0] = v.x;
cache[1] = v.y;
}
} else {
if (arraysEqual(cache, v)) return;
gl.uniform2uiv(this.addr, v);
copyArray(cache, v);
}
}
function setValueV3ui(gl, v) {
const cache = this.cache;
if (v.x !== undefined) {
if (cache[0] !== v.x || cache[1] !== v.y || cache[2] !== v.z) {
gl.uniform3ui(this.addr, v.x, v.y, v.z);
cache[0] = v.x;
cache[1] = v.y;
cache[2] = v.z;
}
} else {
if (arraysEqual(cache, v)) return;
gl.uniform3uiv(this.addr, v);
copyArray(cache, v);
}
}
function setValueV4ui(gl, v) {
const cache = this.cache;
if (v.x !== undefined) {
if (cache[0] !== v.x || cache[1] !== v.y || cache[2] !== v.z || cache[3] !== v.w) {
gl.uniform4ui(this.addr, v.x, v.y, v.z, v.w);
cache[0] = v.x;
cache[1] = v.y;
cache[2] = v.z;
cache[3] = v.w;
}
} else {
if (arraysEqual(cache, v)) return;
gl.uniform4uiv(this.addr, v);
copyArray(cache, v);
}
}
// Single texture (2D / Cube)
function setValueT1(gl, v, textures) {
const cache = this.cache;
const unit = textures.allocateTextureUnit();
if (cache[0] !== unit) {
gl.uniform1i(this.addr, unit);
cache[0] = unit;
}
let emptyTexture2D;
if (this.type === gl.SAMPLER_2D_SHADOW) {
emptyShadowTexture.compareFunction = _constants.LessEqualCompare; // #28670
emptyTexture2D = emptyShadowTexture;
} else {
emptyTexture2D = emptyTexture;
}
textures.setTexture2D(v || emptyTexture2D, unit);
}
function setValueT3D1(gl, v, textures) {
const cache = this.cache;
const unit = textures.allocateTextureUnit();
if (cache[0] !== unit) {
gl.uniform1i(this.addr, unit);
cache[0] = unit;
}
textures.setTexture3D(v || empty3dTexture, unit);
}
function setValueT6(gl, v, textures) {
const cache = this.cache;
const unit = textures.allocateTextureUnit();
if (cache[0] !== unit) {
gl.uniform1i(this.addr, unit);
cache[0] = unit;
}
textures.setTextureCube(v || emptyCubeTexture, unit);
}
function setValueT2DArray1(gl, v, textures) {
const cache = this.cache;
const unit = textures.allocateTextureUnit();
if (cache[0] !== unit) {
gl.uniform1i(this.addr, unit);
cache[0] = unit;
}
textures.setTexture2DArray(v || emptyArrayTexture, unit);
}
// Helper to pick the right setter for the singular case
function getSingularSetter(type) {
switch (type) {
case 0x1406:
return setValueV1f;
// FLOAT
case 0x8b50:
return setValueV2f;
// _VEC2
case 0x8b51:
return setValueV3f;
// _VEC3
case 0x8b52:
return setValueV4f;
// _VEC4
case 0x8b5a:
return setValueM2;
// _MAT2
case 0x8b5b:
return setValueM3;
// _MAT3
case 0x8b5c:
return setValueM4;
// _MAT4
case 0x1404:
case 0x8b56:
return setValueV1i;
// INT, BOOL
case 0x8b53:
case 0x8b57:
return setValueV2i;
// _VEC2
case 0x8b54:
case 0x8b58:
return setValueV3i;
// _VEC3
case 0x8b55:
case 0x8b59:
return setValueV4i;
// _VEC4
case 0x1405:
return setValueV1ui;
// UINT
case 0x8dc6:
return setValueV2ui;
// _VEC2
case 0x8dc7:
return setValueV3ui;
// _VEC3
case 0x8dc8:
return setValueV4ui;
// _VEC4
case 0x8b5e: // SAMPLER_2D
case 0x8d66: // SAMPLER_EXTERNAL_OES
case 0x8dca: // INT_SAMPLER_2D
case 0x8dd2: // UNSIGNED_INT_SAMPLER_2D
case 0x8b62:
// SAMPLER_2D_SHADOW
return setValueT1;
case 0x8b5f: // SAMPLER_3D
case 0x8dcb: // INT_SAMPLER_3D
case 0x8dd3:
// UNSIGNED_INT_SAMPLER_3D
return setValueT3D1;
case 0x8b60: // SAMPLER_CUBE
case 0x8dcc: // INT_SAMPLER_CUBE
case 0x8dd4: // UNSIGNED_INT_SAMPLER_CUBE
case 0x8dc5:
// SAMPLER_CUBE_SHADOW
return setValueT6;
case 0x8dc1: // SAMPLER_2D_ARRAY
case 0x8dcf: // INT_SAMPLER_2D_ARRAY
case 0x8dd7: // UNSIGNED_INT_SAMPLER_2D_ARRAY
case 0x8dc4:
// SAMPLER_2D_ARRAY_SHADOW
return setValueT2DArray1;
}
}
// Array of scalars
function setValueV1fArray(gl, v) {
gl.uniform1fv(this.addr, v);
}
// Array of vectors (from flat array or array of THREE.VectorN)
function setValueV2fArray(gl, v) {
const data = flatten(v, this.size, 2);
gl.uniform2fv(this.addr, data);
}
function setValueV3fArray(gl, v) {
const data = flatten(v, this.size, 3);
gl.uniform3fv(this.addr, data);
}
function setValueV4fArray(gl, v) {
const data = flatten(v, this.size, 4);
gl.uniform4fv(this.addr, data);
}
// Array of matrices (from flat array or array of THREE.MatrixN)
function setValueM2Array(gl, v) {
const data = flatten(v, this.size, 4);
gl.uniformMatrix2fv(this.addr, false, data);
}
function setValueM3Array(gl, v) {
const data = flatten(v, this.size, 9);
gl.uniformMatrix3fv(this.addr, false, data);
}
function setValueM4Array(gl, v) {
const data = flatten(v, this.size, 16);
gl.uniformMatrix4fv(this.addr, false, data);
}
// Array of integer / boolean
function setValueV1iArray(gl, v) {
gl.uniform1iv(this.addr, v);
}
// Array of integer / boolean vectors (from flat array)
function setValueV2iArray(gl, v) {
gl.uniform2iv(this.addr, v);
}
function setValueV3iArray(gl, v) {
gl.uniform3iv(this.addr, v);
}
function setValueV4iArray(gl, v) {
gl.uniform4iv(this.addr, v);
}
// Array of unsigned integer
function setValueV1uiArray(gl, v) {
gl.uniform1uiv(this.addr, v);
}
// Array of unsigned integer vectors (from flat array)
function setValueV2uiArray(gl, v) {
gl.uniform2uiv(this.addr, v);
}
function setValueV3uiArray(gl, v) {
gl.uniform3uiv(this.addr, v);
}
function setValueV4uiArray(gl, v) {
gl.uniform4uiv(this.addr, v);
}
// Array of textures (2D / 3D / Cube / 2DArray)
function setValueT1Array(gl, v, textures) {
const cache = this.cache;
const n = v.length;
const units = allocTexUnits(textures, n);
if (!arraysEqual(cache, units)) {
gl.uniform1iv(this.addr, units);
copyArray(cache, units);
}
for (let i = 0; i !== n; ++i) {
textures.setTexture2D(v[i] || emptyTexture, units[i]);
}
}
function setValueT3DArray(gl, v, textures) {
const cache = this.cache;
const n = v.length;
const units = allocTexUnits(textures, n);
if (!arraysEqual(cache, units)) {
gl.uniform1iv(this.addr, units);
copyArray(cache, units);
}
for (let i = 0; i !== n; ++i) {
textures.setTexture3D(v[i] || empty3dTexture, units[i]);
}
}
function setValueT6Array(gl, v, textures) {
const cache = this.cache;
const n = v.length;
const units = allocTexUnits(textures, n);
if (!arraysEqual(cache, units)) {
gl.uniform1iv(this.addr, units);
copyArray(cache, units);
}
for (let i = 0; i !== n; ++i) {
textures.setTextureCube(v[i] || emptyCubeTexture, units[i]);
}
}
function setValueT2DArrayArray(gl, v, textures) {
const cache = this.cache;
const n = v.length;
const units = allocTexUnits(textures, n);
if (!arraysEqual(cache, units)) {
gl.uniform1iv(this.addr, units);
copyArray(cache, units);
}
for (let i = 0; i !== n; ++i) {
textures.setTexture2DArray(v[i] || emptyArrayTexture, units[i]);
}
}
// Helper to pick the right setter for a pure (bottom-level) array
function getPureArraySetter(type) {
switch (type) {
case 0x1406:
return setValueV1fArray;
// FLOAT
case 0x8b50:
return setValueV2fArray;
// _VEC2
case 0x8b51:
return setValueV3fArray;
// _VEC3
case 0x8b52:
return setValueV4fArray;
// _VEC4
case 0x8b5a:
return setValueM2Array;
// _MAT2
case 0x8b5b:
return setValueM3Array;
// _MAT3
case 0x8b5c:
return setValueM4Array;
// _MAT4
case 0x1404:
case 0x8b56:
return setValueV1iArray;
// INT, BOOL
case 0x8b53:
case 0x8b57:
return setValueV2iArray;
// _VEC2
case 0x8b54:
case 0x8b58:
return setValueV3iArray;
// _VEC3
case 0x8b55:
case 0x8b59:
return setValueV4iArray;
// _VEC4
case 0x1405:
return setValueV1uiArray;
// UINT
case 0x8dc6:
return setValueV2uiArray;
// _VEC2
case 0x8dc7:
return setValueV3uiArray;
// _VEC3
case 0x8dc8:
return setValueV4uiArray;
// _VEC4
case 0x8b5e: // SAMPLER_2D
case 0x8d66: // SAMPLER_EXTERNAL_OES
case 0x8dca: // INT_SAMPLER_2D
case 0x8dd2: // UNSIGNED_INT_SAMPLER_2D
case 0x8b62:
// SAMPLER_2D_SHADOW
return setValueT1Array;
case 0x8b5f: // SAMPLER_3D
case 0x8dcb: // INT_SAMPLER_3D
case 0x8dd3:
// UNSIGNED_INT_SAMPLER_3D
return setValueT3DArray;
case 0x8b60: // SAMPLER_CUBE
case 0x8dcc: // INT_SAMPLER_CUBE
case 0x8dd4: // UNSIGNED_INT_SAMPLER_CUBE
case 0x8dc5:
// SAMPLER_CUBE_SHADOW
return setValueT6Array;
case 0x8dc1: // SAMPLER_2D_ARRAY
case 0x8dcf: // INT_SAMPLER_2D_ARRAY
case 0x8dd7: // UNSIGNED_INT_SAMPLER_2D_ARRAY
case 0x8dc4:
// SAMPLER_2D_ARRAY_SHADOW
return setValueT2DArrayArray;
}
}
// --- Uniform Classes ---
class SingleUniform {
constructor(id, activeInfo, addr) {
this.id = id;
this.addr = addr;
this.cache = [];
this.type = activeInfo.type;
this.setValue = getSingularSetter(activeInfo.type);
// this.path = activeInfo.name; // DEBUG
}
}
class PureArrayUniform {
constructor(id, activeInfo, addr) {
this.id = id;
this.addr = addr;
this.cache = [];
this.type = activeInfo.type;
this.size = activeInfo.size;
this.setValue = getPureArraySetter(activeInfo.type);
// this.path = activeInfo.name; // DEBUG
}
}
class StructuredUniform {
constructor(id) {
this.id = id;
this.seq = [];
this.map = {};
}
setValue(gl, value, textures) {
const seq = this.seq;
for (let i = 0, n = seq.length; i !== n; ++i) {
const u = seq[i];
u.setValue(gl, value[u.id], textures);
}
}
}
// --- Top-level ---
// Parser - builds up the property tree from the path strings
const RePathPart = /(\w+)(\])?(\[|\.)?/g;
// extracts
// - the identifier (member name or array index)
// - followed by an optional right bracket (found when array index)
// - followed by an optional left bracket or dot (type of subscript)
//
// Note: These portions can be read in a non-overlapping fashion and
// allow straightforward parsing of the hierarchy that WebGL encodes
// in the uniform names.
function addUniform(container, uniformObject) {
container.seq.push(uniformObject);
container.map[uniformObject.id] = uniformObject;
}
function parseUniform(activeInfo, addr, container) {
const path = activeInfo.name,
pathLength = path.length;
// reset RegExp object, because of the early exit of a previous run
RePathPart.lastIndex = 0;
while (true) {
const match = RePathPart.exec(path),
matchEnd = RePathPart.lastIndex;
let id = match[1];
const idIsIndex = match[2] === ']',
subscript = match[3];
if (idIsIndex) id = id | 0; // convert to integer
if (subscript === undefined || subscript === '[' && matchEnd + 2 === pathLength) {
// bare name or "pure" bottom-level array "[0]" suffix
addUniform(container, subscript === undefined ? new SingleUniform(id, activeInfo, addr) : new PureArrayUniform(id, activeInfo, addr));
break;
} else {
// step into inner node / create it in case it doesn't exist
const map = container.map;
let next = map[id];
if (next === undefined) {
next = new StructuredUniform(id);
addUniform(container, next);
}
container = next;
}
}
}
// Root Container
class WebGLUniforms {
constructor(gl, program) {
this.seq = [];
this.map = {};
const n = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS);
for (let i = 0; i < n; ++i) {
const info = gl.getActiveUniform(program, i),
addr = gl.getUniformLocation(program, info.name);
parseUniform(info, addr, this);
}
}
setValue(gl, name, value, textures) {
const u = this.map[name];
if (u !== undefined) u.setValue(gl, value, textures);
}
setOptional(gl, object, name) {
const v = object[name];
if (v !== undefined) this.setValue(gl, name, v);
}
static upload(gl, seq, values, textures) {
for (let i = 0, n = seq.length; i !== n; ++i) {
const u = seq[i],
v = values[u.id];
if (v.needsUpdate !== false) {
// note: always updating when .needsUpdate is undefined
u.setValue(gl, v.value, textures);
}
}
}
static seqWithValue(seq, values) {
const r = [];
for (let i = 0, n = seq.length; i !== n; ++i) {
const u = seq[i];
if (u.id in values) r.push(u);
}
return r;
}
}
exports.WebGLUniforms = WebGLUniforms;