UNPKG

uglymol

Version:

Macromolecular Viewer for Crystallographers

2,042 lines (1,530 loc) 98.9 kB
// a small subset of THREE.js v83 that is used by UglyMol // modified with eslint --fix and manually, // LICENSE: threejs.org/license (MIT) /* eslint-disable max-len, one-var, guard-for-in */ /* eslint-disable prefer-rest-params, no-invalid-this, no-useless-escape */ /* eslint-disable new-cap, no-extend-native */ // Polyfills if ( Function.prototype.name === undefined ) { // Missing in IE9-11. // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/name Object.defineProperty( Function.prototype, 'name', { get: function () { return this.toString().match( /^\s*function\s*([^\(\s]*)/ )[1]; }, } ); } if ( Object.assign === undefined ) { // Missing in IE. // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign ( function () { Object.assign = function ( target ) { if ( target === undefined || target === null ) { throw new TypeError( 'Cannot convert undefined or null to object' ); } let output = Object( target ); for ( let index = 1; index < arguments.length; index ++ ) { let source = arguments[index]; if ( source !== undefined && source !== null ) { for ( let nextKey in source ) { if ( Object.prototype.hasOwnProperty.call( source, nextKey ) ) { output[nextKey] = source[nextKey]; } } } } return output; }; } )(); } let NoBlending = 0; let NormalBlending = 1; let TrianglesDrawMode = 0; let TriangleStripDrawMode = 1; let TriangleFanDrawMode = 2; /** * @author alteredq / http://alteredqualia.com/ * @author mrdoob / http://mrdoob.com/ */ let _Math = { generateUUID: function () { // http://www.broofa.com/Tools/Math.uuid.htm let chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split( '' ); let uuid = new Array( 36 ); let rnd = 0, r; return function generateUUID() { for ( let i = 0; i < 36; i ++ ) { if ( i === 8 || i === 13 || i === 18 || i === 23 ) { uuid[i] = '-'; } else if ( i === 14 ) { uuid[i] = '4'; } else { if ( rnd <= 0x02 ) rnd = 0x2000000 + ( Math.random() * 0x1000000 ) | 0; r = rnd & 0xf; rnd = rnd >> 4; uuid[i] = chars[( i === 19 ) ? ( r & 0x3 ) | 0x8 : r]; } } return uuid.join( '' ); }; }(), clamp: function ( value, min, max ) { return Math.max( min, Math.min( max, value ) ); }, // compute euclidian modulo of m % n // https://en.wikipedia.org/wiki/Modulo_operation euclideanModulo: function ( n, m ) { return ( ( n % m ) + m ) % m; }, }; /** * @author mikael emtinger / http://gomo.se/ * @author alteredq / http://alteredqualia.com/ * @author WestLangley / http://github.com/WestLangley * @author bhouston / http://clara.io */ function Quaternion( x, y, z, w ) { this._x = x || 0; this._y = y || 0; this._z = z || 0; this._w = ( w !== undefined ) ? w : 1; } Quaternion.prototype = { constructor: Quaternion, get x() { return this._x; }, get y() { return this._y; }, get z() { return this._z; }, get w() { return this._w; }, setFromAxisAngle: function ( axis, angle ) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm // assumes axis is normalized let halfAngle = angle / 2, s = Math.sin( halfAngle ); this._x = axis.x * s; this._y = axis.y * s; this._z = axis.z * s; this._w = Math.cos( halfAngle ); return this; }, setFromRotationMatrix: function ( m ) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) let te = m.elements, m11 = te[0], m12 = te[4], m13 = te[8], m21 = te[1], m22 = te[5], m23 = te[9], m31 = te[2], m32 = te[6], m33 = te[10], trace = m11 + m22 + m33, s; if ( trace > 0 ) { s = 0.5 / Math.sqrt( trace + 1.0 ); this._w = 0.25 / s; this._x = ( m32 - m23 ) * s; this._y = ( m13 - m31 ) * s; this._z = ( m21 - m12 ) * s; } else if ( m11 > m22 && m11 > m33 ) { s = 2.0 * Math.sqrt( 1.0 + m11 - m22 - m33 ); this._w = ( m32 - m23 ) / s; this._x = 0.25 * s; this._y = ( m12 + m21 ) / s; this._z = ( m13 + m31 ) / s; } else if ( m22 > m33 ) { s = 2.0 * Math.sqrt( 1.0 + m22 - m11 - m33 ); this._w = ( m13 - m31 ) / s; this._x = ( m12 + m21 ) / s; this._y = 0.25 * s; this._z = ( m23 + m32 ) / s; } else { s = 2.0 * Math.sqrt( 1.0 + m33 - m11 - m22 ); this._w = ( m21 - m12 ) / s; this._x = ( m13 + m31 ) / s; this._y = ( m23 + m32 ) / s; this._z = 0.25 * s; } return this; }, setFromUnitVectors: function () { // http://lolengine.net/blog/2014/02/24/quaternion-from-two-vectors-final // assumes direction vectors vFrom and vTo are normalized let v1, r; let EPS = 0.000001; return function setFromUnitVectors( vFrom, vTo ) { if ( v1 === undefined ) v1 = new Vector3(); r = vFrom.dot( vTo ) + 1; if ( r < EPS ) { r = 0; if ( Math.abs( vFrom.x ) > Math.abs( vFrom.z ) ) { v1.set( - vFrom.y, vFrom.x, 0 ); } else { v1.set( 0, - vFrom.z, vFrom.y ); } } else { v1.crossVectors( vFrom, vTo ); } this._x = v1.x; this._y = v1.y; this._z = v1.z; this._w = r; return this.normalize(); }; }(), length: function () { return Math.sqrt( this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w ); }, normalize: function () { let l = this.length(); if ( l === 0 ) { this._x = 0; this._y = 0; this._z = 0; this._w = 1; } else { l = 1 / l; this._x = this._x * l; this._y = this._y * l; this._z = this._z * l; this._w = this._w * l; } return this; }, }; /** * @author mrdoob / http://mrdoob.com/ * @author *kile / http://kile.stravaganza.org/ * @author philogb / http://blog.thejit.org/ * @author mikael emtinger / http://gomo.se/ * @author egraether / http://egraether.com/ * @author WestLangley / http://github.com/WestLangley */ function Vector3( x, y, z ) { this.x = x || 0; this.y = y || 0; this.z = z || 0; } Vector3.prototype = { constructor: Vector3, isVector3: true, set: function ( x, y, z ) { this.x = x; this.y = y; this.z = z; return this; }, clone: function () { return new this.constructor( this.x, this.y, this.z ); }, copy: function ( v ) { this.x = v.x; this.y = v.y; this.z = v.z; return this; }, add: function ( v ) { this.x += v.x; this.y += v.y; this.z += v.z; return this; }, addVectors: function ( a, b ) { this.x = a.x + b.x; this.y = a.y + b.y; this.z = a.z + b.z; return this; }, addScaledVector: function ( v, s ) { this.x += v.x * s; this.y += v.y * s; this.z += v.z * s; return this; }, sub: function ( v ) { this.x -= v.x; this.y -= v.y; this.z -= v.z; return this; }, subVectors: function ( a, b ) { this.x = a.x - b.x; this.y = a.y - b.y; this.z = a.z - b.z; return this; }, multiplyScalar: function ( scalar ) { if ( isFinite( scalar ) ) { this.x *= scalar; this.y *= scalar; this.z *= scalar; } else { this.x = 0; this.y = 0; this.z = 0; } return this; }, applyMatrix4: function ( m ) { // input: Matrix4 affine matrix let x = this.x, y = this.y, z = this.z; let e = m.elements; this.x = e[0] * x + e[4] * y + e[8] * z + e[12]; this.y = e[1] * x + e[5] * y + e[9] * z + e[13]; this.z = e[2] * x + e[6] * y + e[10] * z + e[14]; return this; }, applyProjection: function ( m ) { // input: Matrix4 projection matrix let x = this.x, y = this.y, z = this.z; let e = m.elements; let d = 1 / ( e[3] * x + e[7] * y + e[11] * z + e[15] ); // perspective divide this.x = ( e[0] * x + e[4] * y + e[8] * z + e[12] ) * d; this.y = ( e[1] * x + e[5] * y + e[9] * z + e[13] ) * d; this.z = ( e[2] * x + e[6] * y + e[10] * z + e[14] ) * d; return this; }, applyQuaternion: function ( q ) { let x = this.x, y = this.y, z = this.z; let qx = q.x, qy = q.y, qz = q.z, qw = q.w; // calculate quat * vector let ix = qw * x + qy * z - qz * y; let iy = qw * y + qz * x - qx * z; let iz = qw * z + qx * y - qy * x; let iw = - qx * x - qy * y - qz * z; // calculate result * inverse quat this.x = ix * qw + iw * - qx + iy * - qz - iz * - qy; this.y = iy * qw + iw * - qy + iz * - qx - ix * - qz; this.z = iz * qw + iw * - qz + ix * - qy - iy * - qx; return this; }, unproject: function () { let matrix; return function unproject( camera ) { if ( matrix === undefined ) matrix = new Matrix4(); matrix.multiplyMatrices( camera.matrixWorld, matrix.getInverse( camera.projectionMatrix ) ); return this.applyProjection( matrix ); }; }(), transformDirection: function ( m ) { // input: Matrix4 affine matrix // vector interpreted as a direction let x = this.x, y = this.y, z = this.z; let e = m.elements; this.x = e[0] * x + e[4] * y + e[8] * z; this.y = e[1] * x + e[5] * y + e[9] * z; this.z = e[2] * x + e[6] * y + e[10] * z; return this.normalize(); }, divideScalar: function ( scalar ) { return this.multiplyScalar( 1 / scalar ); }, dot: function ( v ) { return this.x * v.x + this.y * v.y + this.z * v.z; }, lengthSq: function () { return this.x * this.x + this.y * this.y + this.z * this.z; }, length: function () { return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z ); }, normalize: function () { return this.divideScalar( this.length() ); }, setLength: function ( length ) { return this.multiplyScalar( length / this.length() ); }, lerp: function ( v, alpha ) { this.x += ( v.x - this.x ) * alpha; this.y += ( v.y - this.y ) * alpha; this.z += ( v.z - this.z ) * alpha; return this; }, cross: function ( v ) { let x = this.x, y = this.y, z = this.z; this.x = y * v.z - z * v.y; this.y = z * v.x - x * v.z; this.z = x * v.y - y * v.x; return this; }, crossVectors: function ( a, b ) { let ax = a.x, ay = a.y, az = a.z; let bx = b.x, by = b.y, bz = b.z; this.x = ay * bz - az * by; this.y = az * bx - ax * bz; this.z = ax * by - ay * bx; return this; }, projectOnVector: function ( vector ) { let scalar = vector.dot( this ) / vector.lengthSq(); return this.copy( vector ).multiplyScalar( scalar ); }, projectOnPlane: function () { let v1; return function projectOnPlane( planeNormal ) { if ( v1 === undefined ) v1 = new Vector3(); v1.copy( this ).projectOnVector( planeNormal ); return this.sub( v1 ); }; }(), distanceTo: function ( v ) { return Math.sqrt( this.distanceToSquared( v ) ); }, distanceToSquared: function ( v ) { let dx = this.x - v.x, dy = this.y - v.y, dz = this.z - v.z; return dx * dx + dy * dy + dz * dz; }, setFromMatrixPosition: function ( m ) { return this.setFromMatrixColumn( m, 3 ); }, setFromMatrixColumn: function ( m, index ) { return this.fromArray( m.elements, index * 4 ); }, equals: function ( v ) { return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) ); }, fromArray: function ( array, offset ) { if ( offset === undefined ) offset = 0; this.x = array[offset]; this.y = array[offset + 1]; this.z = array[offset + 2]; return this; }, }; /** * @author mrdoob / http://mrdoob.com/ * @author supereggbert / http://www.paulbrunt.co.uk/ * @author philogb / http://blog.thejit.org/ * @author jordi_ros / http://plattsoft.com * @author D1plo1d / http://github.com/D1plo1d * @author alteredq / http://alteredqualia.com/ * @author mikael emtinger / http://gomo.se/ * @author timknip / http://www.floorplanner.com/ * @author bhouston / http://clara.io * @author WestLangley / http://github.com/WestLangley */ function Matrix4() { this.elements = new Float32Array( [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, ] ); } Matrix4.prototype = { constructor: Matrix4, isMatrix4: true, copy: function ( m ) { this.elements.set( m.elements ); return this; }, makeRotationFromQuaternion: function ( q ) { let te = this.elements; let x = q.x, y = q.y, z = q.z, w = q.w; let x2 = x + x, y2 = y + y, z2 = z + z; let xx = x * x2, xy = x * y2, xz = x * z2; let yy = y * y2, yz = y * z2, zz = z * z2; let wx = w * x2, wy = w * y2, wz = w * z2; te[0] = 1 - ( yy + zz ); te[4] = xy - wz; te[8] = xz + wy; te[1] = xy + wz; te[5] = 1 - ( xx + zz ); te[9] = yz - wx; te[2] = xz - wy; te[6] = yz + wx; te[10] = 1 - ( xx + yy ); // last column te[3] = 0; te[7] = 0; te[11] = 0; // bottom row te[12] = 0; te[13] = 0; te[14] = 0; te[15] = 1; return this; }, lookAt: function () { let x, y, z; return function lookAt( eye, target, up ) { if ( x === undefined ) { x = new Vector3(); y = new Vector3(); z = new Vector3(); } let te = this.elements; z.subVectors( eye, target ).normalize(); if ( z.lengthSq() === 0 ) { z.z = 1; } x.crossVectors( up, z ).normalize(); if ( x.lengthSq() === 0 ) { z.z += 0.0001; x.crossVectors( up, z ).normalize(); } y.crossVectors( z, x ); te[0] = x.x; te[4] = y.x; te[8] = z.x; te[1] = x.y; te[5] = y.y; te[9] = z.y; te[2] = x.z; te[6] = y.z; te[10] = z.z; return this; }; }(), multiplyMatrices: function ( a, b ) { let ae = a.elements; let be = b.elements; let te = this.elements; let a11 = ae[0], a12 = ae[4], a13 = ae[8], a14 = ae[12]; let a21 = ae[1], a22 = ae[5], a23 = ae[9], a24 = ae[13]; let a31 = ae[2], a32 = ae[6], a33 = ae[10], a34 = ae[14]; let a41 = ae[3], a42 = ae[7], a43 = ae[11], a44 = ae[15]; let b11 = be[0], b12 = be[4], b13 = be[8], b14 = be[12]; let b21 = be[1], b22 = be[5], b23 = be[9], b24 = be[13]; let b31 = be[2], b32 = be[6], b33 = be[10], b34 = be[14]; let b41 = be[3], b42 = be[7], b43 = be[11], b44 = be[15]; te[0] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41; te[4] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42; te[8] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43; te[12] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44; te[1] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41; te[5] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42; te[9] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43; te[13] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44; te[2] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41; te[6] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42; te[10] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43; te[14] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44; te[3] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41; te[7] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42; te[11] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43; te[15] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44; return this; }, setPosition: function ( v ) { let te = this.elements; te[12] = v.x; te[13] = v.y; te[14] = v.z; return this; }, getInverse: function ( m, throwOnDegenerate ) { // based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm let te = this.elements, me = m.elements, n11 = me[0], n21 = me[1], n31 = me[2], n41 = me[3], n12 = me[4], n22 = me[5], n32 = me[6], n42 = me[7], n13 = me[8], n23 = me[9], n33 = me[10], n43 = me[11], n14 = me[12], n24 = me[13], n34 = me[14], n44 = me[15], t11 = n23 * n34 * n42 - n24 * n33 * n42 + n24 * n32 * n43 - n22 * n34 * n43 - n23 * n32 * n44 + n22 * n33 * n44, t12 = n14 * n33 * n42 - n13 * n34 * n42 - n14 * n32 * n43 + n12 * n34 * n43 + n13 * n32 * n44 - n12 * n33 * n44, t13 = n13 * n24 * n42 - n14 * n23 * n42 + n14 * n22 * n43 - n12 * n24 * n43 - n13 * n22 * n44 + n12 * n23 * n44, t14 = n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34; let det = n11 * t11 + n21 * t12 + n31 * t13 + n41 * t14; if ( det === 0 ) { let msg = 'Matrix4.getInverse(): can\'t invert matrix, determinant is 0'; if ( throwOnDegenerate === true ) { throw new Error( msg ); } else { console.warn( msg ); } return this.identity(); } let detInv = 1 / det; te[0] = t11 * detInv; te[1] = ( n24 * n33 * n41 - n23 * n34 * n41 - n24 * n31 * n43 + n21 * n34 * n43 + n23 * n31 * n44 - n21 * n33 * n44 ) * detInv; te[2] = ( n22 * n34 * n41 - n24 * n32 * n41 + n24 * n31 * n42 - n21 * n34 * n42 - n22 * n31 * n44 + n21 * n32 * n44 ) * detInv; te[3] = ( n23 * n32 * n41 - n22 * n33 * n41 - n23 * n31 * n42 + n21 * n33 * n42 + n22 * n31 * n43 - n21 * n32 * n43 ) * detInv; te[4] = t12 * detInv; te[5] = ( n13 * n34 * n41 - n14 * n33 * n41 + n14 * n31 * n43 - n11 * n34 * n43 - n13 * n31 * n44 + n11 * n33 * n44 ) * detInv; te[6] = ( n14 * n32 * n41 - n12 * n34 * n41 - n14 * n31 * n42 + n11 * n34 * n42 + n12 * n31 * n44 - n11 * n32 * n44 ) * detInv; te[7] = ( n12 * n33 * n41 - n13 * n32 * n41 + n13 * n31 * n42 - n11 * n33 * n42 - n12 * n31 * n43 + n11 * n32 * n43 ) * detInv; te[8] = t13 * detInv; te[9] = ( n14 * n23 * n41 - n13 * n24 * n41 - n14 * n21 * n43 + n11 * n24 * n43 + n13 * n21 * n44 - n11 * n23 * n44 ) * detInv; te[10] = ( n12 * n24 * n41 - n14 * n22 * n41 + n14 * n21 * n42 - n11 * n24 * n42 - n12 * n21 * n44 + n11 * n22 * n44 ) * detInv; te[11] = ( n13 * n22 * n41 - n12 * n23 * n41 - n13 * n21 * n42 + n11 * n23 * n42 + n12 * n21 * n43 - n11 * n22 * n43 ) * detInv; te[12] = t14 * detInv; te[13] = ( n13 * n24 * n31 - n14 * n23 * n31 + n14 * n21 * n33 - n11 * n24 * n33 - n13 * n21 * n34 + n11 * n23 * n34 ) * detInv; te[14] = ( n14 * n22 * n31 - n12 * n24 * n31 - n14 * n21 * n32 + n11 * n24 * n32 + n12 * n21 * n34 - n11 * n22 * n34 ) * detInv; te[15] = ( n12 * n23 * n31 - n13 * n22 * n31 + n13 * n21 * n32 - n11 * n23 * n32 - n12 * n21 * n33 + n11 * n22 * n33 ) * detInv; return this; }, scale: function ( v ) { let te = this.elements; let x = v.x, y = v.y, z = v.z; te[0] *= x; te[4] *= y; te[8] *= z; te[1] *= x; te[5] *= y; te[9] *= z; te[2] *= x; te[6] *= y; te[10] *= z; te[3] *= x; te[7] *= y; te[11] *= z; return this; }, getMaxScaleOnAxis: function () { let te = this.elements; let scaleXSq = te[0] * te[0] + te[1] * te[1] + te[2] * te[2]; let scaleYSq = te[4] * te[4] + te[5] * te[5] + te[6] * te[6]; let scaleZSq = te[8] * te[8] + te[9] * te[9] + te[10] * te[10]; return Math.sqrt( Math.max( scaleXSq, scaleYSq, scaleZSq ) ); }, compose: function ( position, quaternion, scale ) { this.makeRotationFromQuaternion( quaternion ); this.scale( scale ); this.setPosition( position ); return this; }, makeOrthographic: function ( left, right, top, bottom, near, far ) { let te = this.elements; let w = 1.0 / ( right - left ); let h = 1.0 / ( top - bottom ); let p = 1.0 / ( far - near ); let x = ( right + left ) * w; let y = ( top + bottom ) * h; let z = ( far + near ) * p; te[0] = 2 * w; te[4] = 0; te[8] = 0; te[12] = - x; te[1] = 0; te[5] = 2 * h; te[9] = 0; te[13] = - y; te[2] = 0; te[6] = 0; te[10] = - 2 * p; te[14] = - z; te[3] = 0; te[7] = 0; te[11] = 0; te[15] = 1; return this; }, }; /** * https://github.com/mrdoob/eventdispatcher.js/ */ function EventDispatcher() {} Object.assign( EventDispatcher.prototype, { addEventListener: function ( type, listener ) { if ( this._listeners === undefined ) this._listeners = {}; let listeners = this._listeners; if ( listeners[type] === undefined ) { listeners[type] = []; } if ( listeners[type].indexOf( listener ) === - 1 ) { listeners[type].push( listener ); } }, removeEventListener: function ( type, listener ) { if ( this._listeners === undefined ) return; let listeners = this._listeners; let listenerArray = listeners[type]; if ( listenerArray !== undefined ) { let index = listenerArray.indexOf( listener ); if ( index !== - 1 ) { listenerArray.splice( index, 1 ); } } }, dispatchEvent: function ( event ) { if ( this._listeners === undefined ) return; let listeners = this._listeners; let listenerArray = listeners[event.type]; if ( listenerArray !== undefined ) { event.target = this; let array = [], i = 0; let length = listenerArray.length; for ( i = 0; i < length; i ++ ) { array[i] = listenerArray[i]; } for ( i = 0; i < length; i ++ ) { array[i].call( this, event ); } } }, } ); /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ * @author szimek / https://github.com/szimek/ */ let textureId = 0; function Texture( image ) { Object.defineProperty( this, 'id', { value: textureId ++ } ); this.uuid = _Math.generateUUID(); this.name = ''; this.image = image; this.version = 0; } Texture.prototype = { constructor: Texture, isTexture: true, set needsUpdate( value ) { if ( value === true ) this.version ++; }, dispose: function () { this.dispatchEvent( { type: 'dispose' } ); }, }; Object.assign( Texture.prototype, EventDispatcher.prototype ); /** * @author tschw * * 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 * */ let emptyTexture = new Texture(); // --- Base for inner nodes (including the root) --- function UniformContainer() { this.seq = []; this.map = {}; } // --- Setters --- // Note: Defining these methods externally, because they come in a bunch // and this way their names minify. // Single scalar function setValue1f( gl, v ) { gl.uniform1f( this.addr, v ); } function setValue1i( gl, v ) { gl.uniform1i( this.addr, v ); } // Single float vector (from flat array or VectorN) function setValue2fv( gl, v ) { if ( v.x === undefined ) gl.uniform2fv( this.addr, v ); else gl.uniform2f( this.addr, v.x, v.y ); } function setValue3fv( gl, v ) { 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 ); } } function setValue4fv( gl, v ) { if ( v.x === undefined ) gl.uniform4fv( this.addr, v ); else gl.uniform4f( this.addr, v.x, v.y, v.z, v.w ); } // Single matrix (from flat array or MatrixN) function setValue2fm( gl, v ) { gl.uniformMatrix2fv( this.addr, false, v.elements || v ); } function setValue3fm( gl, v ) { gl.uniformMatrix3fv( this.addr, false, v.elements || v ); } function setValue4fm( gl, v ) { gl.uniformMatrix4fv( this.addr, false, v.elements || v ); } // Single texture (2D / Cube) function setValueT1( gl, v, renderer ) { let unit = renderer.allocTextureUnit(); gl.uniform1i( this.addr, unit ); renderer.setTexture2D( v || emptyTexture, unit ); } // Integer / Boolean vectors or arrays thereof (always flat arrays) 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 ); } // 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: return setValueT1; // SAMPLER_2D 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 } } // --- Uniform Classes --- function SingleUniform( id, activeInfo, addr ) { this.id = id; this.addr = addr; this.setValue = getSingularSetter( activeInfo.type ); // this.path = activeInfo.name; // DEBUG } // --- Top-level --- // Parser - builds up the property tree from the path strings let 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 ) { let path = activeInfo.name; // reset RegExp object, because of the early exit of a previous run RePathPart.lastIndex = 0; for (; ;) { let match = RePathPart.exec( path ), id = match[1], idIsIndex = match[2] === ']', subscript = match[3]; if ( idIsIndex ) id = id | 0; // convert to integer if ( subscript === undefined ) { addUniform( container, new SingleUniform( id, activeInfo, addr ) ); break; } } } // Root Container function WebGLUniforms( gl, program, renderer ) { UniformContainer.call( this ); this.renderer = renderer; let n = gl.getProgramParameter( program, gl.ACTIVE_UNIFORMS ); for ( let i = 0; i !== n; ++ i ) { let info = gl.getActiveUniform( program, i ), path = info.name, addr = gl.getUniformLocation( program, path ); parseUniform( info, addr, this ); } } WebGLUniforms.prototype.setValue = function ( gl, name, value ) { let u = this.map[name]; if ( u !== undefined ) u.setValue( gl, value, this.renderer ); }; WebGLUniforms.prototype.set = function ( gl, object, name ) { let u = this.map[name]; if ( u !== undefined ) u.setValue( gl, object[name], this.renderer ); }; // Static interface WebGLUniforms.upload = function ( gl, seq, values, renderer ) { for ( let i = 0, n = seq.length; i !== n; ++ i ) { let u = seq[i], v = values[u.id]; if ( v.needsUpdate !== false ) { // note: always updating when .needsUpdate is undefined u.setValue( gl, v.value, renderer ); } } }; WebGLUniforms.seqWithValue = function ( seq, values ) { let r = []; for ( let i = 0, n = seq.length; i !== n; ++ i ) { let u = seq[i]; if ( u.id in values ) r.push( u ); } return r; }; /** * @author supereggbert / http://www.paulbrunt.co.uk/ * @author philogb / http://blog.thejit.org/ * @author mikael emtinger / http://gomo.se/ * @author egraether / http://egraether.com/ * @author WestLangley / http://github.com/WestLangley */ function Vector4( x, y, z, w ) { this.x = x || 0; this.y = y || 0; this.z = z || 0; this.w = ( w !== undefined ) ? w : 1; } Vector4.prototype = { constructor: Vector4, isVector4: true, set: function ( x, y, z, w ) { this.x = x; this.y = y; this.z = z; this.w = w; return this; }, copy: function ( v ) { this.x = v.x; this.y = v.y; this.z = v.z; this.w = ( v.w !== undefined ) ? v.w : 1; return this; }, multiplyScalar: function ( scalar ) { if ( isFinite( scalar ) ) { this.x *= scalar; this.y *= scalar; this.z *= scalar; this.w *= scalar; } else { this.x = 0; this.y = 0; this.z = 0; this.w = 0; } return this; }, equals: function ( v ) { return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) && ( v.w === this.w ) ); }, }; /** * @author mrdoob / http://mrdoob.com/ */ function Color( r, g, b ) { if ( g === undefined && b === undefined ) { // r is Color, hex or string return this.set( r ); } return this.setRGB( r, g, b ); } Color.prototype = { constructor: Color, isColor: true, r: 1, g: 1, b: 1, set: function ( value ) { if ( value && value.isColor ) { this.copy( value ); } else if ( typeof value === 'number' ) { this.setHex( value ); } return this; }, setHex: function ( hex ) { hex = Math.floor( hex ); this.r = ( hex >> 16 & 255 ) / 255; this.g = ( hex >> 8 & 255 ) / 255; this.b = ( hex & 255 ) / 255; return this; }, setRGB: function ( r, g, b ) { this.r = r; this.g = g; this.b = b; return this; }, setHSL: function () { function hue2rgb( p, q, t ) { if ( t < 0 ) t += 1; if ( t > 1 ) t -= 1; if ( t < 1 / 6 ) return p + ( q - p ) * 6 * t; if ( t < 1 / 2 ) return q; if ( t < 2 / 3 ) return p + ( q - p ) * 6 * ( 2 / 3 - t ); return p; } return function setHSL( h, s, l ) { // h,s,l ranges are in 0.0 - 1.0 h = _Math.euclideanModulo( h, 1 ); s = _Math.clamp( s, 0, 1 ); l = _Math.clamp( l, 0, 1 ); if ( s === 0 ) { this.r = this.g = this.b = l; } else { let p = l <= 0.5 ? l * ( 1 + s ) : l + s - ( l * s ); let q = ( 2 * l ) - p; this.r = hue2rgb( q, p, h + 1 / 3 ); this.g = hue2rgb( q, p, h ); this.b = hue2rgb( q, p, h - 1 / 3 ); } return this; }; }(), getHSL: function () { // h,s,l ranges are in 0.0 - 1.0 let hsl = { h: 0, s: 0, l: 0 }; const r = this.r, g = this.g, b = this.b; const max = Math.max( r, g, b ); const min = Math.min( r, g, b ); let hue, saturation; const lightness = ( min + max ) / 2.0; if ( min === max ) { hue = 0; saturation = 0; } else { const delta = max - min; saturation = lightness <= 0.5 ? delta / ( max + min ) : delta / ( 2 - max - min ); switch ( max ) { case r: hue = ( g - b ) / delta + ( g < b ? 6 : 0 ); break; case g: hue = ( b - r ) / delta + 2; break; case b: hue = ( r - g ) / delta + 4; break; } hue /= 6; } hsl.h = hue; hsl.s = saturation; hsl.l = lightness; return hsl; }, clone: function () { return new this.constructor( this.r, this.g, this.b ); }, copy: function ( color ) { this.r = color.r; this.g = color.g; this.b = color.b; return this; }, getHex: function () { return ( this.r * 255 ) << 16 ^ ( this.g * 255 ) << 8 ^ ( this.b * 255 ) << 0; }, getHexString: function () { return ( '000000' + this.getHex().toString( 16 ) ).slice( - 6 ); }, }; /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ */ let materialId = 0; function Material() { Object.defineProperty( this, 'id', { value: materialId ++ } ); this.uuid = _Math.generateUUID(); this.name = ''; this.type = 'Material'; this.fog = true; this.opacity = 1; this.transparent = false; this.depthTest = true; this.depthWrite = true; this.precision = null; // override the renderer's default precision for this material this.premultipliedAlpha = false; this.visible = true; this._needsUpdate = true; } Material.prototype = { constructor: Material, isMaterial: true, get needsUpdate() { return this._needsUpdate; }, set needsUpdate( value ) { if ( value === true ) this.update(); this._needsUpdate = value; }, setValues: function ( values ) { for ( let key in values ) { let newValue = values[key]; this[key] = newValue; } }, update: function () { this.dispatchEvent( { type: 'update' } ); }, dispose: function () { this.dispatchEvent( { type: 'dispose' } ); }, }; Object.assign( Material.prototype, EventDispatcher.prototype ); /** * @author alteredq / http://alteredqualia.com/ * * parameters = { * uniforms: { "parameter1": { value: 1.0 }, "parameter2": { value2: 2 } }, * * fragmentShader: <string>, * vertexShader: <string>, * } */ function ShaderMaterial( parameters ) { Material.call( this ); this.type = 'ShaderMaterial'; this.uniforms = {}; this.vertexShader = ''; this.fragmentShader = ''; this.linewidth = 1; this.fog = false; // set to use scene fog this.extensions = { fragDepth: false, // set to use fragment depth values }; this.setValues( parameters ); } ShaderMaterial.prototype = Object.create( Material.prototype ); ShaderMaterial.prototype.constructor = ShaderMaterial; ShaderMaterial.prototype.isShaderMaterial = true; /** * @author bhouston / http://clara.io */ function Ray( origin, direction ) { this.origin = ( origin !== undefined ) ? origin : new Vector3(); this.direction = ( direction !== undefined ) ? direction : new Vector3(); } Ray.prototype = { constructor: Ray, copy: function ( ray ) { this.origin.copy( ray.origin ); this.direction.copy( ray.direction ); return this; }, distanceSqToPoint: function () { let v1 = new Vector3(); return function distanceSqToPoint( point ) { let directionDistance = v1.subVectors( point, this.origin ).dot( this.direction ); // point behind the ray if ( directionDistance < 0 ) { return this.origin.distanceToSquared( point ); } v1.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin ); return v1.distanceToSquared( point ); }; }(), distanceSqToSegment: function () { let segCenter = new Vector3(); let segDir = new Vector3(); let diff = new Vector3(); return function distanceSqToSegment( v0, v1, optionalPointOnRay, optionalPointOnSegment ) { // from http://www.geometrictools.com/GTEngine/Include/Mathematics/GteDistRaySegment.h // It returns the min distance between the ray and the segment // defined by v0 and v1 // It can also set two optional targets : // - The closest point on the ray // - The closest point on the segment segCenter.copy( v0 ).add( v1 ).multiplyScalar( 0.5 ); segDir.copy( v1 ).sub( v0 ).normalize(); diff.copy( this.origin ).sub( segCenter ); let segExtent = v0.distanceTo( v1 ) * 0.5; let a01 = - this.direction.dot( segDir ); let b0 = diff.dot( this.direction ); let b1 = - diff.dot( segDir ); let c = diff.lengthSq(); let det = Math.abs( 1 - a01 * a01 ); let s0, s1, sqrDist, extDet; if ( det > 0 ) { // The ray and segment are not parallel. s0 = a01 * b1 - b0; s1 = a01 * b0 - b1; extDet = segExtent * det; if ( s0 >= 0 ) { if ( s1 >= - extDet ) { if ( s1 <= extDet ) { // region 0 // Minimum at interior points of ray and segment. let invDet = 1 / det; s0 *= invDet; s1 *= invDet; sqrDist = s0 * ( s0 + a01 * s1 + 2 * b0 ) + s1 * ( a01 * s0 + s1 + 2 * b1 ) + c; } else { // region 1 s1 = segExtent; s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; } } else { // region 5 s1 = - segExtent; s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; } } else { if ( s1 <= - extDet ) { // region 4 s0 = Math.max( 0, - ( - a01 * segExtent + b0 ) ); s1 = ( s0 > 0 ) ? - segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent ); sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; } else if ( s1 <= extDet ) { // region 3 s0 = 0; s1 = Math.min( Math.max( - segExtent, - b1 ), segExtent ); sqrDist = s1 * ( s1 + 2 * b1 ) + c; } else { // region 2 s0 = Math.max( 0, - ( a01 * segExtent + b0 ) ); s1 = ( s0 > 0 ) ? segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent ); sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; } } } else { // Ray and segment are parallel. s1 = ( a01 > 0 ) ? - segExtent : segExtent; s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; } if ( optionalPointOnRay ) { optionalPointOnRay.copy( this.direction ).multiplyScalar( s0 ).add( this.origin ); } if ( optionalPointOnSegment ) { optionalPointOnSegment.copy( segDir ).multiplyScalar( s1 ).add( segCenter ); } return sqrDist; }; }(), applyMatrix4: function ( matrix4 ) { this.direction.add( this.origin ).applyMatrix4( matrix4 ); this.origin.applyMatrix4( matrix4 ); this.direction.sub( this.origin ); this.direction.normalize(); return this; }, }; /** * @author mrdoob / http://mrdoob.com/ * @author mikael emtinger / http://gomo.se/ * @author alteredq / http://alteredqualia.com/ * @author WestLangley / http://github.com/WestLangley * @author elephantatwork / www.elephantatwork.ch */ let object3DId = 0; function Object3D() { Object.defineProperty( this, 'id', { value: object3DId ++ } ); this.uuid = _Math.generateUUID(); this.name = ''; this.type = 'Object3D'; this.parent = null; this.children = []; this.up = Object3D.DefaultUp.clone(); let position = new Vector3(); let quaternion = new Quaternion(); let scale = new Vector3( 1, 1, 1 ); Object.defineProperties( this, { position: { enumerable: true, value: position, }, quaternion: { enumerable: true, value: quaternion, }, scale: { enumerable: true, value: scale, }, modelViewMatrix: { value: new Matrix4(), }, } ); this.matrix = new Matrix4(); this.matrixWorld = new Matrix4(); this.matrixAutoUpdate = Object3D.DefaultMatrixAutoUpdate; this.matrixWorldNeedsUpdate = false; this.visible = true; this.frustumCulled = true; this.renderOrder = 0; this.userData = {}; } Object3D.DefaultUp = new Vector3( 0, 1, 0 ); Object3D.DefaultMatrixAutoUpdate = true; Object.assign( Object3D.prototype, EventDispatcher.prototype, { isObject3D: true, add: function ( object ) { if ( arguments.length > 1 ) { for ( let i = 0; i < arguments.length; i ++ ) { this.add( arguments[i] ); } return this; } if ( ( object && object.isObject3D ) ) { if ( object.parent !== null ) { object.parent.remove( object ); } object.parent = this; object.dispatchEvent( { type: 'added' } ); this.children.push( object ); } return this; }, remove: function ( object ) { if ( arguments.length > 1 ) { for ( let i = 0; i < arguments.length; i ++ ) { this.remove( arguments[i] ); } } let index = this.children.indexOf( object ); if ( index !== - 1 ) { object.parent = null; object.dispatchEvent( { type: 'removed' } ); this.children.splice( index, 1 ); } }, updateMatrix: function () { this.matrix.compose( this.position, this.quaternion, this.scale ); this.matrixWorldNeedsUpdate = true; }, updateMatrixWorld: function ( force ) { if ( this.matrixAutoUpdate === true ) this.updateMatrix(); if ( this.matrixWorldNeedsUpdate === true || force === true ) { if ( this.parent === null ) { this.matrixWorld.copy( this.matrix ); } else { this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix ); } this.matrixWorldNeedsUpdate = false; force = true; } // update children let children = this.children; for ( let i = 0, l = children.length; i < l; i ++ ) { children[i].updateMatrixWorld( force ); } }, } ); /** * @author mrdoob / http://mrdoob.com/ */ function BufferAttribute( array, itemSize, normalized ) { if ( Array.isArray( array ) ) { throw new TypeError( 'BufferAttribute: array should be a Typed Array.' ); } this.uuid = _Math.generateUUID(); this.array = array; this.itemSize = itemSize; this.count = array !== undefined ? array.length / itemSize : 0; this.normalized = normalized === true; this.dynamic = false; this.updateRange = { offset: 0, count: - 1 }; this.onUploadCallback = function () {}; this.version = 0; } BufferAttribute.prototype = { constructor: BufferAttribute, isBufferAttribute: true, }; let count = 0; function GeometryIdCount() { return count++; } /** * @author alteredq / http://alteredqualia.com/ * @author mrdoob / http://mrdoob.com/ */ function BufferGeometry() { Object.defineProperty( this, 'id', { value: GeometryIdCount() } ); this.uuid = _Math.generateUUID(); this.name = ''; this.type = 'BufferGeometry'; this.index = null; this.attributes = {}; this.groups = []; this.boundingBox = null; this.boundingSphere = null; this.drawRange = { start: 0, count: Infinity }; } Object.assign( BufferGeometry.prototype, EventDispatcher.prototype, { isBufferGeometry: true, setIndex: function ( index ) { this.index = index; }, addAttribute: function ( name, attribute ) { this.attributes[name] = attribute; return this; }, dispose: function () { this.dispatchEvent( { type: 'dispose' } ); }, } ); BufferGeometry.MaxIndex = 65535; /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ * @author mikael emtinger / http://gomo.se/ * @author jonobr1 / http://jonobr1.com/ */ function Mesh( geometry, material ) { Object3D.call( this ); this.type = 'Mesh'; this.geometry = geometry !== undefined ? geometry : new BufferGeometry(); this.material = material; this.drawMode = TrianglesDrawMode; } Mesh.prototype = Object.assign( Object.create( Object3D.prototype ), { constructor: Mesh, isMesh: true, } ); /** * @author mrdoob / http://mrdoob.com/ * @author mikael emtinger / http://gomo.se/ * @author WestLangley / http://github.com/WestLangley */ function Camera() { Object3D.call( this ); this.type = 'Camera'; this.matrixWorldInverse = new Matrix4(); this.projectionMatrix = new Matrix4(); } Camera.prototype = Object.create( Object3D.prototype ); Camera.prototype.constructor = Camera; Camera.prototype.isCamera = true; Camera.prototype.lookAt = function () { // This routine does not support cameras with rotated and/or translated parent(s) let m1 = new Matrix4(); return function lookAt( vector ) { m1.lookAt( this.position, vector, this.up ); this.quaternion.setFromRotationMatrix( m1 ); }; }(); /** * @author alteredq / http://alteredqualia.com/ * @author arose / http://github.com/arose */ function OrthographicCamera( left, right, top, bottom, near, far ) { Camera.call( this ); this.type = 'OrthographicCamera'; this.zoom = 1; this.left = left; this.right = right; this.top = top; this.bottom = bottom; this.near = ( near !== undefined ) ? near : 0.1; this.far = ( far !== undefined ) ? far : 2000; this.updateProjectionMatrix(); } OrthographicCamera.prototype = Object.assign( Object.create( Camera.prototype ), { constructor: OrthographicCamera, updateProjectionMatrix: function () { let dx = ( this.right - this.left ) / ( 2 * this.zoom ); let dy = ( this.top - this.bottom ) / ( 2 * this.zoom ); let cx = ( this.right + this.left ) / 2; let cy = ( this.top + this.bottom ) / 2; let left = cx - dx; let right = cx + dx; let top = cy + dy; let bottom = cy - dy; this.projectionMatrix.makeOrthographic( left, right, top, bottom, this.near, this.far ); }, } ); /** * @author mrdoob / http://mrdoob.com/ */ function WebGLIndexedBufferRenderer( gl, extensions, infoRender ) { let mode; function setMode( value ) { mode = value; } let type, size; function setIndex( index ) { if ( index.array instanceof Uint32Array && extensions.get( 'OES_element_index_uint' ) ) { type = gl.UNSIGNED_INT; size = 4; } else if ( index.array instanceof Uint16Array ) { type = gl.UNSIGNED_SHORT; size = 2; } else { type = gl.UNSIGNED_BYTE; size = 1; } } function render( start, count ) { gl.drawElements( mode, count, type, start * size ); infoRender.calls ++; infoRender.vertices += count; if ( mode === gl.TRIANGLES ) infoRender.faces += count / 3; } return { setMode: setMode, setIndex: setIndex, render: render, }; } /** * @author mrdoob / http://mrdoob.com/ */ function WebGLBufferRenderer( gl, extensions, infoRender ) { let mode; function setMode( value ) { mode = value; } function render( start, count ) { gl.drawArrays( mode, start, count ); infoRender.calls ++; infoRender.vertices += count; if ( mode === gl.TRIANGLES ) infoRender.faces += count / 3; } return { setMode: setMode, render: render, }; } /** * @author mrdoob / http://mrdoob.com/ */ function WebGLShader( gl, type, string ) { let shader = gl.createShader( type ); gl.shaderSource( shader, string ); gl.compileShader( shader ); if ( gl.getShaderParameter( shader, gl.COMPILE_STATUS ) === false ) { console.error( 'WebGLShader: Shader couldn\'t compile.' ); } if ( gl.getShaderInfoLog( shader ) !== '' ) { let info = gl.getShaderInfoLog( shader ); // workaround for https://github.com/mrdoob/three.js/issues/9716 if (info.indexOf('GL_ARB_gpu_shader5') === -1) { console.warn( 'WebGLShader: gl.getShaderInfoLog()', type === gl.VERTEX_SHADER ? 'vertex' : 'fragment', info, string ); } } return shader; } /** * @author mrdoob / http://mrdoob.com/ */ let programIdCount = 0; function generateExtensions( extensions, parameters, rendererExtensions ) { extensions = extensions || {}; let chunks = [ ( extensions.fragDepth ) && rendererExtensions.get( 'EXT_frag_depth' ) ? '#extension GL_EXT_frag_depth : enable' : '', ]; return chunks.join( '\n' ); } function fetchAttributeLocations( gl, program, identifiers ) { let attributes = {}; let n = gl.getProgramParameter( program, gl.ACTIVE_ATTRIBUTES ); for ( let i = 0; i < n; i ++ ) { let info = gl.getActiveAttrib( program, i ); let name = info.name; // console.log("WebGLProgram: ACTIVE VERTEX ATTRIBUTE:", name, i ); attributes[name] = gl.getAttribLocation( program, name ); } return attributes; } function WebGLProgram( renderer, code, material, parameters ) { let gl = renderer.context; let extensions = material.extensions; let vertexShader = material.__webglShader.vertexShader; let fragmentShader = material.__webglShader.fragmentShader; // console.log( 'building new program ' ); // let customExtensions = generateExtensions( extensions, parameters, renderer.extensions ); // let program = gl.createProgram(); let prefixVertex, prefixFragment; prefixVertex = [ 'precision ' + parameters.precision + ' float;', 'precision ' + parameters.precision + ' int;', '#define SHADER_NAME ' + material.__webglShader.name, 'uniform mat4 modelMatrix;', 'uniform mat4 modelViewMatrix;', 'uniform mat4 projectionMatrix;', 'uniform mat4 viewMatrix;', 'attribute vec3 position;', '', ].join( '\n' ); prefixFragment = [ customExtensions, 'precision ' + parameters.precision + ' float;', 'precision ' + parameters.precision + ' int;', '#define SHADER_NAME ' + material.__webglShader.name, ( parameters.useFog && parameters.fog ) ? '#define USE_FOG' : '', '', ].join( '\n' ); let vertexGlsl = prefixVertex + vertexShader; let fragmentGlsl = prefixFragment + fragmentShader; // console.log( '*VERTEX*', vertexGlsl ); // console.log( '*FRAGMENT*', fragmentGlsl ); let glVertexShader = WebGLShader( gl, gl.VERTEX_SHADER, vertexGlsl ); let glFragmentShader = WebGLShader( gl, gl.FRAGMENT_SHADER, fragmentGlsl ); gl.atta