123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882 |
- /**
- * @author tschw
- * @author Mugen87 / https://github.com/Mugen87
- * @author mrdoob / http://mrdoob.com/
- *
- * 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
- *
- */
- import { CubeTexture } from '../../textures/CubeTexture.js';
- import { Texture } from '../../textures/Texture.js';
- import { DataTexture2DArray } from '../../textures/DataTexture2DArray.js';
- import { DataTexture3D } from '../../textures/DataTexture3D.js';
- var emptyTexture = new Texture();
- var emptyTexture2dArray = new DataTexture2DArray();
- var emptyTexture3d = new DataTexture3D();
- var emptyCubeTexture = new CubeTexture();
- // --- Utilities ---
- // Array Caches (provide typed arrays for temporary by size)
- var arrayCacheF32 = [];
- var arrayCacheI32 = [];
- // Float32Array caches used for uploading Matrix uniforms
- var mat4array = new Float32Array( 16 );
- var mat3array = new Float32Array( 9 );
- var mat2array = new Float32Array( 4 );
- // Flattening for arrays of vectors and matrices
- function flatten( array, nBlocks, blockSize ) {
- var firstElem = array[ 0 ];
- if ( firstElem <= 0 || firstElem > 0 ) return array;
- // unoptimized: ! isNaN( firstElem )
- // see http://jacksondunstan.com/articles/983
- var n = nBlocks * blockSize,
- r = arrayCacheF32[ n ];
- if ( r === undefined ) {
- r = new Float32Array( n );
- arrayCacheF32[ n ] = r;
- }
- if ( nBlocks !== 0 ) {
- firstElem.toArray( r, 0 );
- for ( var 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 ( var i = 0, l = a.length; i < l; i ++ ) {
- if ( a[ i ] !== b[ i ] ) return false;
- }
- return true;
- }
- function copyArray( a, b ) {
- for ( var i = 0, l = b.length; i < l; i ++ ) {
- a[ i ] = b[ i ];
- }
- }
- // Texture unit allocation
- function allocTexUnits( textures, n ) {
- var r = arrayCacheI32[ n ];
- if ( r === undefined ) {
- r = new Int32Array( n );
- arrayCacheI32[ n ] = r;
- }
- for ( var 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 setValue1f( gl, v ) {
- var cache = this.cache;
- if ( cache[ 0 ] === v ) return;
- gl.uniform1f( this.addr, v );
- cache[ 0 ] = v;
- }
- function setValue1i( gl, v ) {
- var cache = this.cache;
- if ( cache[ 0 ] === v ) return;
- gl.uniform1i( this.addr, v );
- cache[ 0 ] = v;
- }
- // Single float vector (from flat array or THREE.VectorN)
- function setValue2fv( gl, v ) {
- var 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 setValue3fv( gl, v ) {
- var 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 setValue4fv( gl, v ) {
- var 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 MatrixN)
- function setValue2fm( gl, v ) {
- var cache = this.cache;
- var 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 setValue3fm( gl, v ) {
- var cache = this.cache;
- var 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 setValue4fm( gl, v ) {
- var cache = this.cache;
- var 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 texture (2D / Cube)
- function setValueT1( gl, v, textures ) {
- var cache = this.cache;
- var unit = textures.allocateTextureUnit();
- if ( cache[ 0 ] !== unit ) {
- gl.uniform1i( this.addr, unit );
- cache[ 0 ] = unit;
- }
- textures.safeSetTexture2D( v || emptyTexture, unit );
- }
- function setValueT2DArray1( gl, v, textures ) {
- var cache = this.cache;
- var unit = textures.allocateTextureUnit();
- if ( cache[ 0 ] !== unit ) {
- gl.uniform1i( this.addr, unit );
- cache[ 0 ] = unit;
- }
- textures.setTexture2DArray( v || emptyTexture2dArray, unit );
- }
- function setValueT3D1( gl, v, textures ) {
- var cache = this.cache;
- var unit = textures.allocateTextureUnit();
- if ( cache[ 0 ] !== unit ) {
- gl.uniform1i( this.addr, unit );
- cache[ 0 ] = unit;
- }
- textures.setTexture3D( v || emptyTexture3d, unit );
- }
- function setValueT6( gl, v, textures ) {
- var cache = this.cache;
- var unit = textures.allocateTextureUnit();
- if ( cache[ 0 ] !== unit ) {
- gl.uniform1i( this.addr, unit );
- cache[ 0 ] = unit;
- }
- textures.safeSetTextureCube( v || emptyCubeTexture, unit );
- }
- // Integer / Boolean vectors or arrays thereof (always flat arrays)
- function setValue2iv( gl, v ) {
- var cache = this.cache;
- if ( arraysEqual( cache, v ) ) return;
- gl.uniform2iv( this.addr, v );
- copyArray( cache, v );
- }
- function setValue3iv( gl, v ) {
- var cache = this.cache;
- if ( arraysEqual( cache, v ) ) return;
- gl.uniform3iv( this.addr, v );
- copyArray( cache, v );
- }
- function setValue4iv( gl, v ) {
- var cache = this.cache;
- if ( arraysEqual( cache, v ) ) return;
- gl.uniform4iv( this.addr, v );
- copyArray( cache, v );
- }
- // Helper to pick the right setter for the singular case
- function getSingularSetter( type ) {
- switch ( type ) {
- case 0x1406: return setValue1f; // FLOAT
- case 0x8b50: return setValue2fv; // _VEC2
- case 0x8b51: return setValue3fv; // _VEC3
- case 0x8b52: return setValue4fv; // _VEC4
- case 0x8b5a: return setValue2fm; // _MAT2
- case 0x8b5b: return setValue3fm; // _MAT3
- case 0x8b5c: return setValue4fm; // _MAT4
- case 0x8b5e: case 0x8d66: return setValueT1; // SAMPLER_2D, SAMPLER_EXTERNAL_OES
- case 0x8b5f: return setValueT3D1; // SAMPLER_3D
- case 0x8b60: return setValueT6; // SAMPLER_CUBE
- case 0x8DC1: return setValueT2DArray1; // SAMPLER_2D_ARRAY
- case 0x1404: case 0x8b56: return setValue1i; // INT, BOOL
- case 0x8b53: case 0x8b57: return setValue2iv; // _VEC2
- case 0x8b54: case 0x8b58: return setValue3iv; // _VEC3
- case 0x8b55: case 0x8b59: return setValue4iv; // _VEC4
- }
- }
- // Array of scalars
- function setValue1fv( gl, v ) {
- var cache = this.cache;
- if ( arraysEqual( cache, v ) ) return;
- gl.uniform1fv( this.addr, v );
- copyArray( cache, v );
- }
- function setValue1iv( gl, v ) {
- var cache = this.cache;
- if ( arraysEqual( cache, v ) ) return;
- gl.uniform1iv( this.addr, v );
- copyArray( cache, v );
- }
- // Array of vectors (flat or from THREE classes)
- function setValueV2a( gl, v ) {
- var cache = this.cache;
- var data = flatten( v, this.size, 2 );
- if ( arraysEqual( cache, data ) ) return;
- gl.uniform2fv( this.addr, data );
- this.updateCache( data );
- }
- function setValueV3a( gl, v ) {
- var cache = this.cache;
- var data = flatten( v, this.size, 3 );
- if ( arraysEqual( cache, data ) ) return;
- gl.uniform3fv( this.addr, data );
- this.updateCache( data );
- }
- function setValueV4a( gl, v ) {
- var cache = this.cache;
- var data = flatten( v, this.size, 4 );
- if ( arraysEqual( cache, data ) ) return;
- gl.uniform4fv( this.addr, data );
- this.updateCache( data );
- }
- // Array of matrices (flat or from THREE clases)
- function setValueM2a( gl, v ) {
- var cache = this.cache;
- var data = flatten( v, this.size, 4 );
- if ( arraysEqual( cache, data ) ) return;
- gl.uniformMatrix2fv( this.addr, false, data );
- this.updateCache( data );
- }
- function setValueM3a( gl, v ) {
- var cache = this.cache;
- var data = flatten( v, this.size, 9 );
- if ( arraysEqual( cache, data ) ) return;
- gl.uniformMatrix3fv( this.addr, false, data );
- this.updateCache( data );
- }
- function setValueM4a( gl, v ) {
- var cache = this.cache;
- var data = flatten( v, this.size, 16 );
- if ( arraysEqual( cache, data ) ) return;
- gl.uniformMatrix4fv( this.addr, false, data );
- this.updateCache( data );
- }
- // Array of textures (2D / Cube)
- function setValueT1a( gl, v, textures ) {
- var cache = this.cache;
- var n = v.length;
- var units = allocTexUnits( textures, n );
- if ( arraysEqual( cache, units ) === false ) {
- gl.uniform1iv( this.addr, units );
- copyArray( cache, units );
- }
- for ( var i = 0; i !== n; ++ i ) {
- textures.safeSetTexture2D( v[ i ] || emptyTexture, units[ i ] );
- }
- }
- function setValueT6a( gl, v, textures ) {
- var cache = this.cache;
- var n = v.length;
- var units = allocTexUnits( textures, n );
- if ( arraysEqual( cache, units ) === false ) {
- gl.uniform1iv( this.addr, units );
- copyArray( cache, units );
- }
- for ( var i = 0; i !== n; ++ i ) {
- textures.safeSetTextureCube( v[ i ] || emptyCubeTexture, units[ i ] );
- }
- }
- // Helper to pick the right setter for a pure (bottom-level) array
- function getPureArraySetter( type ) {
- switch ( type ) {
- case 0x1406: return setValue1fv; // FLOAT
- case 0x8b50: return setValueV2a; // _VEC2
- case 0x8b51: return setValueV3a; // _VEC3
- case 0x8b52: return setValueV4a; // _VEC4
- case 0x8b5a: return setValueM2a; // _MAT2
- case 0x8b5b: return setValueM3a; // _MAT3
- case 0x8b5c: return setValueM4a; // _MAT4
- case 0x8b5e: return setValueT1a; // SAMPLER_2D
- case 0x8b60: return setValueT6a; // SAMPLER_CUBE
- case 0x1404: case 0x8b56: return setValue1iv; // INT, BOOL
- case 0x8b53: case 0x8b57: return setValue2iv; // _VEC2
- case 0x8b54: case 0x8b58: return setValue3iv; // _VEC3
- case 0x8b55: case 0x8b59: return setValue4iv; // _VEC4
- }
- }
- // --- Uniform Classes ---
- function SingleUniform( id, activeInfo, addr ) {
- this.id = id;
- this.addr = addr;
- this.cache = [];
- this.setValue = getSingularSetter( activeInfo.type );
- // this.path = activeInfo.name; // DEBUG
- }
- function PureArrayUniform( id, activeInfo, addr ) {
- this.id = id;
- this.addr = addr;
- this.cache = [];
- this.size = activeInfo.size;
- this.setValue = getPureArraySetter( activeInfo.type );
- // this.path = activeInfo.name; // DEBUG
- }
- PureArrayUniform.prototype.updateCache = function ( data ) {
- var cache = this.cache;
- if ( data instanceof Float32Array && cache.length !== data.length ) {
- this.cache = new Float32Array( data.length );
- }
- copyArray( cache, data );
- };
- function StructuredUniform( id ) {
- this.id = id;
- this.seq = [];
- this.map = {};
- }
- StructuredUniform.prototype.setValue = function ( gl, value, textures ) {
- var seq = this.seq;
- for ( var i = 0, n = seq.length; i !== n; ++ i ) {
- var u = seq[ i ];
- u.setValue( gl, value[ u.id ], textures );
- }
- };
- // --- Top-level ---
- // Parser - builds up the property tree from the path strings
- var RePathPart = /([\w\d_]+)(\])?(\[|\.)?/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 ) {
- var path = activeInfo.name,
- pathLength = path.length;
- // reset RegExp object, because of the early exit of a previous run
- RePathPart.lastIndex = 0;
- while ( true ) {
- var match = RePathPart.exec( path ),
- matchEnd = RePathPart.lastIndex,
- id = match[ 1 ],
- 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
- var map = container.map, next = map[ id ];
- if ( next === undefined ) {
- next = new StructuredUniform( id );
- addUniform( container, next );
- }
- container = next;
- }
- }
- }
- // Root Container
- function WebGLUniforms( gl, program ) {
- this.seq = [];
- this.map = {};
- var n = gl.getProgramParameter( program, gl.ACTIVE_UNIFORMS );
- for ( var i = 0; i < n; ++ i ) {
- var info = gl.getActiveUniform( program, i ),
- addr = gl.getUniformLocation( program, info.name );
- parseUniform( info, addr, this );
- }
- }
- WebGLUniforms.prototype.setValue = function ( gl, name, value, textures ) {
- var u = this.map[ name ];
- if ( u !== undefined ) u.setValue( gl, value, textures );
- };
- WebGLUniforms.prototype.setOptional = function ( gl, object, name ) {
- var v = object[ name ];
- if ( v !== undefined ) this.setValue( gl, name, v );
- };
- // Static interface
- WebGLUniforms.upload = function ( gl, seq, values, textures ) {
- for ( var i = 0, n = seq.length; i !== n; ++ i ) {
- var u = seq[ i ],
- v = values[ u.id ];
- if ( v.needsUpdate !== false ) {
- // note: always updating when .needsUpdate is undefined
- u.setValue( gl, v.value, textures );
- }
- }
- };
- WebGLUniforms.seqWithValue = function ( seq, values ) {
- var r = [];
- for ( var i = 0, n = seq.length; i !== n; ++ i ) {
- var u = seq[ i ];
- if ( u.id in values ) r.push( u );
- }
- return r;
- };
- export { WebGLUniforms };
|