| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643 | ( function () {	/** * parameters = { *  color: <hex>, *  linewidth: <float>, *  dashed: <boolean>, *  dashScale: <float>, *  dashSize: <float>, *  dashOffset: <float>, *  gapSize: <float>, *  resolution: <Vector2>, // to be set by renderer * } */	THREE.UniformsLib.line = {		worldUnits: {			value: 1		},		linewidth: {			value: 1		},		resolution: {			value: new THREE.Vector2( 1, 1 )		},		dashOffset: {			value: 0		},		dashScale: {			value: 1		},		dashSize: {			value: 1		},		gapSize: {			value: 1		} // todo FIX - maybe change to totalSize	};	THREE.ShaderLib[ 'line' ] = {		uniforms: THREE.UniformsUtils.merge( [ THREE.UniformsLib.common, THREE.UniformsLib.fog, THREE.UniformsLib.line ] ),		vertexShader:  /* glsl */  `		#include <common>		#include <color_pars_vertex>		#include <fog_pars_vertex>		#include <logdepthbuf_pars_vertex>		#include <clipping_planes_pars_vertex>		uniform float linewidth;		uniform vec2 resolution;		attribute vec3 instanceStart;		attribute vec3 instanceEnd;		attribute vec3 instanceColorStart;		attribute vec3 instanceColorEnd;		#ifdef WORLD_UNITS			varying vec4 worldPos;			varying vec3 worldStart;			varying vec3 worldEnd;			#ifdef USE_DASH				varying vec2 vUv;			#endif		#else			varying vec2 vUv;		#endif		#ifdef USE_DASH			uniform float dashScale;			attribute float instanceDistanceStart;			attribute float instanceDistanceEnd;			varying float vLineDistance;		#endif		void trimSegment( const in vec4 start, inout vec4 end ) {			// trim end segment so it terminates between the camera plane and the near plane			// conservative estimate of the near plane			float a = projectionMatrix[ 2 ][ 2 ]; // 3nd entry in 3th column			float b = projectionMatrix[ 3 ][ 2 ]; // 3nd entry in 4th column			float nearEstimate = - 0.5 * b / a;			float alpha = ( nearEstimate - start.z ) / ( end.z - start.z );			end.xyz = mix( start.xyz, end.xyz, alpha );		}		void main() {			#ifdef USE_COLOR				vColor.xyz = ( position.y < 0.5 ) ? instanceColorStart : instanceColorEnd;			#endif			#ifdef USE_DASH				vLineDistance = ( position.y < 0.5 ) ? dashScale * instanceDistanceStart : dashScale * instanceDistanceEnd;				vUv = uv;			#endif			float aspect = resolution.x / resolution.y;			// camera space			vec4 start = modelViewMatrix * vec4( instanceStart, 1.0 );			vec4 end = modelViewMatrix * vec4( instanceEnd, 1.0 );			#ifdef WORLD_UNITS				worldStart = start.xyz;				worldEnd = end.xyz;			#else				vUv = uv;			#endif			// special case for perspective projection, and segments that terminate either in, or behind, the camera plane			// clearly the gpu firmware has a way of addressing this issue when projecting into ndc space			// but we need to perform ndc-space calculations in the shader, so we must address this issue directly			// perhaps there is a more elegant solution -- WestLangley			bool perspective = ( projectionMatrix[ 2 ][ 3 ] == - 1.0 ); // 4th entry in the 3rd column			if ( perspective ) {				if ( start.z < 0.0 && end.z >= 0.0 ) {					trimSegment( start, end );				} else if ( end.z < 0.0 && start.z >= 0.0 ) {					trimSegment( end, start );				}			}			// clip space			vec4 clipStart = projectionMatrix * start;			vec4 clipEnd = projectionMatrix * end;			// ndc space			vec3 ndcStart = clipStart.xyz / clipStart.w;			vec3 ndcEnd = clipEnd.xyz / clipEnd.w;			// direction			vec2 dir = ndcEnd.xy - ndcStart.xy;			// account for clip-space aspect ratio			dir.x *= aspect;			dir = normalize( dir );			#ifdef WORLD_UNITS				// get the offset direction as perpendicular to the view vector				vec3 worldDir = normalize( end.xyz - start.xyz );				vec3 offset;				if ( position.y < 0.5 ) {					offset = normalize( cross( start.xyz, worldDir ) );				} else {					offset = normalize( cross( end.xyz, worldDir ) );				}				// sign flip				if ( position.x < 0.0 ) offset *= - 1.0;				float forwardOffset = dot( worldDir, vec3( 0.0, 0.0, 1.0 ) );				// don't extend the line if we're rendering dashes because we				// won't be rendering the endcaps				#ifndef USE_DASH					// extend the line bounds to encompass  endcaps					start.xyz += - worldDir * linewidth * 0.5;					end.xyz += worldDir * linewidth * 0.5;					// shift the position of the quad so it hugs the forward edge of the line					offset.xy -= dir * forwardOffset;					offset.z += 0.5;				#endif				// endcaps				if ( position.y > 1.0 || position.y < 0.0 ) {					offset.xy += dir * 2.0 * forwardOffset;				}				// adjust for linewidth				offset *= linewidth * 0.5;				// set the world position				worldPos = ( position.y < 0.5 ) ? start : end;				worldPos.xyz += offset;				// project the worldpos				vec4 clip = projectionMatrix * worldPos;				// shift the depth of the projected points so the line				// segements overlap neatly				vec3 clipPose = ( position.y < 0.5 ) ? ndcStart : ndcEnd;				clip.z = clipPose.z * clip.w;			#else				vec2 offset = vec2( dir.y, - dir.x );				// undo aspect ratio adjustment				dir.x /= aspect;				offset.x /= aspect;				// sign flip				if ( position.x < 0.0 ) offset *= - 1.0;				// endcaps				if ( position.y < 0.0 ) {					offset += - dir;				} else if ( position.y > 1.0 ) {					offset += dir;				}				// adjust for linewidth				offset *= linewidth;				// adjust for clip-space to screen-space conversion // maybe resolution should be based on viewport ...				offset /= resolution.y;				// select end				vec4 clip = ( position.y < 0.5 ) ? clipStart : clipEnd;				// back to clip space				offset *= clip.w;				clip.xy += offset;			#endif			gl_Position = clip;			vec4 mvPosition = ( position.y < 0.5 ) ? start : end; // this is an approximation			#include <logdepthbuf_vertex>			#include <clipping_planes_vertex>			#include <fog_vertex>		}		`,		fragmentShader:  /* glsl */  `		uniform vec3 diffuse;		uniform float opacity;		uniform float linewidth;		#ifdef USE_DASH			uniform float dashOffset;			uniform float dashSize;			uniform float gapSize;		#endif		varying float vLineDistance;		#ifdef WORLD_UNITS			varying vec4 worldPos;			varying vec3 worldStart;			varying vec3 worldEnd;			#ifdef USE_DASH				varying vec2 vUv;			#endif		#else			varying vec2 vUv;		#endif		#include <common>		#include <color_pars_fragment>		#include <fog_pars_fragment>		#include <logdepthbuf_pars_fragment>		#include <clipping_planes_pars_fragment>		vec2 closestLineToLine(vec3 p1, vec3 p2, vec3 p3, vec3 p4) {			float mua;			float mub;			vec3 p13 = p1 - p3;			vec3 p43 = p4 - p3;			vec3 p21 = p2 - p1;			float d1343 = dot( p13, p43 );			float d4321 = dot( p43, p21 );			float d1321 = dot( p13, p21 );			float d4343 = dot( p43, p43 );			float d2121 = dot( p21, p21 );			float denom = d2121 * d4343 - d4321 * d4321;			float numer = d1343 * d4321 - d1321 * d4343;			mua = numer / denom;			mua = clamp( mua, 0.0, 1.0 );			mub = ( d1343 + d4321 * ( mua ) ) / d4343;			mub = clamp( mub, 0.0, 1.0 );			return vec2( mua, mub );		}		void main() {			#include <clipping_planes_fragment>			#ifdef USE_DASH				if ( vUv.y < - 1.0 || vUv.y > 1.0 ) discard; // discard endcaps				if ( mod( vLineDistance + dashOffset, dashSize + gapSize ) > dashSize ) discard; // todo - FIX			#endif			float alpha = opacity;			#ifdef WORLD_UNITS				// Find the closest points on the view ray and the line segment				vec3 rayEnd = normalize( worldPos.xyz ) * 1e5;				vec3 lineDir = worldEnd - worldStart;				vec2 params = closestLineToLine( worldStart, worldEnd, vec3( 0.0, 0.0, 0.0 ), rayEnd );				vec3 p1 = worldStart + lineDir * params.x;				vec3 p2 = rayEnd * params.y;				vec3 delta = p1 - p2;				float len = length( delta );				float norm = len / linewidth;				#ifndef USE_DASH					#ifdef USE_ALPHA_TO_COVERAGE						float dnorm = fwidth( norm );						alpha = 1.0 - smoothstep( 0.5 - dnorm, 0.5 + dnorm, norm );					#else						if ( norm > 0.5 ) {							discard;						}					#endif				#endif			#else				#ifdef USE_ALPHA_TO_COVERAGE					// artifacts appear on some hardware if a derivative is taken within a conditional					float a = vUv.x;					float b = ( vUv.y > 0.0 ) ? vUv.y - 1.0 : vUv.y + 1.0;					float len2 = a * a + b * b;					float dlen = fwidth( len2 );					if ( abs( vUv.y ) > 1.0 ) {						alpha = 1.0 - smoothstep( 1.0 - dlen, 1.0 + dlen, len2 );					}				#else					if ( abs( vUv.y ) > 1.0 ) {						float a = vUv.x;						float b = ( vUv.y > 0.0 ) ? vUv.y - 1.0 : vUv.y + 1.0;						float len2 = a * a + b * b;						if ( len2 > 1.0 ) discard;					}				#endif			#endif			vec4 diffuseColor = vec4( diffuse, alpha );			#include <logdepthbuf_fragment>			#include <color_fragment>			gl_FragColor = vec4( diffuseColor.rgb, alpha );			#include <tonemapping_fragment>			#include <encodings_fragment>			#include <fog_fragment>			#include <premultiplied_alpha_fragment>		}		`	};	class LineMaterial extends THREE.ShaderMaterial {		constructor( parameters ) {			super( {				type: 'LineMaterial',				uniforms: THREE.UniformsUtils.clone( THREE.ShaderLib[ 'line' ].uniforms ),				vertexShader: THREE.ShaderLib[ 'line' ].vertexShader,				fragmentShader: THREE.ShaderLib[ 'line' ].fragmentShader,				clipping: true // required for clipping support			} );			Object.defineProperties( this, {				color: {					enumerable: true,					get: function () {						return this.uniforms.diffuse.value;					},					set: function ( value ) {						this.uniforms.diffuse.value = value;					}				},				worldUnits: {					enumerable: true,					get: function () {						return 'WORLD_UNITS' in this.defines;					},					set: function ( value ) {						if ( value === true ) {							this.defines.WORLD_UNITS = '';						} else {							delete this.defines.WORLD_UNITS;						}					}				},				linewidth: {					enumerable: true,					get: function () {						return this.uniforms.linewidth.value;					},					set: function ( value ) {						this.uniforms.linewidth.value = value;					}				},				dashed: {					enumerable: true,					get: function () {						return Boolean( 'USE_DASH' in this.defines );					},					set( value ) {						if ( Boolean( value ) !== Boolean( 'USE_DASH' in this.defines ) ) {							this.needsUpdate = true;						}						if ( value === true ) {							this.defines.USE_DASH = '';						} else {							delete this.defines.USE_DASH;						}					}				},				dashScale: {					enumerable: true,					get: function () {						return this.uniforms.dashScale.value;					},					set: function ( value ) {						this.uniforms.dashScale.value = value;					}				},				dashSize: {					enumerable: true,					get: function () {						return this.uniforms.dashSize.value;					},					set: function ( value ) {						this.uniforms.dashSize.value = value;					}				},				dashOffset: {					enumerable: true,					get: function () {						return this.uniforms.dashOffset.value;					},					set: function ( value ) {						this.uniforms.dashOffset.value = value;					}				},				gapSize: {					enumerable: true,					get: function () {						return this.uniforms.gapSize.value;					},					set: function ( value ) {						this.uniforms.gapSize.value = value;					}				},				opacity: {					enumerable: true,					get: function () {						return this.uniforms.opacity.value;					},					set: function ( value ) {						this.uniforms.opacity.value = value;					}				},				resolution: {					enumerable: true,					get: function () {						return this.uniforms.resolution.value;					},					set: function ( value ) {						this.uniforms.resolution.value.copy( value );					}				},				alphaToCoverage: {					enumerable: true,					get: function () {						return Boolean( 'USE_ALPHA_TO_COVERAGE' in this.defines );					},					set: function ( value ) {						if ( Boolean( value ) !== Boolean( 'USE_ALPHA_TO_COVERAGE' in this.defines ) ) {							this.needsUpdate = true;						}						if ( value === true ) {							this.defines.USE_ALPHA_TO_COVERAGE = '';							this.extensions.derivatives = true;						} else {							delete this.defines.USE_ALPHA_TO_COVERAGE;							this.extensions.derivatives = false;						}					}				}			} );			this.setValues( parameters );		}	}	LineMaterial.prototype.isLineMaterial = true;	THREE.LineMaterial = LineMaterial;} )();
 |