tangram
Version:
WebGL Maps for Vector Tiles
237 lines (220 loc) • 7.55 kB
JavaScript
var GLSL = {};
export default GLSL;
/**
Parse uniforms from a JS object, infers types and returns an array of objects with the
necessary information to set uniform values on a GL program. Each object in the returned
array has the form:
{ type, method, name, value }
type: the GL uniform type, such as 'vec3', 'float', etc.
method: the GL uniform setter method to use, such as '1f', '3fv', etc.
name: the fully qualified name of the GL uniform location, e.g. 'array[0].field', etc.
value: the value to be passed to the GL uniform setter for that type, e.g. [1, 2, 3] for a vec3
Textures have special behavior: uniforms with string values are treated as textures, and
the string is used as a unique texture 'key' to be interpreted by the caller (which is responsible
for actually setting the uniforms). For example, this could be used as a key into a dictionary of
known texture names, or it could simply be used as a URL to dynamically load the texture from.
*/
GLSL.parseUniforms = function (uniforms = {}) {
var parsed = [];
for (const [name, uniform] of Object.entries(uniforms)) {
// Single float
if (typeof uniform === 'number') {
parsed.push({
type: 'float',
method: '1f',
name,
value: uniform,
path: [name]
});
}
// Array: vector, array of floats, array of textures
else if (Array.isArray(uniform)) {
// Numeric values
if (typeof uniform[0] === 'number') {
// float vectors (vec2, vec3, vec4)
if (uniform.length >= 2 && uniform.length <= 4) {
parsed.push({
type: 'vec' + uniform.length,
method: uniform.length + 'fv',
name,
value: uniform,
path: [name]
});
}
// float array
else if (uniform.length > 4) {
parsed.push({
type: 'float[]',
method: '1fv',
name: name + '[0]',
value: uniform,
path: [name]
});
}
// TODO: assume matrix for (typeof == Float32Array && length == 16)?
}
// Array of textures
else if (typeof uniform[0] === 'string') {
for (let u = 0; u < uniform.length; u++) {
parsed.push({
type: 'sampler2D',
method: '1i',
name: name + '[' + u + ']',
value: uniform[u],
path: [name, u]
});
}
}
// Array of arrays - but only arrays of vectors are allowed in this case
else if (Array.isArray(uniform[0]) && typeof uniform[0][0] === 'number') {
// float vectors (vec2, vec3, vec4)
if (uniform[0].length >= 2 && uniform[0].length <= 4) {
// Set each vector in the array
for (let u = 0; u < uniform.length; u++) {
parsed.push({
type: 'vec' + uniform[0].length,
method: uniform[0].length + 'fv',
name: name + '[' + u + ']',
value: uniform[u],
path: [name, u]
});
}
}
}
// TODO: else warning
}
// Boolean
else if (typeof uniform === 'boolean') {
parsed.push({
type: 'bool',
method: '1i',
name,
value: uniform,
path: [name]
});
}
// Texture
else if (typeof uniform === 'string') {
parsed.push({
type: 'sampler2D',
method: '1i',
name,
value: uniform,
path: [name]
});
}
}
return parsed;
};
/**
Generate a GLSL variable definition from a JS object
*/
GLSL.defineVariable = function (name, value) {
var type, array;
// Single float
if (typeof value === 'number') {
type = 'float';
}
// Multiple floats - vector or array
else if (Array.isArray(value)) {
// Numeric values
if (typeof value[0] === 'number') {
// float vectors (vec2, vec3, vec4)
if (value.length >= 2 && value.length <= 4) {
type = 'vec' + value.length;
}
// float array
else { //if (value.length > 4) {
type = 'float';
array = value.length;
}
// TODO: assume matrix for (typeof == Float32Array && length == 16)?
}
// Array of textures
else if (typeof value[0] === 'string') {
type = 'sampler2D';
array = value.length;
}
// Array of arrays - but only arrays of vectors are allowed in this case
else if (Array.isArray(value[0]) && typeof value[0][0] === 'number') {
// float vectors (vec2, vec3, vec4)
if (value[0].length >= 2 && value[0].length <= 4) {
type = 'vec' + value[0].length;
array = value.length;
}
}
}
// Boolean
else if (typeof value === 'boolean') {
type = 'bool';
}
// Texture
else if (typeof value === 'string') {
type = 'sampler2D';
}
else {
return; // no valid type found
}
// Construct variable definition
var variable = '';
variable += `${type} ${name}`;
if (array) {
variable += `[${array}]`;
}
variable += ';\n';
return variable;
};
/**
Generate a GLSL uniform definition from a JS object
*/
GLSL.defineUniform = function (name, value) {
var def = GLSL.defineVariable(name, value);
if (!def) {
return;
}
return 'uniform ' + def;
};
/**
Expand a single value or 2-element array into a 3-element array, with the last ( z )
coordinate defaulting to 1 (with option to specify). Also runs parseFloat to try to maintain
data integrity. Returns null if input couldn't be parsed.
*/
GLSL.expandVec3 = function (v, z = 1) {
let x;
if (Array.isArray(v)) {
if (v.length === 2) {
x = [...v, z].map(parseFloat);
}
else {
return v;
}
}
else {
x = [v, v, v].map(parseFloat);
}
if (x && x.every(n => typeof n === 'number' && !isNaN(n))) {
return x;
}
};
/**
Expand a single value or 3-element array into a 4-element array, with the last (e.g. w or a)
coordinate defaulting to 1 (with option to specify). Also runs parseFloat to try to maintain
data integrity. Returns null if input couldn't be parsed.
*/
GLSL.expandVec4 = function (v, w = 1) {
let x;
if (Array.isArray(v)) {
if (v.length === 3) {
x = [...v, w].map(parseFloat);
}
else {
return v;
}
}
else {
x = [v, v, v, w].map(parseFloat);
}
if (x && x.every(n => typeof n === 'number' && !isNaN(n))) {
return x;
}
};