WebGLUniforms.js 11.2 KB
Newer Older
T
tschw 已提交
1
/**
2
 * @author tschw
T
tschw 已提交
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
 *
 * 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, renderer )'.
 *
 *
 * 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, [renderer] )
 *
 * 		uploads a uniform value(s)
 *  	the 'renderer' parameter is needed for sampler uniforms
 *
 *
 * Static methods of the top-level container (renderer factorizations):
 *
 * .upload( gl, seq, values, renderer )
 *
 * 		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 (renderer factorizations):
 *
 * .setValue( gl, name, value )
 *
 * 		sets uniform with  name 'name' to 'value'
 *
 * .set( gl, obj, prop )
 *
 * 		sets uniform from object and property with same name than uniform
 *
 * .setOptional( gl, obj, prop )
 *
 * 		like .set for an optional property of the object
 *
 */

50 51
import { CubeTexture } from '../../textures/CubeTexture';
import { Texture } from '../../textures/Texture';
T
tschw 已提交
52

53 54
var emptyTexture = new Texture();
var emptyCubeTexture = new CubeTexture();
55

56
// --- Base for inner nodes (including the root) ---
T
tschw 已提交
57

58
function UniformContainer() {
T
tschw 已提交
59

60 61
	this.seq = [];
	this.map = {};
T
tschw 已提交
62

63
}
T
tschw 已提交
64

65
// --- Utilities ---
T
tschw 已提交
66

67
// Array Caches (provide typed arrays for temporary by size)
T
tschw 已提交
68

69 70
var arrayCacheF32 = [];
var arrayCacheI32 = [];
T
tschw 已提交
71

72
// Float32Array caches used for uploading Matrix uniforms
73

M
Mr.doob 已提交
74
var mat4array = new Float32Array( 16 );
75
var mat3array = new Float32Array( 9 );
76

77
// Flattening for arrays of vectors and matrices
T
tschw 已提交
78

79
function flatten( array, nBlocks, blockSize ) {
T
tschw 已提交
80

81
	var firstElem = array[ 0 ];
T
tschw 已提交
82

83 84 85
	if ( firstElem <= 0 || firstElem > 0 ) return array;
	// unoptimized: ! isNaN( firstElem )
	// see http://jacksondunstan.com/articles/983
T
tschw 已提交
86

87 88
	var n = nBlocks * blockSize,
		r = arrayCacheF32[ n ];
T
tschw 已提交
89

90
	if ( r === undefined ) {
T
tschw 已提交
91

92 93
		r = new Float32Array( n );
		arrayCacheF32[ n ] = r;
T
tschw 已提交
94

95
	}
T
tschw 已提交
96

97
	if ( nBlocks !== 0 ) {
T
tschw 已提交
98

99
		firstElem.toArray( r, 0 );
T
tschw 已提交
100

101
		for ( var i = 1, offset = 0; i !== nBlocks; ++ i ) {
T
tschw 已提交
102

103 104
			offset += blockSize;
			array[ i ].toArray( r, offset );
T
tschw 已提交
105

106
		}
T
tschw 已提交
107

108
	}
T
tschw 已提交
109

110
	return r;
T
tschw 已提交
111

112
}
T
tschw 已提交
113

114
// Texture unit allocation
T
tschw 已提交
115

116
function allocTexUnits( renderer, n ) {
T
tschw 已提交
117

118
	var r = arrayCacheI32[ n ];
119

120
	if ( r === undefined ) {
121

122 123
		r = new Int32Array( n );
		arrayCacheI32[ n ] = r;
124

125
	}
T
tschw 已提交
126

127 128
	for ( var i = 0; i !== n; ++ i )
		r[ i ] = renderer.allocTextureUnit();
T
tschw 已提交
129

130
	return r;
T
tschw 已提交
131

132
}
T
tschw 已提交
133

134
// --- Setters ---
T
tschw 已提交
135

136 137
// Note: Defining these methods externally, because they come in a bunch
// and this way their names minify.
T
tschw 已提交
138

139
// Single scalar
T
tschw 已提交
140

M
Mr.doob 已提交
141 142
function setValue1f( gl, v ) { gl.uniform1f( this.addr, v ) }
function setValue1i( gl, v ) { gl.uniform1i( this.addr, v ) }
T
tschw 已提交
143

144
// Single float vector (from flat array or THREE.VectorN)
T
tschw 已提交
145

146
function setValue2fv( gl, v ) {
T
tschw 已提交
147

148 149
	if ( v.x === undefined ) gl.uniform2fv( this.addr, v );
	else gl.uniform2f( this.addr, v.x, v.y );
T
tschw 已提交
150

151
}
T
tschw 已提交
152

153
function setValue3fv( gl, v ) {
T
tschw 已提交
154

155 156 157 158 159 160
	if ( v.x !== undefined )
		gl.uniform3f( this.addr, v.x, v.y, v.z );
	else if ( v.r !== undefined )
		gl.uniform3f( this.addr, v.r, v.g, v.b );
	else
		gl.uniform3fv( this.addr, v );
T
tschw 已提交
161

162
}
T
tschw 已提交
163

164
function setValue4fv( gl, v ) {
T
tschw 已提交
165

166 167
	if ( v.x === undefined ) gl.uniform4fv( this.addr, v );
	else gl.uniform4f( this.addr, v.x, v.y, v.z, v.w );
T
tschw 已提交
168

169
}
T
tschw 已提交
170

171
// Single matrix (from flat array or MatrixN)
T
tschw 已提交
172

173
function setValue2fm( gl, v ) {
T
tschw 已提交
174

175
	gl.uniformMatrix2fv( this.addr, false, v.elements || v );
T
tschw 已提交
176

177
}
T
tschw 已提交
178

179
function setValue3fm( gl, v ) {
T
tschw 已提交
180

181 182 183 184 185 186 187 188 189 190
	if ( v.elements === undefined ) {

		gl.uniformMatrix3fv( this.addr, false, v );

	} else {

		mat3array.set( v.elements );
		gl.uniformMatrix3fv( this.addr, false, mat3array );

	}
T
tschw 已提交
191

192
}
T
tschw 已提交
193

194
function setValue4fm( gl, v ) {
T
tschw 已提交
195

196
	if ( v.elements === undefined ) {
M
Mr.doob 已提交
197

198
		gl.uniformMatrix4fv( this.addr, false, v );
M
Mr.doob 已提交
199 200 201 202

	} else {

		mat4array.set( v.elements );
203
		gl.uniformMatrix4fv( this.addr, false, mat4array );
M
Mr.doob 已提交
204

205
	}
M
Mr.doob 已提交
206

207
}
T
tschw 已提交
208

209
// Single texture (2D / Cube)
T
tschw 已提交
210

211
function setValueT1( gl, v, renderer ) {
T
tschw 已提交
212

213 214 215
	var unit = renderer.allocTextureUnit();
	gl.uniform1i( this.addr, unit );
	renderer.setTexture2D( v || emptyTexture, unit );
T
tschw 已提交
216

217
}
T
tschw 已提交
218

219
function setValueT6( gl, v, renderer ) {
T
tschw 已提交
220

221 222 223
	var unit = renderer.allocTextureUnit();
	gl.uniform1i( this.addr, unit );
	renderer.setTextureCube( v || emptyCubeTexture, unit );
T
tschw 已提交
224

225
}
T
tschw 已提交
226

227
// Integer / Boolean vectors or arrays thereof (always flat arrays)
T
tschw 已提交
228

M
Mr.doob 已提交
229 230 231
function setValue2iv( gl, v ) { gl.uniform2iv( this.addr, v ) }
function setValue3iv( gl, v ) { gl.uniform3iv( this.addr, v ) }
function setValue4iv( gl, v ) { gl.uniform4iv( this.addr, v ) }
T
tschw 已提交
232

233
// Helper to pick the right setter for the singular case
T
tschw 已提交
234

235
function getSingularSetter( type ) {
T
tschw 已提交
236

237
	switch ( type ) {
T
tschw 已提交
238

239 240 241 242
		case 0x1406: return setValue1f; // FLOAT
		case 0x8b50: return setValue2fv; // _VEC2
		case 0x8b51: return setValue3fv; // _VEC3
		case 0x8b52: return setValue4fv; // _VEC4
T
tschw 已提交
243

244 245 246
		case 0x8b5a: return setValue2fm; // _MAT2
		case 0x8b5b: return setValue3fm; // _MAT3
		case 0x8b5c: return setValue4fm; // _MAT4
T
tschw 已提交
247

248
		case 0x8b5e: case 0x8d66: return setValueT1; // SAMPLER_2D, SAMPLER_EXTERNAL_OES
249
		case 0x8b60: return setValueT6; // SAMPLER_CUBE
T
tschw 已提交
250

251 252 253 254
		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
T
tschw 已提交
255

256
	}
T
tschw 已提交
257

258
}
T
tschw 已提交
259

260
// Array of scalars
T
tschw 已提交
261

M
Mr.doob 已提交
262 263
function setValue1fv( gl, v ) { gl.uniform1fv( this.addr, v ) }
function setValue1iv( gl, v ) { gl.uniform1iv( this.addr, v ) }
T
tschw 已提交
264

265
// Array of vectors (flat or from THREE classes)
T
tschw 已提交
266

267
function setValueV2a( gl, v ) {
T
tschw 已提交
268

269
	gl.uniform2fv( this.addr, flatten( v, this.size, 2 ) );
T
tschw 已提交
270

271
}
T
tschw 已提交
272

273
function setValueV3a( gl, v ) {
T
tschw 已提交
274

275
	gl.uniform3fv( this.addr, flatten( v, this.size, 3 ) );
T
tschw 已提交
276

277
}
T
tschw 已提交
278

279
function setValueV4a( gl, v ) {
T
tschw 已提交
280

281
	gl.uniform4fv( this.addr, flatten( v, this.size, 4 ) );
T
tschw 已提交
282

283
}
T
tschw 已提交
284

285
// Array of matrices (flat or from THREE clases)
T
tschw 已提交
286

287
function setValueM2a( gl, v ) {
T
tschw 已提交
288

289
	gl.uniformMatrix2fv( this.addr, false, flatten( v, this.size, 4 ) );
T
tschw 已提交
290

291
}
T
tschw 已提交
292

293
function setValueM3a( gl, v ) {
T
tschw 已提交
294

295
	gl.uniformMatrix3fv( this.addr, false, flatten( v, this.size, 9 ) );
T
tschw 已提交
296

297
}
T
tschw 已提交
298

299
function setValueM4a( gl, v ) {
T
tschw 已提交
300

301
	gl.uniformMatrix4fv( this.addr, false, flatten( v, this.size, 16 ) );
T
tschw 已提交
302

303
}
T
tschw 已提交
304

305
// Array of textures (2D / Cube)
T
tschw 已提交
306

307
function setValueT1a( gl, v, renderer ) {
T
tschw 已提交
308

309 310
	var n = v.length,
		units = allocTexUnits( renderer, n );
T
tschw 已提交
311

312
	gl.uniform1iv( this.addr, units );
T
tschw 已提交
313

314
	for ( var i = 0; i !== n; ++ i ) {
T
tschw 已提交
315

316
		renderer.setTexture2D( v[ i ] || emptyTexture, units[ i ] );
T
tschw 已提交
317

318
	}
T
tschw 已提交
319

320
}
T
tschw 已提交
321

322
function setValueT6a( gl, v, renderer ) {
T
tschw 已提交
323

324 325
	var n = v.length,
		units = allocTexUnits( renderer, n );
T
tschw 已提交
326

327
	gl.uniform1iv( this.addr, units );
T
tschw 已提交
328

329
	for ( var i = 0; i !== n; ++ i ) {
T
tschw 已提交
330

331
		renderer.setTextureCube( v[ i ] || emptyCubeTexture, units[ i ] );
T
tschw 已提交
332

333
	}
T
tschw 已提交
334

335
}
T
tschw 已提交
336

337
// Helper to pick the right setter for a pure (bottom-level) array
T
tschw 已提交
338

339
function getPureArraySetter( type ) {
T
tschw 已提交
340

341
	switch ( type ) {
T
tschw 已提交
342

343 344 345 346
		case 0x1406: return setValue1fv; // FLOAT
		case 0x8b50: return setValueV2a; // _VEC2
		case 0x8b51: return setValueV3a; // _VEC3
		case 0x8b52: return setValueV4a; // _VEC4
T
tschw 已提交
347

348 349 350
		case 0x8b5a: return setValueM2a; // _MAT2
		case 0x8b5b: return setValueM3a; // _MAT3
		case 0x8b5c: return setValueM4a; // _MAT4
T
tschw 已提交
351

352 353
		case 0x8b5e: return setValueT1a; // SAMPLER_2D
		case 0x8b60: return setValueT6a; // SAMPLER_CUBE
T
tschw 已提交
354

355 356 357 358
		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
T
tschw 已提交
359

360
	}
T
tschw 已提交
361

362
}
T
tschw 已提交
363

364
// --- Uniform Classes ---
T
tschw 已提交
365

366
function SingleUniform( id, activeInfo, addr ) {
T
tschw 已提交
367

368 369 370
	this.id = id;
	this.addr = addr;
	this.setValue = getSingularSetter( activeInfo.type );
T
tschw 已提交
371

372
	// this.path = activeInfo.name; // DEBUG
T
tschw 已提交
373

374
}
T
tschw 已提交
375

376
function PureArrayUniform( id, activeInfo, addr ) {
T
tschw 已提交
377

378 379 380 381
	this.id = id;
	this.addr = addr;
	this.size = activeInfo.size;
	this.setValue = getPureArraySetter( activeInfo.type );
T
tschw 已提交
382

383
	// this.path = activeInfo.name; // DEBUG
T
tschw 已提交
384

385
}
T
tschw 已提交
386

387
function StructuredUniform( id ) {
T
tschw 已提交
388

389
	this.id = id;
T
tschw 已提交
390

391
	UniformContainer.call( this ); // mix-in
T
tschw 已提交
392

393
}
T
tschw 已提交
394

M
Mr.doob 已提交
395
StructuredUniform.prototype.setValue = function ( gl, value ) {
T
tschw 已提交
396

397 398
	// Note: Don't need an extra 'renderer' parameter, since samplers
	// are not allowed in structured uniforms.
T
tschw 已提交
399

400
	var seq = this.seq;
T
tschw 已提交
401

402
	for ( var i = 0, n = seq.length; i !== n; ++ i ) {
T
tschw 已提交
403

404 405
		var u = seq[ i ];
		u.setValue( gl, value[ u.id ] );
T
tschw 已提交
406

407
	}
T
tschw 已提交
408

409
};
T
tschw 已提交
410

411
// --- Top-level ---
T
tschw 已提交
412

413
// Parser - builds up the property tree from the path strings
T
tschw 已提交
414

415
var RePathPart = /([\w\d_]+)(\])?(\[|\.)?/g;
T
tschw 已提交
416

417 418 419 420 421 422 423 424
// 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.
T
tschw 已提交
425

426
function addUniform( container, uniformObject ) {
T
tschw 已提交
427

428 429
	container.seq.push( uniformObject );
	container.map[ uniformObject.id ] = uniformObject;
T
tschw 已提交
430

431
}
T
tschw 已提交
432

433
function parseUniform( activeInfo, addr, container ) {
T
tschw 已提交
434

435 436
	var path = activeInfo.name,
		pathLength = path.length;
T
tschw 已提交
437

438 439
	// reset RegExp object, because of the early exit of a previous run
	RePathPart.lastIndex = 0;
T
tschw 已提交
440

M
Mr.doob 已提交
441
	for ( ; ; ) {
T
tschw 已提交
442

443 444
		var match = RePathPart.exec( path ),
			matchEnd = RePathPart.lastIndex,
T
tschw 已提交
445

446 447 448
			id = match[ 1 ],
			idIsIndex = match[ 2 ] === ']',
			subscript = match[ 3 ];
T
tschw 已提交
449

450
		if ( idIsIndex ) id = id | 0; // convert to integer
T
tschw 已提交
451

M
Mr.doob 已提交
452 453
		if ( subscript === undefined || subscript === '[' && matchEnd + 2 === pathLength ) {

454
			// bare name or "pure" bottom-level array "[0]" suffix
T
tschw 已提交
455

456 457 458
			addUniform( container, subscript === undefined ?
					new SingleUniform( id, activeInfo, addr ) :
					new PureArrayUniform( id, activeInfo, addr ) );
T
tschw 已提交
459

460
			break;
T
tschw 已提交
461

462
		} else {
M
Mr.doob 已提交
463

464
			// step into inner node / create it in case it doesn't exist
T
tschw 已提交
465

M
Mr.doob 已提交
466
			var map = container.map, next = map[ id ];
T
tschw 已提交
467

468
			if ( next === undefined ) {
T
tschw 已提交
469

470 471
				next = new StructuredUniform( id );
				addUniform( container, next );
T
tschw 已提交
472 473 474

			}

475
			container = next;
T
tschw 已提交
476

477
		}
T
tschw 已提交
478

479
	}
T
tschw 已提交
480

481
}
T
tschw 已提交
482

483
// Root Container
T
tschw 已提交
484

485
function WebGLUniforms( gl, program, renderer ) {
T
tschw 已提交
486

487
	UniformContainer.call( this );
T
tschw 已提交
488

489
	this.renderer = renderer;
T
tschw 已提交
490

491
	var n = gl.getProgramParameter( program, gl.ACTIVE_UNIFORMS );
T
tschw 已提交
492

M
Mr.doob 已提交
493
	for ( var i = 0; i < n; ++ i ) {
T
tschw 已提交
494

495 496 497
		var info = gl.getActiveUniform( program, i ),
			path = info.name,
			addr = gl.getUniformLocation( program, path );
T
tschw 已提交
498

499
		parseUniform( info, addr, this );
T
tschw 已提交
500

501
	}
T
tschw 已提交
502

503
}
T
tschw 已提交
504

M
Mr.doob 已提交
505
WebGLUniforms.prototype.setValue = function ( gl, name, value ) {
T
tschw 已提交
506

507
	var u = this.map[ name ];
T
tschw 已提交
508

509
	if ( u !== undefined ) u.setValue( gl, value, this.renderer );
T
tschw 已提交
510

511
};
T
tschw 已提交
512

M
Mr.doob 已提交
513
WebGLUniforms.prototype.setOptional = function ( gl, object, name ) {
T
tschw 已提交
514

515
	var v = object[ name ];
T
tschw 已提交
516

517
	if ( v !== undefined ) this.setValue( gl, name, v );
T
tschw 已提交
518

519
};
T
tschw 已提交
520 521


522
// Static interface
T
tschw 已提交
523

M
Mr.doob 已提交
524
WebGLUniforms.upload = function ( gl, seq, values, renderer ) {
T
tschw 已提交
525

526
	for ( var i = 0, n = seq.length; i !== n; ++ i ) {
T
tschw 已提交
527

528 529
		var u = seq[ i ],
			v = values[ u.id ];
T
tschw 已提交
530

531 532
		if ( v.needsUpdate !== false ) {

M
Mr.doob 已提交
533
			// note: always updating when .needsUpdate is undefined
534
			u.setValue( gl, v.value, renderer );
T
tschw 已提交
535 536 537

		}

538
	}
T
tschw 已提交
539

540
};
T
tschw 已提交
541

M
Mr.doob 已提交
542
WebGLUniforms.seqWithValue = function ( seq, values ) {
T
tschw 已提交
543

544
	var r = [];
T
tschw 已提交
545

546
	for ( var i = 0, n = seq.length; i !== n; ++ i ) {
T
tschw 已提交
547

548 549
		var u = seq[ i ];
		if ( u.id in values ) r.push( u );
T
tschw 已提交
550

551
	}
T
tschw 已提交
552

553
	return r;
T
tschw 已提交
554

555
};
T
tschw 已提交
556

557
export { WebGLUniforms };