Skip to content

Commit efef2b3

Browse files
authored
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
1 parent 9eeefa6 commit efef2b3

File tree

2 files changed

+142
-2
lines changed

2 files changed

+142
-2
lines changed

examples/js/lines/LineSegments2.js

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@ THREE.LineSegments2.prototype = Object.assign( Object.create( THREE.Mesh.prototy
6060
var line = new THREE.Line3();
6161
var closestPoint = new THREE.Vector3();
6262

63+
var box = new THREE.Box3();
64+
var sphere = new THREE.Sphere();
65+
var clipToWorldVector = new THREE.Vector4();
66+
6367
return function raycast( raycaster, intersects ) {
6468

6569
if ( raycaster.camera === null ) {
@@ -74,6 +78,7 @@ THREE.LineSegments2.prototype = Object.assign( Object.create( THREE.Mesh.prototy
7478
var camera = raycaster.camera;
7579
var projectionMatrix = camera.projectionMatrix;
7680

81+
var matrixWorld = this.matrixWorld;
7782
var geometry = this.geometry;
7883
var material = this.material;
7984
var resolution = material.resolution;
@@ -85,6 +90,71 @@ THREE.LineSegments2.prototype = Object.assign( Object.create( THREE.Mesh.prototy
8590
// camera forward is negative
8691
var near = - camera.near;
8792

93+
// clip space is [ - 1, 1 ] so multiply by two to get the full
94+
// width in clip space
95+
var ssMaxWidth = 2.0 * Math.max( lineWidth / resolution.width, lineWidth / resolution.height );
96+
97+
//
98+
99+
// check if we intersect the sphere bounds
100+
if ( geometry.boundingSphere === null ) {
101+
102+
geometry.computeBoundingSphere();
103+
104+
}
105+
106+
sphere.copy( geometry.boundingSphere ).applyMatrix4( matrixWorld );
107+
var distanceToSphere = Math.max( camera.near, sphere.distanceToPoint( ray.origin ) );
108+
109+
// get the w component to scale the world space line width
110+
clipToWorldVector.set( 0, 0, - distanceToSphere, 1.0 ).applyMatrix4( camera.projectionMatrix );
111+
clipToWorldVector.multiplyScalar( 1.0 / clipToWorldVector.w );
112+
clipToWorldVector.applyMatrix4( camera.projectionMatrixInverse );
113+
114+
// increase the sphere bounds by the worst case line screen space width
115+
var sphereMargin = Math.abs( ssMaxWidth / clipToWorldVector.w ) * 0.5;
116+
sphere.radius += sphereMargin;
117+
118+
if ( raycaster.ray.intersectsSphere( sphere ) === false ) {
119+
120+
return;
121+
122+
}
123+
124+
//
125+
126+
// check if we intersect the box bounds
127+
if ( geometry.boundingBox === null ) {
128+
129+
geometry.computeBoundingBox();
130+
131+
}
132+
133+
box.copy( geometry.boundingBox ).applyMatrix4( matrixWorld );
134+
var distanceToBox = Math.max( camera.near, box.distanceToPoint( ray.origin ) );
135+
136+
// get the w component to scale the world space line width
137+
clipToWorldVector.set( 0, 0, - distanceToBox, 1.0 ).applyMatrix4( camera.projectionMatrix );
138+
clipToWorldVector.multiplyScalar( 1.0 / clipToWorldVector.w );
139+
clipToWorldVector.applyMatrix4( camera.projectionMatrixInverse );
140+
141+
// increase the sphere bounds by the worst case line screen space width
142+
var boxMargin = Math.abs( ssMaxWidth / clipToWorldVector.w ) * 0.5;
143+
box.max.x += boxMargin;
144+
box.max.y += boxMargin;
145+
box.max.z += boxMargin;
146+
box.min.x -= boxMargin;
147+
box.min.y -= boxMargin;
148+
box.min.z -= boxMargin;
149+
150+
if ( raycaster.ray.intersectsBox( box ) === false ) {
151+
152+
return;
153+
154+
}
155+
156+
//
157+
88158
// pick a point 1 unit out along the ray to avoid the ray origin
89159
// sitting at the camera origin which will cause "w" to be 0 when
90160
// applying the projection matrix.
@@ -103,7 +173,6 @@ THREE.LineSegments2.prototype = Object.assign( Object.create( THREE.Mesh.prototy
103173

104174
ssOrigin3.copy( ssOrigin );
105175

106-
var matrixWorld = this.matrixWorld;
107176
mvMatrix.multiplyMatrices( camera.matrixWorldInverse, matrixWorld );
108177

109178
for ( var i = 0, l = instanceStart.count; i < l; i ++ ) {

examples/jsm/lines/LineSegments2.js

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import {
2+
Box3,
23
InstancedInterleavedBuffer,
34
InterleavedBufferAttribute,
45
Line3,
56
MathUtils,
67
Matrix4,
78
Mesh,
9+
Sphere,
810
Vector3,
911
Vector4
1012
} from '../../../build/three.module.js';
@@ -73,6 +75,10 @@ LineSegments2.prototype = Object.assign( Object.create( Mesh.prototype ), {
7375
var line = new Line3();
7476
var closestPoint = new Vector3();
7577

78+
var box = new Box3();
79+
var sphere = new Sphere();
80+
var clipToWorldVector = new Vector4();
81+
7682
return function raycast( raycaster, intersects ) {
7783

7884
if ( raycaster.camera === null ) {
@@ -87,6 +93,7 @@ LineSegments2.prototype = Object.assign( Object.create( Mesh.prototype ), {
8793
var camera = raycaster.camera;
8894
var projectionMatrix = camera.projectionMatrix;
8995

96+
var matrixWorld = this.matrixWorld;
9097
var geometry = this.geometry;
9198
var material = this.material;
9299
var resolution = material.resolution;
@@ -98,6 +105,71 @@ LineSegments2.prototype = Object.assign( Object.create( Mesh.prototype ), {
98105
// camera forward is negative
99106
var near = - camera.near;
100107

108+
// clip space is [ - 1, 1 ] so multiply by two to get the full
109+
// width in clip space
110+
var ssMaxWidth = 2.0 * Math.max( lineWidth / resolution.width, lineWidth / resolution.height );
111+
112+
//
113+
114+
// check if we intersect the sphere bounds
115+
if ( geometry.boundingSphere === null ) {
116+
117+
geometry.computeBoundingSphere();
118+
119+
}
120+
121+
sphere.copy( geometry.boundingSphere ).applyMatrix4( matrixWorld );
122+
var distanceToSphere = Math.max( camera.near, sphere.distanceToPoint( ray.origin ) );
123+
124+
// get the w component to scale the world space line width
125+
clipToWorldVector.set( 0, 0, - distanceToSphere, 1.0 ).applyMatrix4( camera.projectionMatrix );
126+
clipToWorldVector.multiplyScalar( 1.0 / clipToWorldVector.w );
127+
clipToWorldVector.applyMatrix4( camera.projectionMatrixInverse );
128+
129+
// increase the sphere bounds by the worst case line screen space width
130+
var sphereMargin = Math.abs( ssMaxWidth / clipToWorldVector.w ) * 0.5;
131+
sphere.radius += sphereMargin;
132+
133+
if ( raycaster.ray.intersectsSphere( sphere ) === false ) {
134+
135+
return;
136+
137+
}
138+
139+
//
140+
141+
// check if we intersect the box bounds
142+
if ( geometry.boundingBox === null ) {
143+
144+
geometry.computeBoundingBox();
145+
146+
}
147+
148+
box.copy( geometry.boundingBox ).applyMatrix4( matrixWorld );
149+
var distanceToBox = Math.max( camera.near, box.distanceToPoint( ray.origin ) );
150+
151+
// get the w component to scale the world space line width
152+
clipToWorldVector.set( 0, 0, - distanceToBox, 1.0 ).applyMatrix4( camera.projectionMatrix );
153+
clipToWorldVector.multiplyScalar( 1.0 / clipToWorldVector.w );
154+
clipToWorldVector.applyMatrix4( camera.projectionMatrixInverse );
155+
156+
// increase the sphere bounds by the worst case line screen space width
157+
var boxMargin = Math.abs( ssMaxWidth / clipToWorldVector.w ) * 0.5;
158+
box.max.x += boxMargin;
159+
box.max.y += boxMargin;
160+
box.max.z += boxMargin;
161+
box.min.x -= boxMargin;
162+
box.min.y -= boxMargin;
163+
box.min.z -= boxMargin;
164+
165+
if ( raycaster.ray.intersectsBox( box ) === false ) {
166+
167+
return;
168+
169+
}
170+
171+
//
172+
101173
// pick a point 1 unit out along the ray to avoid the ray origin
102174
// sitting at the camera origin which will cause "w" to be 0 when
103175
// applying the projection matrix.
@@ -116,7 +188,6 @@ LineSegments2.prototype = Object.assign( Object.create( Mesh.prototype ), {
116188

117189
ssOrigin3.copy( ssOrigin );
118190

119-
var matrixWorld = this.matrixWorld;
120191
mvMatrix.multiplyMatrices( camera.matrixWorldInverse, matrixWorld );
121192

122193
for ( var i = 0, l = instanceStart.count; i < l; i ++ ) {

0 commit comments

Comments
 (0)