import Cartesian2 from "../Core/Cartesian2.js"; import Cartesian3 from "../Core/Cartesian3.js"; import Cartesian4 from "../Core/Cartesian4.js"; import Color from "../Core/Color.js"; import defined from "../Core/defined.js"; import DeveloperError from "../Core/DeveloperError.js"; import Matrix2 from "../Core/Matrix2.js"; import Matrix3 from "../Core/Matrix3.js"; import Matrix4 from "../Core/Matrix4.js"; import RuntimeError from "../Core/RuntimeError.js"; /** * @private * @constructor */ function createUniformArray(gl, activeUniform, uniformName, locations) { switch (activeUniform.type) { case gl.FLOAT: return new UniformArrayFloat(gl, activeUniform, uniformName, locations); case gl.FLOAT_VEC2: return new UniformArrayFloatVec2( gl, activeUniform, uniformName, locations ); case gl.FLOAT_VEC3: return new UniformArrayFloatVec3( gl, activeUniform, uniformName, locations ); case gl.FLOAT_VEC4: return new UniformArrayFloatVec4( gl, activeUniform, uniformName, locations ); case gl.SAMPLER_2D: case gl.SAMPLER_CUBE: return new UniformArraySampler(gl, activeUniform, uniformName, locations); case gl.INT: case gl.BOOL: return new UniformArrayInt(gl, activeUniform, uniformName, locations); case gl.INT_VEC2: case gl.BOOL_VEC2: return new UniformArrayIntVec2(gl, activeUniform, uniformName, locations); case gl.INT_VEC3: case gl.BOOL_VEC3: return new UniformArrayIntVec3(gl, activeUniform, uniformName, locations); case gl.INT_VEC4: case gl.BOOL_VEC4: return new UniformArrayIntVec4(gl, activeUniform, uniformName, locations); case gl.FLOAT_MAT2: return new UniformArrayMat2(gl, activeUniform, uniformName, locations); case gl.FLOAT_MAT3: return new UniformArrayMat3(gl, activeUniform, uniformName, locations); case gl.FLOAT_MAT4: return new UniformArrayMat4(gl, activeUniform, uniformName, locations); default: throw new RuntimeError( `Unrecognized uniform type: ${activeUniform.type} for uniform "${uniformName}".` ); } } /** * @private * @constructor */ function UniformArrayFloat(gl, activeUniform, uniformName, locations) { const length = locations.length; /** * @type {string} * @readonly */ this.name = uniformName; this.value = new Array(length); this._value = new Float32Array(length); this._gl = gl; this._location = locations[0]; } UniformArrayFloat.prototype.set = function () { const value = this.value; const length = value.length; const arraybuffer = this._value; let changed = false; for (let i = 0; i < length; ++i) { const v = value[i]; if (v !== arraybuffer[i]) { arraybuffer[i] = v; changed = true; } } if (changed) { this._gl.uniform1fv(this._location, arraybuffer); } }; /////////////////////////////////////////////////////////////////////////// /** * @private * @constructor */ function UniformArrayFloatVec2(gl, activeUniform, uniformName, locations) { const length = locations.length; /** * @type {string} * @readonly */ this.name = uniformName; this.value = new Array(length); this._value = new Float32Array(length * 2); this._gl = gl; this._location = locations[0]; } UniformArrayFloatVec2.prototype.set = function () { const value = this.value; const length = value.length; const arraybuffer = this._value; let changed = false; let j = 0; for (let i = 0; i < length; ++i) { const v = value[i]; if (!Cartesian2.equalsArray(v, arraybuffer, j)) { Cartesian2.pack(v, arraybuffer, j); changed = true; } j += 2; } if (changed) { this._gl.uniform2fv(this._location, arraybuffer); } }; /////////////////////////////////////////////////////////////////////////// /** * @private * @constructor */ function UniformArrayFloatVec3(gl, activeUniform, uniformName, locations) { const length = locations.length; /** * @type {string} * @readonly */ this.name = uniformName; this.value = new Array(length); this._value = new Float32Array(length * 3); this._gl = gl; this._location = locations[0]; } UniformArrayFloatVec3.prototype.set = function () { const value = this.value; const length = value.length; const arraybuffer = this._value; let changed = false; let j = 0; for (let i = 0; i < length; ++i) { const v = value[i]; if (defined(v.red)) { if ( v.red !== arraybuffer[j] || v.green !== arraybuffer[j + 1] || v.blue !== arraybuffer[j + 2] ) { arraybuffer[j] = v.red; arraybuffer[j + 1] = v.green; arraybuffer[j + 2] = v.blue; changed = true; } } else if (defined(v.x)) { if (!Cartesian3.equalsArray(v, arraybuffer, j)) { Cartesian3.pack(v, arraybuffer, j); changed = true; } } else { //>>includeStart('debug', pragmas.debug); throw new DeveloperError("Invalid vec3 value."); //>>includeEnd('debug'); } j += 3; } if (changed) { this._gl.uniform3fv(this._location, arraybuffer); } }; /////////////////////////////////////////////////////////////////////////// /** * @private * @constructor */ function UniformArrayFloatVec4(gl, activeUniform, uniformName, locations) { const length = locations.length; /** * @type {string} * @readonly */ this.name = uniformName; this.value = new Array(length); this._value = new Float32Array(length * 4); this._gl = gl; this._location = locations[0]; } UniformArrayFloatVec4.prototype.set = function () { // PERFORMANCE_IDEA: if it is a common case that only a few elements // in a uniform array change, we could use heuristics to determine // when it is better to call uniform4f for each element that changed // vs. call uniform4fv once to set the entire array. This applies // to all uniform array types, not just vec4. We might not care // once we have uniform buffers since that will be the fast path. // PERFORMANCE_IDEA: Micro-optimization (I bet it works though): // As soon as changed is true, break into a separate loop that // does the copy without the equals check. const value = this.value; const length = value.length; const arraybuffer = this._value; let changed = false; let j = 0; for (let i = 0; i < length; ++i) { const v = value[i]; if (defined(v.red)) { if (!Color.equalsArray(v, arraybuffer, j)) { Color.pack(v, arraybuffer, j); changed = true; } } else if (defined(v.x)) { if (!Cartesian4.equalsArray(v, arraybuffer, j)) { Cartesian4.pack(v, arraybuffer, j); changed = true; } } else { //>>includeStart('debug', pragmas.debug); throw new DeveloperError("Invalid vec4 value."); //>>includeEnd('debug'); } j += 4; } if (changed) { this._gl.uniform4fv(this._location, arraybuffer); } }; /////////////////////////////////////////////////////////////////////////// /** * @private * @constructor */ function UniformArraySampler(gl, activeUniform, uniformName, locations) { const length = locations.length; /** * @type {string} * @readonly */ this.name = uniformName; this.value = new Array(length); this._value = new Float32Array(length); this._gl = gl; this._locations = locations; this.textureUnitIndex = undefined; } UniformArraySampler.prototype.set = function () { const gl = this._gl; const textureUnitIndex = gl.TEXTURE0 + this.textureUnitIndex; const value = this.value; const length = value.length; for (let i = 0; i < length; ++i) { const v = value[i]; gl.activeTexture(textureUnitIndex + i); gl.bindTexture(v._target, v._texture); } }; UniformArraySampler.prototype._setSampler = function (textureUnitIndex) { this.textureUnitIndex = textureUnitIndex; const locations = this._locations; const length = locations.length; for (let i = 0; i < length; ++i) { const index = textureUnitIndex + i; this._gl.uniform1i(locations[i], index); } return textureUnitIndex + length; }; /////////////////////////////////////////////////////////////////////////// /** * @private * @constructor */ function UniformArrayInt(gl, activeUniform, uniformName, locations) { const length = locations.length; /** * @type {string} * @readonly */ this.name = uniformName; this.value = new Array(length); this._value = new Int32Array(length); this._gl = gl; this._location = locations[0]; } UniformArrayInt.prototype.set = function () { const value = this.value; const length = value.length; const arraybuffer = this._value; let changed = false; for (let i = 0; i < length; ++i) { const v = value[i]; if (v !== arraybuffer[i]) { arraybuffer[i] = v; changed = true; } } if (changed) { this._gl.uniform1iv(this._location, arraybuffer); } }; /////////////////////////////////////////////////////////////////////////// /** * @private * @constructor */ function UniformArrayIntVec2(gl, activeUniform, uniformName, locations) { const length = locations.length; /** * @type {string} * @readonly */ this.name = uniformName; this.value = new Array(length); this._value = new Int32Array(length * 2); this._gl = gl; this._location = locations[0]; } UniformArrayIntVec2.prototype.set = function () { const value = this.value; const length = value.length; const arraybuffer = this._value; let changed = false; let j = 0; for (let i = 0; i < length; ++i) { const v = value[i]; if (!Cartesian2.equalsArray(v, arraybuffer, j)) { Cartesian2.pack(v, arraybuffer, j); changed = true; } j += 2; } if (changed) { this._gl.uniform2iv(this._location, arraybuffer); } }; /////////////////////////////////////////////////////////////////////////// /** * @private * @constructor */ function UniformArrayIntVec3(gl, activeUniform, uniformName, locations) { const length = locations.length; /** * @type {string} * @readonly */ this.name = uniformName; this.value = new Array(length); this._value = new Int32Array(length * 3); this._gl = gl; this._location = locations[0]; } UniformArrayIntVec3.prototype.set = function () { const value = this.value; const length = value.length; const arraybuffer = this._value; let changed = false; let j = 0; for (let i = 0; i < length; ++i) { const v = value[i]; if (!Cartesian3.equalsArray(v, arraybuffer, j)) { Cartesian3.pack(v, arraybuffer, j); changed = true; } j += 3; } if (changed) { this._gl.uniform3iv(this._location, arraybuffer); } }; /////////////////////////////////////////////////////////////////////////// /** * @private * @constructor */ function UniformArrayIntVec4(gl, activeUniform, uniformName, locations) { const length = locations.length; /** * @type {string} * @readonly */ this.name = uniformName; this.value = new Array(length); this._value = new Int32Array(length * 4); this._gl = gl; this._location = locations[0]; } UniformArrayIntVec4.prototype.set = function () { const value = this.value; const length = value.length; const arraybuffer = this._value; let changed = false; let j = 0; for (let i = 0; i < length; ++i) { const v = value[i]; if (!Cartesian4.equalsArray(v, arraybuffer, j)) { Cartesian4.pack(v, arraybuffer, j); changed = true; } j += 4; } if (changed) { this._gl.uniform4iv(this._location, arraybuffer); } }; /////////////////////////////////////////////////////////////////////////// /** * @private * @constructor */ function UniformArrayMat2(gl, activeUniform, uniformName, locations) { const length = locations.length; /** * @type {string} * @readonly */ this.name = uniformName; this.value = new Array(length); this._value = new Float32Array(length * 4); this._gl = gl; this._location = locations[0]; } UniformArrayMat2.prototype.set = function () { const value = this.value; const length = value.length; const arraybuffer = this._value; let changed = false; let j = 0; for (let i = 0; i < length; ++i) { const v = value[i]; if (!Matrix2.equalsArray(v, arraybuffer, j)) { Matrix2.pack(v, arraybuffer, j); changed = true; } j += 4; } if (changed) { this._gl.uniformMatrix2fv(this._location, false, arraybuffer); } }; /////////////////////////////////////////////////////////////////////////// /** * @private * @constructor */ function UniformArrayMat3(gl, activeUniform, uniformName, locations) { const length = locations.length; /** * @type {string} * @readonly */ this.name = uniformName; this.value = new Array(length); this._value = new Float32Array(length * 9); this._gl = gl; this._location = locations[0]; } UniformArrayMat3.prototype.set = function () { const value = this.value; const length = value.length; const arraybuffer = this._value; let changed = false; let j = 0; for (let i = 0; i < length; ++i) { const v = value[i]; if (!Matrix3.equalsArray(v, arraybuffer, j)) { Matrix3.pack(v, arraybuffer, j); changed = true; } j += 9; } if (changed) { this._gl.uniformMatrix3fv(this._location, false, arraybuffer); } }; /////////////////////////////////////////////////////////////////////////// /** * @private * @constructor */ function UniformArrayMat4(gl, activeUniform, uniformName, locations) { const length = locations.length; /** * @type {string} * @readonly */ this.name = uniformName; this.value = new Array(length); this._value = new Float32Array(length * 16); this._gl = gl; this._location = locations[0]; } UniformArrayMat4.prototype.set = function () { const value = this.value; const length = value.length; const arraybuffer = this._value; let changed = false; let j = 0; for (let i = 0; i < length; ++i) { const v = value[i]; if (!Matrix4.equalsArray(v, arraybuffer, j)) { Matrix4.pack(v, arraybuffer, j); changed = true; } j += 16; } if (changed) { this._gl.uniformMatrix4fv(this._location, false, arraybuffer); } }; export default createUniformArray;