diff --git a/examples/js/lines/LineSegments2.js b/examples/js/lines/LineSegments2.js index fd17bb39bd2630..0c364675ac7ca8 100644 --- a/examples/js/lines/LineSegments2.js +++ b/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, + + } ); + + } + + } + + } + + } () ) } ); diff --git a/examples/jsm/lines/LineSegments2.js b/examples/jsm/lines/LineSegments2.js index 20beea38349097..c94d10f19eca9a 100644 --- a/examples/jsm/lines/LineSegments2.js +++ b/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, + + } ); + + } + + } + + } + + } () ) } );