فهرست منبع

LineSegments2: Check bounding box and bounding sphere when raycasting (#21496)

* account for bounding box and sphere in Line2 raycast

* improve thick line raycast performance

* improve comment

* fix distance computation, adjust box margin application
Garrett Johnson 4 سال پیش
والد
کامیت
efef2b3f5e
2فایلهای تغییر یافته به همراه142 افزوده شده و 2 حذف شده
  1. 70 1
      examples/js/lines/LineSegments2.js
  2. 72 1
      examples/jsm/lines/LineSegments2.js

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

@@ -60,6 +60,10 @@ THREE.LineSegments2.prototype = Object.assign( Object.create( THREE.Mesh.prototy
 		var line = new THREE.Line3();
 		var closestPoint = new THREE.Vector3();
 
+		var box = new THREE.Box3();
+		var sphere = new THREE.Sphere();
+		var clipToWorldVector = new THREE.Vector4();
+
 		return function raycast( raycaster, intersects ) {
 
 			if ( raycaster.camera === null ) {
@@ -74,6 +78,7 @@ THREE.LineSegments2.prototype = Object.assign( Object.create( THREE.Mesh.prototy
 			var camera = raycaster.camera;
 			var projectionMatrix = camera.projectionMatrix;
 
+			var matrixWorld = this.matrixWorld;
 			var geometry = this.geometry;
 			var material = this.material;
 			var resolution = material.resolution;
@@ -85,6 +90,71 @@ THREE.LineSegments2.prototype = Object.assign( Object.create( THREE.Mesh.prototy
 			// camera forward is negative
 			var near = - camera.near;
 
+			// clip space is [ - 1, 1 ] so multiply by two to get the full
+			// width in clip space
+			var ssMaxWidth = 2.0 * Math.max( lineWidth / resolution.width, lineWidth / resolution.height );
+
+			//
+
+			// check if we intersect the sphere bounds
+			if ( geometry.boundingSphere === null ) {
+
+				geometry.computeBoundingSphere();
+
+			}
+
+			sphere.copy( geometry.boundingSphere ).applyMatrix4( matrixWorld );
+			var distanceToSphere = Math.max( camera.near, sphere.distanceToPoint( ray.origin ) );
+
+			// get the w component to scale the world space line width
+			clipToWorldVector.set( 0, 0, - distanceToSphere, 1.0 ).applyMatrix4( camera.projectionMatrix );
+			clipToWorldVector.multiplyScalar( 1.0 / clipToWorldVector.w );
+			clipToWorldVector.applyMatrix4( camera.projectionMatrixInverse );
+
+			// increase the sphere bounds by the worst case line screen space width
+			var sphereMargin = Math.abs( ssMaxWidth / clipToWorldVector.w ) * 0.5;
+			sphere.radius += sphereMargin;
+
+			if ( raycaster.ray.intersectsSphere( sphere ) === false ) {
+
+				return;
+
+			}
+
+			//
+
+			// check if we intersect the box bounds
+			if ( geometry.boundingBox === null ) {
+
+				geometry.computeBoundingBox();
+
+			}
+
+			box.copy( geometry.boundingBox ).applyMatrix4( matrixWorld );
+			var distanceToBox = Math.max( camera.near, box.distanceToPoint( ray.origin ) );
+
+			// get the w component to scale the world space line width
+			clipToWorldVector.set( 0, 0, - distanceToBox, 1.0 ).applyMatrix4( camera.projectionMatrix );
+			clipToWorldVector.multiplyScalar( 1.0 / clipToWorldVector.w );
+			clipToWorldVector.applyMatrix4( camera.projectionMatrixInverse );
+
+			// increase the sphere bounds by the worst case line screen space width
+			var boxMargin = Math.abs( ssMaxWidth / clipToWorldVector.w ) * 0.5;
+			box.max.x += boxMargin;
+			box.max.y += boxMargin;
+			box.max.z += boxMargin;
+			box.min.x -= boxMargin;
+			box.min.y -= boxMargin;
+			box.min.z -= boxMargin;
+
+			if ( raycaster.ray.intersectsBox( box ) === false ) {
+
+				return;
+
+			}
+
+			//
+
 			// 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.
@@ -103,7 +173,6 @@ THREE.LineSegments2.prototype = Object.assign( Object.create( THREE.Mesh.prototy
 
 			ssOrigin3.copy( ssOrigin );
 
-			var matrixWorld = this.matrixWorld;
 			mvMatrix.multiplyMatrices( camera.matrixWorldInverse, matrixWorld );
 
 			for ( var i = 0, l = instanceStart.count; i < l; i ++ ) {

+ 72 - 1
examples/jsm/lines/LineSegments2.js

@@ -1,10 +1,12 @@
 import {
+	Box3,
 	InstancedInterleavedBuffer,
 	InterleavedBufferAttribute,
 	Line3,
 	MathUtils,
 	Matrix4,
 	Mesh,
+	Sphere,
 	Vector3,
 	Vector4
 } from '../../../build/three.module.js';
@@ -73,6 +75,10 @@ LineSegments2.prototype = Object.assign( Object.create( Mesh.prototype ), {
 		var line = new Line3();
 		var closestPoint = new Vector3();
 
+		var box = new Box3();
+		var sphere = new Sphere();
+		var clipToWorldVector = new Vector4();
+
 		return function raycast( raycaster, intersects ) {
 
 			if ( raycaster.camera === null ) {
@@ -87,6 +93,7 @@ LineSegments2.prototype = Object.assign( Object.create( Mesh.prototype ), {
 			var camera = raycaster.camera;
 			var projectionMatrix = camera.projectionMatrix;
 
+			var matrixWorld = this.matrixWorld;
 			var geometry = this.geometry;
 			var material = this.material;
 			var resolution = material.resolution;
@@ -98,6 +105,71 @@ LineSegments2.prototype = Object.assign( Object.create( Mesh.prototype ), {
 			// camera forward is negative
 			var near = - camera.near;
 
+			// clip space is [ - 1, 1 ] so multiply by two to get the full
+			// width in clip space
+			var ssMaxWidth = 2.0 * Math.max( lineWidth / resolution.width, lineWidth / resolution.height );
+
+			//
+
+			// check if we intersect the sphere bounds
+			if ( geometry.boundingSphere === null ) {
+
+				geometry.computeBoundingSphere();
+
+			}
+
+			sphere.copy( geometry.boundingSphere ).applyMatrix4( matrixWorld );
+			var distanceToSphere = Math.max( camera.near, sphere.distanceToPoint( ray.origin ) );
+
+			// get the w component to scale the world space line width
+			clipToWorldVector.set( 0, 0, - distanceToSphere, 1.0 ).applyMatrix4( camera.projectionMatrix );
+			clipToWorldVector.multiplyScalar( 1.0 / clipToWorldVector.w );
+			clipToWorldVector.applyMatrix4( camera.projectionMatrixInverse );
+
+			// increase the sphere bounds by the worst case line screen space width
+			var sphereMargin = Math.abs( ssMaxWidth / clipToWorldVector.w ) * 0.5;
+			sphere.radius += sphereMargin;
+
+			if ( raycaster.ray.intersectsSphere( sphere ) === false ) {
+
+				return;
+
+			}
+
+			//
+
+			// check if we intersect the box bounds
+			if ( geometry.boundingBox === null ) {
+
+				geometry.computeBoundingBox();
+
+			}
+
+			box.copy( geometry.boundingBox ).applyMatrix4( matrixWorld );
+			var distanceToBox = Math.max( camera.near, box.distanceToPoint( ray.origin ) );
+
+			// get the w component to scale the world space line width
+			clipToWorldVector.set( 0, 0, - distanceToBox, 1.0 ).applyMatrix4( camera.projectionMatrix );
+			clipToWorldVector.multiplyScalar( 1.0 / clipToWorldVector.w );
+			clipToWorldVector.applyMatrix4( camera.projectionMatrixInverse );
+
+			// increase the sphere bounds by the worst case line screen space width
+			var boxMargin = Math.abs( ssMaxWidth / clipToWorldVector.w ) * 0.5;
+			box.max.x += boxMargin;
+			box.max.y += boxMargin;
+			box.max.z += boxMargin;
+			box.min.x -= boxMargin;
+			box.min.y -= boxMargin;
+			box.min.z -= boxMargin;
+
+			if ( raycaster.ray.intersectsBox( box ) === false ) {
+
+				return;
+
+			}
+
+			//
+
 			// 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.
@@ -116,7 +188,6 @@ LineSegments2.prototype = Object.assign( Object.create( Mesh.prototype ), {
 
 			ssOrigin3.copy( ssOrigin );
 
-			var matrixWorld = this.matrixWorld;
 			mvMatrix.multiplyMatrices( camera.matrixWorldInverse, matrixWorld );
 
 			for ( var i = 0, l = instanceStart.count; i < l; i ++ ) {