WebGLUniforms.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848
  1. /**
  2. * @author tschw
  3. * @author Mugen87 / https://github.com/Mugen87
  4. * @author mrdoob / http://mrdoob.com/
  5. *
  6. * Uniforms of a program.
  7. * Those form a tree structure with a special top-level container for the root,
  8. * which you get by calling 'new WebGLUniforms( gl, program )'.
  9. *
  10. *
  11. * Properties of inner nodes including the top-level container:
  12. *
  13. * .seq - array of nested uniforms
  14. * .map - nested uniforms by name
  15. *
  16. *
  17. * Methods of all nodes except the top-level container:
  18. *
  19. * .setValue( gl, value, [textures] )
  20. *
  21. * uploads a uniform value(s)
  22. * the 'textures' parameter is needed for sampler uniforms
  23. *
  24. *
  25. * Static methods of the top-level container (textures factorizations):
  26. *
  27. * .upload( gl, seq, values, textures )
  28. *
  29. * sets uniforms in 'seq' to 'values[id].value'
  30. *
  31. * .seqWithValue( seq, values ) : filteredSeq
  32. *
  33. * filters 'seq' entries with corresponding entry in values
  34. *
  35. *
  36. * Methods of the top-level container (textures factorizations):
  37. *
  38. * .setValue( gl, name, value, textures )
  39. *
  40. * sets uniform with name 'name' to 'value'
  41. *
  42. * .setOptional( gl, obj, prop )
  43. *
  44. * like .set for an optional property of the object
  45. *
  46. */
  47. import { CubeTexture } from '../../textures/CubeTexture.js';
  48. import { Texture } from '../../textures/Texture.js';
  49. import { DataTexture2DArray } from '../../textures/DataTexture2DArray.js';
  50. import { DataTexture3D } from '../../textures/DataTexture3D.js';
  51. var emptyTexture = new Texture();
  52. var emptyTexture2dArray = new DataTexture2DArray();
  53. var emptyTexture3d = new DataTexture3D();
  54. var emptyCubeTexture = new CubeTexture();
  55. // --- Utilities ---
  56. // Array Caches (provide typed arrays for temporary by size)
  57. var arrayCacheF32 = [];
  58. var arrayCacheI32 = [];
  59. // Float32Array caches used for uploading Matrix uniforms
  60. var mat4array = new Float32Array( 16 );
  61. var mat3array = new Float32Array( 9 );
  62. var mat2array = new Float32Array( 4 );
  63. // Flattening for arrays of vectors and matrices
  64. function flatten( array, nBlocks, blockSize ) {
  65. var firstElem = array[ 0 ];
  66. if ( firstElem <= 0 || firstElem > 0 ) return array;
  67. // unoptimized: ! isNaN( firstElem )
  68. // see http://jacksondunstan.com/articles/983
  69. var n = nBlocks * blockSize,
  70. r = arrayCacheF32[ n ];
  71. if ( r === undefined ) {
  72. r = new Float32Array( n );
  73. arrayCacheF32[ n ] = r;
  74. }
  75. if ( nBlocks !== 0 ) {
  76. firstElem.toArray( r, 0 );
  77. for ( var i = 1, offset = 0; i !== nBlocks; ++ i ) {
  78. offset += blockSize;
  79. array[ i ].toArray( r, offset );
  80. }
  81. }
  82. return r;
  83. }
  84. function arraysEqual( a, b ) {
  85. if ( a.length !== b.length ) return false;
  86. for ( var i = 0, l = a.length; i < l; i ++ ) {
  87. if ( a[ i ] !== b[ i ] ) return false;
  88. }
  89. return true;
  90. }
  91. function copyArray( a, b ) {
  92. for ( var i = 0, l = b.length; i < l; i ++ ) {
  93. a[ i ] = b[ i ];
  94. }
  95. }
  96. // Texture unit allocation
  97. function allocTexUnits( textures, n ) {
  98. var r = arrayCacheI32[ n ];
  99. if ( r === undefined ) {
  100. r = new Int32Array( n );
  101. arrayCacheI32[ n ] = r;
  102. }
  103. for ( var i = 0; i !== n; ++ i )
  104. r[ i ] = textures.allocateTextureUnit();
  105. return r;
  106. }
  107. // --- Setters ---
  108. // Note: Defining these methods externally, because they come in a bunch
  109. // and this way their names minify.
  110. // Single scalar
  111. function setValueV1f( gl, v ) {
  112. var cache = this.cache;
  113. if ( cache[ 0 ] === v ) return;
  114. gl.uniform1f( this.addr, v );
  115. cache[ 0 ] = v;
  116. }
  117. // Single float vector (from flat array or THREE.VectorN)
  118. function setValueV2f( gl, v ) {
  119. var cache = this.cache;
  120. if ( v.x !== undefined ) {
  121. if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y ) {
  122. gl.uniform2f( this.addr, v.x, v.y );
  123. cache[ 0 ] = v.x;
  124. cache[ 1 ] = v.y;
  125. }
  126. } else {
  127. if ( arraysEqual( cache, v ) ) return;
  128. gl.uniform2fv( this.addr, v );
  129. copyArray( cache, v );
  130. }
  131. }
  132. function setValueV3f( gl, v ) {
  133. var cache = this.cache;
  134. if ( v.x !== undefined ) {
  135. if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z ) {
  136. gl.uniform3f( this.addr, v.x, v.y, v.z );
  137. cache[ 0 ] = v.x;
  138. cache[ 1 ] = v.y;
  139. cache[ 2 ] = v.z;
  140. }
  141. } else if ( v.r !== undefined ) {
  142. if ( cache[ 0 ] !== v.r || cache[ 1 ] !== v.g || cache[ 2 ] !== v.b ) {
  143. gl.uniform3f( this.addr, v.r, v.g, v.b );
  144. cache[ 0 ] = v.r;
  145. cache[ 1 ] = v.g;
  146. cache[ 2 ] = v.b;
  147. }
  148. } else {
  149. if ( arraysEqual( cache, v ) ) return;
  150. gl.uniform3fv( this.addr, v );
  151. copyArray( cache, v );
  152. }
  153. }
  154. function setValueV4f( gl, v ) {
  155. var cache = this.cache;
  156. if ( v.x !== undefined ) {
  157. if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z || cache[ 3 ] !== v.w ) {
  158. gl.uniform4f( this.addr, v.x, v.y, v.z, v.w );
  159. cache[ 0 ] = v.x;
  160. cache[ 1 ] = v.y;
  161. cache[ 2 ] = v.z;
  162. cache[ 3 ] = v.w;
  163. }
  164. } else {
  165. if ( arraysEqual( cache, v ) ) return;
  166. gl.uniform4fv( this.addr, v );
  167. copyArray( cache, v );
  168. }
  169. }
  170. // Single matrix (from flat array or MatrixN)
  171. function setValueM2( gl, v ) {
  172. var cache = this.cache;
  173. var elements = v.elements;
  174. if ( elements === undefined ) {
  175. if ( arraysEqual( cache, v ) ) return;
  176. gl.uniformMatrix2fv( this.addr, false, v );
  177. copyArray( cache, v );
  178. } else {
  179. if ( arraysEqual( cache, elements ) ) return;
  180. mat2array.set( elements );
  181. gl.uniformMatrix2fv( this.addr, false, mat2array );
  182. copyArray( cache, elements );
  183. }
  184. }
  185. function setValueM3( gl, v ) {
  186. var cache = this.cache;
  187. var elements = v.elements;
  188. if ( elements === undefined ) {
  189. if ( arraysEqual( cache, v ) ) return;
  190. gl.uniformMatrix3fv( this.addr, false, v );
  191. copyArray( cache, v );
  192. } else {
  193. if ( arraysEqual( cache, elements ) ) return;
  194. mat3array.set( elements );
  195. gl.uniformMatrix3fv( this.addr, false, mat3array );
  196. copyArray( cache, elements );
  197. }
  198. }
  199. function setValueM4( gl, v ) {
  200. var cache = this.cache;
  201. var elements = v.elements;
  202. if ( elements === undefined ) {
  203. if ( arraysEqual( cache, v ) ) return;
  204. gl.uniformMatrix4fv( this.addr, false, v );
  205. copyArray( cache, v );
  206. } else {
  207. if ( arraysEqual( cache, elements ) ) return;
  208. mat4array.set( elements );
  209. gl.uniformMatrix4fv( this.addr, false, mat4array );
  210. copyArray( cache, elements );
  211. }
  212. }
  213. // Single texture (2D / Cube)
  214. function setValueT1( gl, v, textures ) {
  215. var cache = this.cache;
  216. var unit = textures.allocateTextureUnit();
  217. if ( cache[ 0 ] !== unit ) {
  218. gl.uniform1i( this.addr, unit );
  219. cache[ 0 ] = unit;
  220. }
  221. textures.safeSetTexture2D( v || emptyTexture, unit );
  222. }
  223. function setValueT2DArray1( gl, v, textures ) {
  224. var cache = this.cache;
  225. var unit = textures.allocateTextureUnit();
  226. if ( cache[ 0 ] !== unit ) {
  227. gl.uniform1i( this.addr, unit );
  228. cache[ 0 ] = unit;
  229. }
  230. textures.setTexture2DArray( v || emptyTexture2dArray, unit );
  231. }
  232. function setValueT3D1( gl, v, textures ) {
  233. var cache = this.cache;
  234. var unit = textures.allocateTextureUnit();
  235. if ( cache[ 0 ] !== unit ) {
  236. gl.uniform1i( this.addr, unit );
  237. cache[ 0 ] = unit;
  238. }
  239. textures.setTexture3D( v || emptyTexture3d, unit );
  240. }
  241. function setValueT6( gl, v, textures ) {
  242. var cache = this.cache;
  243. var unit = textures.allocateTextureUnit();
  244. if ( cache[ 0 ] !== unit ) {
  245. gl.uniform1i( this.addr, unit );
  246. cache[ 0 ] = unit;
  247. }
  248. textures.safeSetTextureCube( v || emptyCubeTexture, unit );
  249. }
  250. // Integer / Boolean vectors or arrays thereof (always flat arrays)
  251. function setValueV1i( gl, v ) {
  252. var cache = this.cache;
  253. if ( arraysEqual( cache, v ) ) return;
  254. gl.uniform1iv( this.addr, v );
  255. copyArray( cache, v );
  256. }
  257. function setValueV2i( gl, v ) {
  258. var cache = this.cache;
  259. if ( arraysEqual( cache, v ) ) return;
  260. gl.uniform2iv( this.addr, v );
  261. copyArray( cache, v );
  262. }
  263. function setValueV3i( gl, v ) {
  264. var cache = this.cache;
  265. if ( arraysEqual( cache, v ) ) return;
  266. gl.uniform3iv( this.addr, v );
  267. copyArray( cache, v );
  268. }
  269. function setValueV4i( gl, v ) {
  270. var cache = this.cache;
  271. if ( arraysEqual( cache, v ) ) return;
  272. gl.uniform4iv( this.addr, v );
  273. copyArray( cache, v );
  274. }
  275. // Helper to pick the right setter for the singular case
  276. function getSingularSetter( type ) {
  277. switch ( type ) {
  278. case 0x1406: return setValueV1f; // FLOAT
  279. case 0x8b50: return setValueV2f; // _VEC2
  280. case 0x8b51: return setValueV3f; // _VEC3
  281. case 0x8b52: return setValueV4f; // _VEC4
  282. case 0x8b5a: return setValueM2; // _MAT2
  283. case 0x8b5b: return setValueM3; // _MAT3
  284. case 0x8b5c: return setValueM4; // _MAT4
  285. case 0x8b5e: case 0x8d66: return setValueT1; // SAMPLER_2D, SAMPLER_EXTERNAL_OES
  286. case 0x8b5f: return setValueT3D1; // SAMPLER_3D
  287. case 0x8b60: return setValueT6; // SAMPLER_CUBE
  288. case 0x8DC1: return setValueT2DArray1; // SAMPLER_2D_ARRAY
  289. case 0x1404: case 0x8b56: return setValueV1i; // INT, BOOL
  290. case 0x8b53: case 0x8b57: return setValueV2i; // _VEC2
  291. case 0x8b54: case 0x8b58: return setValueV3i; // _VEC3
  292. case 0x8b55: case 0x8b59: return setValueV4i; // _VEC4
  293. }
  294. }
  295. // Array of scalars
  296. function setValueV1fa( gl, v ) {
  297. gl.uniform1fv( this.addr, v );
  298. }
  299. // Integer / Boolean vectors or arrays thereof (always flat arrays)
  300. function setValueV1ia( gl, v ) {
  301. gl.uniform1iv( this.addr, v );
  302. }
  303. function setValueV2ia( gl, v ) {
  304. gl.uniform2iv( this.addr, v );
  305. }
  306. function setValueV3ia( gl, v ) {
  307. gl.uniform3iv( this.addr, v );
  308. }
  309. function setValueV4ia( gl, v ) {
  310. gl.uniform4iv( this.addr, v );
  311. }
  312. // Array of vectors (flat or from THREE classes)
  313. function setValueV2fa( gl, v ) {
  314. var data = flatten( v, this.size, 2 );
  315. gl.uniform2fv( this.addr, data );
  316. }
  317. function setValueV3fa( gl, v ) {
  318. var data = flatten( v, this.size, 3 );
  319. gl.uniform3fv( this.addr, data );
  320. }
  321. function setValueV4fa( gl, v ) {
  322. var data = flatten( v, this.size, 4 );
  323. gl.uniform4fv( this.addr, data );
  324. }
  325. // Array of matrices (flat or from THREE clases)
  326. function setValueM2a( gl, v ) {
  327. var data = flatten( v, this.size, 4 );
  328. gl.uniformMatrix2fv( this.addr, false, data );
  329. }
  330. function setValueM3a( gl, v ) {
  331. var data = flatten( v, this.size, 9 );
  332. gl.uniformMatrix3fv( this.addr, false, data );
  333. }
  334. function setValueM4a( gl, v ) {
  335. var data = flatten( v, this.size, 16 );
  336. gl.uniformMatrix4fv( this.addr, false, data );
  337. }
  338. // Array of textures (2D / Cube)
  339. function setValueT1a( gl, v, textures ) {
  340. var n = v.length;
  341. var units = allocTexUnits( textures, n );
  342. gl.uniform1iv( this.addr, units );
  343. for ( var i = 0; i !== n; ++ i ) {
  344. textures.safeSetTexture2D( v[ i ] || emptyTexture, units[ i ] );
  345. }
  346. }
  347. function setValueT6a( gl, v, textures ) {
  348. var n = v.length;
  349. var units = allocTexUnits( textures, n );
  350. gl.uniform1iv( this.addr, units );
  351. for ( var i = 0; i !== n; ++ i ) {
  352. textures.safeSetTextureCube( v[ i ] || emptyCubeTexture, units[ i ] );
  353. }
  354. }
  355. // Helper to pick the right setter for a pure (bottom-level) array
  356. function getPureArraySetter( type ) {
  357. switch ( type ) {
  358. case 0x1406: return setValueV1fa; // FLOAT
  359. case 0x8b50: return setValueV2fa; // _VEC2
  360. case 0x8b51: return setValueV3fa; // _VEC3
  361. case 0x8b52: return setValueV4fa; // _VEC4
  362. case 0x8b5a: return setValueM2a; // _MAT2
  363. case 0x8b5b: return setValueM3a; // _MAT3
  364. case 0x8b5c: return setValueM4a; // _MAT4
  365. case 0x8b5e: return setValueT1a; // SAMPLER_2D
  366. case 0x8b60: return setValueT6a; // SAMPLER_CUBE
  367. case 0x1404: case 0x8b56: return setValueV1ia; // INT, BOOL
  368. case 0x8b53: case 0x8b57: return setValueV2ia; // _VEC2
  369. case 0x8b54: case 0x8b58: return setValueV3ia; // _VEC3
  370. case 0x8b55: case 0x8b59: return setValueV4ia; // _VEC4
  371. }
  372. }
  373. // --- Uniform Classes ---
  374. function SingleUniform( id, activeInfo, addr ) {
  375. this.id = id;
  376. this.addr = addr;
  377. this.cache = [];
  378. this.setValue = getSingularSetter( activeInfo.type );
  379. // this.path = activeInfo.name; // DEBUG
  380. }
  381. function PureArrayUniform( id, activeInfo, addr ) {
  382. this.id = id;
  383. this.addr = addr;
  384. this.cache = [];
  385. this.size = activeInfo.size;
  386. this.setValue = getPureArraySetter( activeInfo.type );
  387. // this.path = activeInfo.name; // DEBUG
  388. }
  389. PureArrayUniform.prototype.updateCache = function ( data ) {
  390. var cache = this.cache;
  391. if ( data instanceof Float32Array && cache.length !== data.length ) {
  392. this.cache = new Float32Array( data.length );
  393. }
  394. copyArray( cache, data );
  395. };
  396. function StructuredUniform( id ) {
  397. this.id = id;
  398. this.seq = [];
  399. this.map = {};
  400. }
  401. StructuredUniform.prototype.setValue = function ( gl, value, textures ) {
  402. var seq = this.seq;
  403. for ( var i = 0, n = seq.length; i !== n; ++ i ) {
  404. var u = seq[ i ];
  405. u.setValue( gl, value[ u.id ], textures );
  406. }
  407. };
  408. // --- Top-level ---
  409. // Parser - builds up the property tree from the path strings
  410. var RePathPart = /([\w\d_]+)(\])?(\[|\.)?/g;
  411. // extracts
  412. // - the identifier (member name or array index)
  413. // - followed by an optional right bracket (found when array index)
  414. // - followed by an optional left bracket or dot (type of subscript)
  415. //
  416. // Note: These portions can be read in a non-overlapping fashion and
  417. // allow straightforward parsing of the hierarchy that WebGL encodes
  418. // in the uniform names.
  419. function addUniform( container, uniformObject ) {
  420. container.seq.push( uniformObject );
  421. container.map[ uniformObject.id ] = uniformObject;
  422. }
  423. function parseUniform( activeInfo, addr, container ) {
  424. var path = activeInfo.name,
  425. pathLength = path.length;
  426. // reset RegExp object, because of the early exit of a previous run
  427. RePathPart.lastIndex = 0;
  428. while ( true ) {
  429. var match = RePathPart.exec( path ),
  430. matchEnd = RePathPart.lastIndex,
  431. id = match[ 1 ],
  432. idIsIndex = match[ 2 ] === ']',
  433. subscript = match[ 3 ];
  434. if ( idIsIndex ) id = id | 0; // convert to integer
  435. if ( subscript === undefined || subscript === '[' && matchEnd + 2 === pathLength ) {
  436. // bare name or "pure" bottom-level array "[0]" suffix
  437. addUniform( container, subscript === undefined ?
  438. new SingleUniform( id, activeInfo, addr ) :
  439. new PureArrayUniform( id, activeInfo, addr ) );
  440. break;
  441. } else {
  442. // step into inner node / create it in case it doesn't exist
  443. var map = container.map, next = map[ id ];
  444. if ( next === undefined ) {
  445. next = new StructuredUniform( id );
  446. addUniform( container, next );
  447. }
  448. container = next;
  449. }
  450. }
  451. }
  452. // Root Container
  453. function WebGLUniforms( gl, program ) {
  454. this.seq = [];
  455. this.map = {};
  456. var n = gl.getProgramParameter( program, gl.ACTIVE_UNIFORMS );
  457. for ( var i = 0; i < n; ++ i ) {
  458. var info = gl.getActiveUniform( program, i ),
  459. addr = gl.getUniformLocation( program, info.name );
  460. parseUniform( info, addr, this );
  461. }
  462. }
  463. WebGLUniforms.prototype.setValue = function ( gl, name, value, textures ) {
  464. var u = this.map[ name ];
  465. if ( u !== undefined ) u.setValue( gl, value, textures );
  466. };
  467. WebGLUniforms.prototype.setOptional = function ( gl, object, name ) {
  468. var v = object[ name ];
  469. if ( v !== undefined ) this.setValue( gl, name, v );
  470. };
  471. // Static interface
  472. WebGLUniforms.upload = function ( gl, seq, values, textures ) {
  473. for ( var i = 0, n = seq.length; i !== n; ++ i ) {
  474. var u = seq[ i ],
  475. v = values[ u.id ];
  476. if ( v.needsUpdate !== false ) {
  477. // note: always updating when .needsUpdate is undefined
  478. u.setValue( gl, v.value, textures );
  479. }
  480. }
  481. };
  482. WebGLUniforms.seqWithValue = function ( seq, values ) {
  483. var r = [];
  484. for ( var i = 0, n = seq.length; i !== n; ++ i ) {
  485. var u = seq[ i ];
  486. if ( u.id in values ) r.push( u );
  487. }
  488. return r;
  489. };
  490. export { WebGLUniforms };