/* @module gl-util/unifrom */ 'use strict' const isPlainObject = require('is-plain-obj') const isInt = require('number-is-integer') const extend = require('object-assign') const getProgram = require('./program') const WeakMap = require('weak-map') let uniformsCache = setUniform.cache = new WeakMap(); module.exports = setUniform; function setUniform (gl, name, options) { if (!gl) throw Error('WebGL context is not provided'); let program if (gl instanceof WebGLProgram) { program = gl gl = gl.gl } else { program = getProgram(gl) } if (!program) throw Error('Context has no active program'); // object with uniforms passed if (name && typeof name != 'string') { let result = {}; let uniforms = name; for (let name in uniforms) { result[name] = setUniform(gl, name, uniforms[name], program); } return result; } let uniforms = uniformsCache.has(program) ? uniformsCache.get(program) : uniformsCache.set(program, {}).get(program); // return all uniforms if no name provided if (!name) return uniforms; let uniform = uniforms[name]; // autoinit uniform(s) if (!uniform) { let count = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS); for (var i=0; i < count; ++i) { let info = gl.getActiveUniform(program, i); if (!info) continue; // ignore textures if (info.type === gl.SAMPLER_2D || info.type === gl.SAMPLER_CUBE) { } else { uniforms[info.name] = {name: info.name, type: info.type, data: null} } } if (!uniforms[name]) uniforms[name] = {name: name, data: null, type: null} uniform = uniforms[name]; } // detect location if (uniform.location == null) { uniform.location = gl.getUniformLocation(program, name); } // if no options passed - just return known uniform info if (options == null) return uniform; if (!isPlainObject(options)) options = {data: options}; extend(uniform, options); // detect type if (uniform.type == null && uniform.data) { if (typeof uniform.data === 'number') { if (isInt(uniform.data)) { uniform.type = gl.INT; } else uniform.type = gl.FLOAT; } else if (uniform.data.length === 16) { uniform.type = gl.FLOAT_MAT4; } else if (uniform.data.length === 9) { uniform.type = gl.FLOAT_MAT3; } else if (uniform.data.length <= 4 ) { if (uniform.data instanceof Float32Array) { uniform.type = gl.FLOAT_VEC2 + (uniform.data.length-2); } else if (ArrayBuffer.isView(uniform.data)) { uniform.type = gl.INT_VEC2 + (uniform.data.length-2); } else if (Array.isArray(uniform.data)) { if (uniform.data.every(isInt)) { uniform.type = gl.INT_VEC2 + (uniform.data.length-2); } else { uniform.type = gl.FLOAT_VEC2 + (uniform.data.length-2); } } } } // make sure data is typed array if (Array.isArray(uniform.data)) { switch (uniform.type) { case gl.INT_VEC2: case gl.INT_VEC3: case gl.INT_VEC4: uniform.data = new Int32Array(uniform.data); break; default: uniform.data = new Float32Array(uniform.data); } } // put data to shader if (uniform.location && uniform.data != null) { switch (uniform.type) { case gl.FLOAT_VEC4: case gl.FLOAT_MAT2: gl.uniform4fv(uniform.location, uniform.data); break; case gl.INT_VEC4: gl.uniform4iv(uniform.location, uniform.data); break; case gl.FLOAT_VEC3: gl.uniform3fv(uniform.location, uniform.data); break; case gl.INT_VEC3: gl.uniform3iv(uniform.location, uniform.data); break; case gl.FLOAT_VEC2: gl.uniform2fv(uniform.location, uniform.data); break; case gl.INT_VEC2: gl.uniform2iv(uniform.location, uniform.data); break; case gl.FLOAT: gl.uniform1f(uniform.location, uniform.data); break; case gl.INT: gl.uniform1i(uniform.location, uniform.data); break; } } return uniform; }