diff --git a/examples/js/lines/LineSegments2.js b/examples/js/lines/LineSegments2.js index cd2a341af7d9a104139f6db984dc7edbb9696f46..08c8b02cf268f62008832b591af3d8c8a6bacf10 100644 --- a/examples/js/lines/LineSegments2.js +++ b/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 ++ ) { diff --git a/examples/jsm/lines/LineSegments2.js b/examples/jsm/lines/LineSegments2.js index b735d2c778462624b0055880875e73b0c50f7c21..6240e7867e22eb5dc782bfaab8c3078f62adbf76 100644 --- a/examples/jsm/lines/LineSegments2.js +++ b/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 ++ ) {