Browse Source

Add support for dashed lines

WestLangley 7 years ago
parent
commit
b67e37fa72

+ 99 - 2
examples/js/lines/LineMaterial.js

@@ -4,6 +4,10 @@
  * parameters = {
  *  color: <hex>,
  *  linewidth: <float>,
+ *  dashed: <boolean>,
+ *  dashScale: <float>,
+ *  dashSize: <float>,
+ *  gapSize: <float>,
  *  resolution: <Vector2>, // to be set by renderer
  * }
  */
@@ -11,7 +15,10 @@
 THREE.UniformsLib.line = {
 
 	linewidth: { value: 1 },
-	resolution: { value: new THREE.Vector2( 1, 1 ) }
+	resolution: { value: new THREE.Vector2( 1, 1 ) },
+	dashScale: { value: 1 },
+	dashSize: { value: 1 },
+	gapSize: { value: 1 } // todo FIX - maybe change to totalSize
 
 };
 
@@ -42,6 +49,15 @@ THREE.ShaderLib[ 'line' ] = {
 
 		varying vec2 vUv;
 
+		#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
@@ -60,7 +76,15 @@ THREE.ShaderLib[ 'line' ] = {
 		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;
+
 			#endif
 
 			float aspect = resolution.x / resolution.y;
@@ -158,18 +182,35 @@ THREE.ShaderLib[ 'line' ] = {
 		uniform vec3 diffuse;
 		uniform float opacity;
 
+		#ifdef USE_DASH
+
+			uniform float dashSize;
+			uniform float gapSize;
+
+		#endif
+
+		varying float vLineDistance;
+
 		#include <common>
 		#include <color_pars_fragment>
 		#include <fog_pars_fragment>
 		#include <logdepthbuf_pars_fragment>
 		#include <clipping_planes_pars_fragment>
 
-			varying vec2 vUv;
+		varying vec2 vUv;
 
 		void main() {
 
 			#include <clipping_planes_fragment>
 
+			#ifdef USE_DASH
+
+				if ( vUv.y < 0.5 || vUv.y > 0.5 ) discard; // discard endcaps
+
+				if ( mod( vLineDistance, dashSize + gapSize ) > dashSize ) discard; // todo - FIX
+
+			#endif
+
 			if ( vUv.y < 0.5 || vUv.y > 0.5 ) {
 
 				float a = vUv.x - 0.5;
@@ -209,6 +250,8 @@ THREE.LineMaterial = function ( parameters ) {
 
 	} );
 
+	this.dashed = false;
+
 	Object.defineProperties( this, {
 
 		color: {
@@ -247,6 +290,60 @@ THREE.LineMaterial = function ( parameters ) {
 
 		},
 
+		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;
+
+			}
+
+		},
+
+		gapSize: {
+
+			enumerable: true,
+
+			get: function () {
+
+				return this.uniforms.gapSize.value;
+
+			},
+
+			set: function ( value ) {
+
+				this.uniforms.gapSize.value = value;
+
+			}
+
+		},
+
 		resolution: {
 
 			enumerable: true,

+ 34 - 0
examples/js/lines/LineSegments2.js

@@ -20,6 +20,40 @@ THREE.LineSegments2.prototype = Object.assign( Object.create( THREE.Mesh.prototy
 
 	isLineSegments2: true,
 
+	computeLineDistances: ( function () { // for backwards-compatability, but could be a method of LineSegmentsGeometry...
+
+		var start = new THREE.Vector3();
+		var end = new THREE.Vector3();
+
+		return function computeLineDistances() {
+
+			var geometry = this.geometry;
+
+			var instanceStart = geometry.attributes.instanceStart;
+			var instanceEnd = geometry.attributes.instanceEnd;
+			var lineDistances = new Float32Array( 2 * instanceStart.data.count );
+
+			for ( var i = 0, j = 0, l = instanceStart.data.count; i < l; i ++, j += 2 ) {
+
+				start.fromBufferAttribute( instanceStart, i );
+				end.fromBufferAttribute( instanceEnd, i );
+
+				lineDistances[ j ] = ( j === 0 ) ? 0 : lineDistances[ j - 1 ];
+				lineDistances[ j + 1 ] = lineDistances[ j ] + start.distanceTo( end );
+
+			}
+
+			var instanceDistanceBuffer = new THREE.InstancedInterleavedBuffer( lineDistances, 2, 1 ); // d0, d1
+
+			geometry.addAttribute( 'instanceDistanceStart', new THREE.InterleavedBufferAttribute( instanceDistanceBuffer, 1, 0 ) ); // d0
+			geometry.addAttribute( 'instanceDistanceEnd', new THREE.InterleavedBufferAttribute( instanceDistanceBuffer, 1, 1 ) ); // d1
+
+			return this;
+
+		};
+
+	}() ),
+
 	copy: function ( source ) {
 
 		// todo

+ 34 - 0
examples/js/lines/Wireframe.js

@@ -20,6 +20,40 @@ THREE.Wireframe.prototype = Object.assign( Object.create( THREE.Mesh.prototype )
 
 	isWireframe: true,
 
+	computeLineDistances: ( function () { // for backwards-compatability, but could be a method of LineSegmentsGeometry...
+
+		var start = new THREE.Vector3();
+		var end = new THREE.Vector3();
+
+		return function computeLineDistances() {
+
+			var geometry = this.geometry;
+
+			var instanceStart = geometry.attributes.instanceStart;
+			var instanceEnd = geometry.attributes.instanceEnd;
+			var lineDistances = new Float32Array( 2 * instanceStart.data.count );
+
+			for ( var i = 0, j = 0, l = instanceStart.data.count; i < l; i ++, j += 2 ) {
+
+				start.fromBufferAttribute( instanceStart, i );
+				end.fromBufferAttribute( instanceEnd, i );
+
+				lineDistances[ j ] = ( j === 0 ) ? 0 : lineDistances[ j - 1 ];
+				lineDistances[ j + 1 ] = lineDistances[ j ] + start.distanceTo( end );
+
+			}
+
+			var instanceDistanceBuffer = new THREE.InstancedInterleavedBuffer( lineDistances, 2, 1 ); // d0, d1
+
+			geometry.addAttribute( 'instanceDistanceStart', new THREE.InterleavedBufferAttribute( instanceDistanceBuffer, 1, 0 ) ); // d0
+			geometry.addAttribute( 'instanceDistanceEnd', new THREE.InterleavedBufferAttribute( instanceDistanceBuffer, 1, 1 ) ); // d1
+
+			return this;
+
+		};
+
+	}() ),
+
 	copy: function ( source ) {
 
 		// todo

+ 77 - 54
examples/webgl_lines_fat.html

@@ -57,8 +57,9 @@
 
 			if ( ! Detector.webgl ) Detector.addGetWebGLMessage();
 
-			var line, wireframe, renderer, scene, camera, controls;
-			var line1, wireframe1;
+			var line, renderer, scene, camera, controls;
+			var line1;
+			var matLine, matLineBasic, matLineDashed;
 			var stats;
 			var gui;
 
@@ -80,7 +81,7 @@
 				scene = new THREE.Scene();
 
 				camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 1, 1000 );
-				camera.position.set( -40, 0, 60 );
+				camera.position.set( - 40, 0, 60 );
 
 				controls = new THREE.OrbitControls( camera, renderer.domElement );
 				controls.minDistance = 10;
@@ -115,16 +116,18 @@
 				geometry.setPositions( positions );
 				geometry.setColors( colors );
 
-				var material = new THREE.LineMaterial( {
+				matLine = new THREE.LineMaterial( {
 
 					color: 0xffffff,
-					linewidth: 10, // in pixels
+					linewidth: 5, // in pixels
 					vertexColors: THREE.VertexColors,
 					//resolution:  // to be set by renderer, eventually
+					dashed: false
 
 				} );
 
-				line = new THREE.Line2( geometry, material );
+				line = new THREE.Line2( geometry, matLine );
+				line.computeLineDistances();
 				line.scale.set( 1, 1, 1 );
 				scene.add( line );
 
@@ -135,44 +138,14 @@
 				geo.addAttribute( 'position', new THREE.Float32BufferAttribute( positions, 3 ) );
 				geo.addAttribute( 'color', new THREE.Float32BufferAttribute( colors, 3 ) );
 
-				var mat = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors } );
+				matLineBasic = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors } );
+				matLineDashed = new THREE.LineDashedMaterial( { vertexColors: THREE.VertexColors, scale: 2, dashSize: 1, gapSize: 1 } );
 
-				line1 = new THREE.Line( geo, mat );
+				line1 = new THREE.Line( geo, matLineBasic );
+				line1.computeLineDistances();
 				line1.visible = false;
 				scene.add( line1 );
 
-
-				// THREE.Wireframe ( WireframeGeometry2, LineMaterial )
-
-				//var geo = new THREE.BoxBufferGeometry( 16, 16, 4, 2, 2, 1 );
-				//var geo = new THREE.IcosahedronBufferGeometry( 8, 0 );
-				var geo = new THREE.PlaneBufferGeometry( 16, 16, 2, 2 );
-
-				var geometry = new THREE.WireframeGeometry2( geo );
-
-				var material = new THREE.LineMaterial( {
-
-					color: 0x4080ff,
-					linewidth: 10, // in pixels
-					//resolution:  // to be set by renderer, eventually
-
-				} );
-
-				wireframe = new THREE.Wireframe( geometry, material );
-				wireframe.scale.set( 1, 1, 1 );
-				scene.add( wireframe );
-
-
-				// THREE.Line ( WireframeGeometry, LineBasicMaterial ) - rendered with gl.LINE
-
-				geo = new THREE.WireframeGeometry( geo );
-
-				var mat = new THREE.LineBasicMaterial( { color: 0x4080ff } );
-
-				wireframe1 = new THREE.LineSegments( geo, mat );
-				wireframe1.visible = false;
-				scene.add( wireframe1 );
-
 				//
 
 				window.addEventListener( 'resize', onWindowResize, false );
@@ -203,13 +176,10 @@
 
 				stats.update();
 
-				wireframe.geometry.maxInstancedCount = Math.floor( Date.now() / 1000 ) % wireframe.geometry.index.count - 1; // why - 1 needed ?
-
 				// main scene
 
 				// renderer will set this eventually
-				line.material.resolution.set( window.innerWidth, window.innerHeight );
-				wireframe.material.resolution.set( window.innerWidth, window.innerHeight );
+				matLine.resolution.set( window.innerWidth, window.innerHeight );
 
 				renderer.setViewport( 0, 0, window.innerWidth, window.innerHeight );
 
@@ -218,8 +188,7 @@
 				// inset scene
 
 				// renderer will set this eventually
-				//line.material.resolution.set( insetWidth, insetHeight );
-				//wireframe.material.resolution.set( insetWidth, insetHeight );
+				//matLine.resolution.set( insetWidth, insetHeight ); // not sure what behavior we want here...
 
 				renderer.clearDepth(); // important!
 
@@ -243,7 +212,10 @@
 
 				var param = {
 					'line type': 0,
-					'line width': 10
+					'width (px)': 5,
+					'dashed': false,
+					'dash scale': 1,
+					'dash / gap': 1
 				};
 
 
@@ -253,19 +225,15 @@
 
 						case '0':
 							line.visible = true;
-							wireframe.visible = true;
 
 							line1.visible = false;
-							wireframe1.visible = false;
 
 							break;
 
 						case '1':
 							line.visible = false;
-							wireframe.visible = false;
 
 							line1.visible = true;
-							wireframe1.visible = true;
 
 							break;
 
@@ -273,10 +241,65 @@
 
 				} );
 
-				gui.add( param, 'line width', 1, 40, 1 ).onChange( function ( val ) {
+				gui.add( param, 'width (px)', 1, 10, 1 ).onChange( function ( val ) {
+
+					matLine.linewidth = val;
+
+				} );
+
+				gui.add( param, 'dashed' ).onChange( function ( val ) {
+
+					matLine.dashed = val;
 
-					line.material.linewidth = val;
-					wireframe.material.linewidth = val;
+					// dashed is implemented as a defines -- not as a uniform. this could be changed.
+					// ... or LineDashedMaterial could be implemented as a separate material
+					// temporary hack - renderer should do this eventually
+					if ( val ) matLine.defines.USE_DASH = ""; else delete matLine.defines.USE_DASH;
+					matLine.needsUpdate = true;
+
+					line1.material = val ? matLineDashed : matLineBasic;
+
+				} );
+
+				gui.add( param, 'dash scale', 0.5, 2, 0.1 ).onChange( function ( val ) {
+
+					matLine.dashScale = val;
+					matLineDashed.scale = val;
+
+				} );
+
+				gui.add( param, 'dash / gap', { '2 : 1': 0, '1 : 1': 1, '1 : 2': 2 } ).onChange( function ( val ) {
+
+					switch ( val ) {
+
+						case '0':
+							matLine.dashSize = 2;
+							matLine.gapSize = 1;
+
+							matLineDashed.dashSize = 2;
+							matLineDashed.gapSize = 1;
+
+							break;
+
+						case '1':
+							matLine.dashSize = 1;
+							matLine.gapSize = 1;
+
+							matLineDashed.dashSize = 1;
+							matLineDashed.gapSize = 1;
+
+							break;
+
+						case '2':
+							matLine.dashSize = 1;
+							matLine.gapSize = 2;
+
+							matLineDashed.dashSize = 1;
+							matLineDashed.gapSize = 2;
+
+							break;
+
+					}
 
 				} );