uglymol
Version:
Macromolecular Viewer for Crystallographers
2,042 lines (1,530 loc) • 98.9 kB
JavaScript
// 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