Просмотр исходного кода

Merge pull request #17872 from gkjohnson/linesegments2-raycast

Examples: Add raycast support for LineSegments2
Mr.doob 5 лет назад
Родитель
Сommit
6a21666565
2 измененных файлов с 287 добавлено и 3 удалено
  1. 141 1
      examples/js/lines/LineSegments2.js
  2. 146 2
      examples/jsm/lines/LineSegments2.js

+ 141 - 1
examples/js/lines/LineSegments2.js

@@ -52,6 +52,146 @@ THREE.LineSegments2.prototype = Object.assign( Object.create( THREE.Mesh.prototy
 
 		};
 
-	}() )
+	}() ),
+
+	raycast: ( function () {
+
+		var start = new THREE.Vector4();
+		var end = new THREE.Vector4();
+
+		var ssOrigin = new THREE.Vector4();
+		var ssOrigin3 = new THREE.Vector3();
+		var mvMatrix = new THREE.Matrix4();
+		var line = new THREE.Line3();
+		var closestPoint = new THREE.Vector3();
+
+		return function raycast( raycaster, intersects ) {
+
+			if ( raycaster.camera === null ) {
+
+				console.error( 'LineSegments2: "Raycaster.camera" needs to be set in order to raycast against LineSegments2.' );
+
+			}
+
+			var ray = raycaster.ray;
+			var camera = raycaster.camera;
+			var projectionMatrix = camera.projectionMatrix;
+
+			var geometry = this.geometry;
+			var material = this.material;
+			var resolution = material.resolution;
+			var lineWidth = material.linewidth;
+
+			var instanceStart = geometry.attributes.instanceStart;
+			var instanceEnd = geometry.attributes.instanceEnd;
+
+			// pick a point 1 unit out along the ray to avoid the ray origin
+			// sitting at the camera origin which will cause "w" to be 0 when
+			// applying the projection matrix.
+			ray.at( 1, ssOrigin );
+
+			// ndc space [ - 1.0, 1.0 ]
+			ssOrigin.w = 1;
+			ssOrigin.applyMatrix4( camera.matrixWorldInverse );
+			ssOrigin.applyMatrix4( projectionMatrix );
+			ssOrigin.multiplyScalar( 1 / ssOrigin.w );
+
+			// screen space
+			ssOrigin.x *= resolution.x / 2;
+			ssOrigin.y *= resolution.y / 2;
+			ssOrigin.z = 0;
+
+			ssOrigin3.copy( ssOrigin );
+
+			var matrixWorld = this.matrixWorld;
+			mvMatrix.multiplyMatrices( camera.matrixWorldInverse, matrixWorld );
+
+			for ( var i = 0, l = instanceStart.count; i < l; i ++ ) {
+
+				start.fromBufferAttribute( instanceStart, i );
+				end.fromBufferAttribute( instanceEnd, i );
+
+				start.w = 1;
+				end.w = 1;
+
+				// camera space
+				start.applyMatrix4( mvMatrix );
+				end.applyMatrix4( mvMatrix );
+
+				// clip space
+				start.applyMatrix4( projectionMatrix );
+				end.applyMatrix4( projectionMatrix );
+
+				// ndc space [ - 1.0, 1.0 ]
+				start.multiplyScalar( 1 / start.w );
+				end.multiplyScalar( 1 / end.w );
+
+				// skip the segment if it's outside the camera near and far planes
+				var isBehindCameraNear = start.z < - 1 && end.z < - 1;
+				var isPastCameraFar = start.z > 1 && end.z > 1;
+				if ( isBehindCameraNear || isPastCameraFar ) {
+
+					continue;
+
+				}
+
+				// screen space
+				start.x *= resolution.x / 2;
+				start.y *= resolution.y / 2;
+
+				end.x *= resolution.x / 2;
+				end.y *= resolution.y / 2;
+
+				// create 2d segment
+				line.start.copy( start );
+				line.start.z = 0;
+
+				line.end.copy( end );
+				line.end.z = 0;
+
+				// get closest point on ray to segment
+				var param = line.closestPointToPointParameter( ssOrigin3, true );
+				line.at( param, closestPoint );
+
+				// check if the intersection point is within clip space
+				var zPos = THREE.Math.lerp( start.z, end.z, param );
+				var isInClipSpace = zPos >= -1 && zPos <= 1;
+
+				var isInside = ssOrigin3.distanceTo( closestPoint ) < lineWidth * 0.5;
+
+				if ( isInClipSpace && isInside ) {
+
+					line.start.fromBufferAttribute( instanceStart, i );
+					line.end.fromBufferAttribute( instanceEnd, i );
+
+					line.start.applyMatrix4( matrixWorld );
+					line.end.applyMatrix4( matrixWorld );
+
+					var pointOnLine = new THREE.Vector3();
+					var point = new THREE.Vector3();
+
+					ray.distanceSqToSegment( line.start, line.end, point, pointOnLine );
+
+					intersects.push( {
+
+						point: point,
+						pointOnLine: pointOnLine,
+						distance: ray.origin.distanceTo( point ),
+
+						object: this,
+						face: null,
+						faceIndex: i,
+						uv: null,
+						uv2: null,
+
+					} );
+
+				}
+
+			}
+
+		}
+
+	} () )
 
 } );

+ 146 - 2
examples/jsm/lines/LineSegments2.js

@@ -6,8 +6,12 @@
 import {
 	InstancedInterleavedBuffer,
 	InterleavedBufferAttribute,
+	Line3,
+	Math as _Math,
+	Matrix4,
 	Mesh,
-	Vector3
+	Vector3,
+	Vector4
 } from "../../../build/three.module.js";
 import { LineSegmentsGeometry } from "../lines/LineSegmentsGeometry.js";
 import { LineMaterial } from "../lines/LineMaterial.js";
@@ -61,7 +65,147 @@ LineSegments2.prototype = Object.assign( Object.create( Mesh.prototype ), {
 
 		};
 
-	}() )
+	}() ),
+
+	raycast: ( function () {
+
+		var start = new Vector4();
+		var end = new Vector4();
+
+		var ssOrigin = new Vector4();
+		var ssOrigin3 = new Vector3();
+		var mvMatrix = new Matrix4();
+		var line = new Line3();
+		var closestPoint = new Vector3();
+
+		return function raycast( raycaster, intersects ) {
+
+			if ( raycaster.camera === null ) {
+
+				console.error( 'LineSegments2: "Raycaster.camera" needs to be set in order to raycast against LineSegments2.' );
+
+			}
+
+			var ray = raycaster.ray;
+			var camera = raycaster.camera;
+			var projectionMatrix = camera.projectionMatrix;
+
+			var geometry = this.geometry;
+			var material = this.material;
+			var resolution = material.resolution;
+			var lineWidth = material.linewidth;
+
+			var instanceStart = geometry.attributes.instanceStart;
+			var instanceEnd = geometry.attributes.instanceEnd;
+
+			// pick a point 1 unit out along the ray to avoid the ray origin
+			// sitting at the camera origin which will cause "w" to be 0 when
+			// applying the projection matrix.
+			ray.at( 1, ssOrigin );
+
+			// ndc space [ - 1.0, 1.0 ]
+			ssOrigin.w = 1;
+			ssOrigin.applyMatrix4( camera.matrixWorldInverse );
+			ssOrigin.applyMatrix4( projectionMatrix );
+			ssOrigin.multiplyScalar( 1 / ssOrigin.w );
+
+			// screen space
+			ssOrigin.x *= resolution.x / 2;
+			ssOrigin.y *= resolution.y / 2;
+			ssOrigin.z = 0;
+
+			ssOrigin3.copy( ssOrigin );
+
+			var matrixWorld = this.matrixWorld;
+			mvMatrix.multiplyMatrices( camera.matrixWorldInverse, matrixWorld );
+
+			for ( var i = 0, l = instanceStart.count; i < l; i ++ ) {
+
+				start.fromBufferAttribute( instanceStart, i );
+				end.fromBufferAttribute( instanceEnd, i );
+
+				start.w = 1;
+				end.w = 1;
+
+				// camera space
+				start.applyMatrix4( mvMatrix );
+				end.applyMatrix4( mvMatrix );
+
+				// clip space
+				start.applyMatrix4( projectionMatrix );
+				end.applyMatrix4( projectionMatrix );
+
+				// ndc space [ - 1.0, 1.0 ]
+				start.multiplyScalar( 1 / start.w );
+				end.multiplyScalar( 1 / end.w );
+
+				// skip the segment if it's outside the camera near and far planes
+				var isBehindCameraNear = start.z < - 1 && end.z < - 1;
+				var isPastCameraFar = start.z > 1 && end.z > 1;
+				if ( isBehindCameraNear || isPastCameraFar ) {
+
+					continue;
+
+				}
+
+				// screen space
+				start.x *= resolution.x / 2;
+				start.y *= resolution.y / 2;
+
+				end.x *= resolution.x / 2;
+				end.y *= resolution.y / 2;
+
+				// create 2d segment
+				line.start.copy( start );
+				line.start.z = 0;
+
+				line.end.copy( end );
+				line.end.z = 0;
+
+				// get closest point on ray to segment
+				var param = line.closestPointToPointParameter( ssOrigin3, true );
+				line.at( param, closestPoint );
+
+				// check if the intersection point is within clip space
+				var zPos = _Math.lerp( start.z, end.z, param );
+				var isInClipSpace = zPos >= -1 && zPos <= 1;
+
+				var isInside = ssOrigin3.distanceTo( closestPoint ) < lineWidth * 0.5;
+
+				if ( isInClipSpace && isInside ) {
+
+					line.start.fromBufferAttribute( instanceStart, i );
+					line.end.fromBufferAttribute( instanceEnd, i );
+
+					line.start.applyMatrix4( matrixWorld );
+					line.end.applyMatrix4( matrixWorld );
+
+					var pointOnLine = new Vector3();
+					var point = new Vector3();
+
+					ray.distanceSqToSegment( line.start, line.end, point, pointOnLine );
+
+					intersects.push( {
+
+						point: point,
+						pointOnLine: pointOnLine,
+						distance: ray.origin.distanceTo( point ),
+
+						object: this,
+						face: null,
+						faceIndex: i,
+						uv: null,
+						uv2: null,
+
+					} );
+
+				}
+
+			}
+
+		}
+
+	} () )
 
 } );