WebGLUniforms.js 13 KB

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