|
6 | 6 | import { |
7 | 7 | InstancedInterleavedBuffer, |
8 | 8 | InterleavedBufferAttribute, |
| 9 | + Line3, |
| 10 | + Math as _Math, |
| 11 | + Matrix4, |
9 | 12 | Mesh, |
10 | | - Vector3 |
| 13 | + Vector3, |
| 14 | + Vector4 |
11 | 15 | } from "../../../build/three.module.js"; |
12 | 16 | import { LineSegmentsGeometry } from "../lines/LineSegmentsGeometry.js"; |
13 | 17 | import { LineMaterial } from "../lines/LineMaterial.js"; |
@@ -61,7 +65,147 @@ LineSegments2.prototype = Object.assign( Object.create( Mesh.prototype ), { |
61 | 65 |
|
62 | 66 | }; |
63 | 67 |
|
64 | | - }() ) |
| 68 | + }() ), |
| 69 | + |
| 70 | + raycast: ( function () { |
| 71 | + |
| 72 | + var start = new Vector4(); |
| 73 | + var end = new Vector4(); |
| 74 | + |
| 75 | + var ssOrigin = new Vector4(); |
| 76 | + var ssOrigin3 = new Vector3(); |
| 77 | + var mvMatrix = new Matrix4(); |
| 78 | + var line = new Line3(); |
| 79 | + var closestPoint = new Vector3(); |
| 80 | + |
| 81 | + return function raycast( raycaster, intersects ) { |
| 82 | + |
| 83 | + if ( raycaster.camera === null ) { |
| 84 | + |
| 85 | + console.error( 'LineSegments2: "Raycaster.camera" needs to be set in order to raycast against LineSegments2.' ); |
| 86 | + |
| 87 | + } |
| 88 | + |
| 89 | + var ray = raycaster.ray; |
| 90 | + var camera = raycaster.camera; |
| 91 | + var projectionMatrix = camera.projectionMatrix; |
| 92 | + |
| 93 | + var geometry = this.geometry; |
| 94 | + var material = this.material; |
| 95 | + var resolution = material.resolution; |
| 96 | + var lineWidth = material.linewidth; |
| 97 | + |
| 98 | + var instanceStart = geometry.attributes.instanceStart; |
| 99 | + var instanceEnd = geometry.attributes.instanceEnd; |
| 100 | + |
| 101 | + // pick a point 1 unit out along the ray to avoid the ray origin |
| 102 | + // sitting at the camera origin which will cause "w" to be 0 when |
| 103 | + // applying the projection matrix. |
| 104 | + ray.at( 1, ssOrigin ); |
| 105 | + |
| 106 | + // ndc space [ - 1.0, 1.0 ] |
| 107 | + ssOrigin.w = 1; |
| 108 | + ssOrigin.applyMatrix4( camera.matrixWorldInverse ); |
| 109 | + ssOrigin.applyMatrix4( projectionMatrix ); |
| 110 | + ssOrigin.multiplyScalar( 1 / ssOrigin.w ); |
| 111 | + |
| 112 | + // screen space |
| 113 | + ssOrigin.x *= resolution.x / 2; |
| 114 | + ssOrigin.y *= resolution.y / 2; |
| 115 | + ssOrigin.z = 0; |
| 116 | + |
| 117 | + ssOrigin3.copy( ssOrigin ); |
| 118 | + |
| 119 | + var matrixWorld = this.matrixWorld; |
| 120 | + mvMatrix.multiplyMatrices( camera.matrixWorldInverse, matrixWorld ); |
| 121 | + |
| 122 | + for ( var i = 0, l = instanceStart.count; i < l; i ++ ) { |
| 123 | + |
| 124 | + start.fromBufferAttribute( instanceStart, i ); |
| 125 | + end.fromBufferAttribute( instanceEnd, i ); |
| 126 | + |
| 127 | + start.w = 1; |
| 128 | + end.w = 1; |
| 129 | + |
| 130 | + // camera space |
| 131 | + start.applyMatrix4( mvMatrix ); |
| 132 | + end.applyMatrix4( mvMatrix ); |
| 133 | + |
| 134 | + // clip space |
| 135 | + start.applyMatrix4( projectionMatrix ); |
| 136 | + end.applyMatrix4( projectionMatrix ); |
| 137 | + |
| 138 | + // ndc space [ - 1.0, 1.0 ] |
| 139 | + start.multiplyScalar( 1 / start.w ); |
| 140 | + end.multiplyScalar( 1 / end.w ); |
| 141 | + |
| 142 | + // skip the segment if it's outside the camera near and far planes |
| 143 | + var isBehindCameraNear = start.z < - 1 && end.z < - 1; |
| 144 | + var isPastCameraFar = start.z > 1 && end.z > 1; |
| 145 | + if ( isBehindCameraNear || isPastCameraFar ) { |
| 146 | + |
| 147 | + continue; |
| 148 | + |
| 149 | + } |
| 150 | + |
| 151 | + // screen space |
| 152 | + start.x *= resolution.x / 2; |
| 153 | + start.y *= resolution.y / 2; |
| 154 | + |
| 155 | + end.x *= resolution.x / 2; |
| 156 | + end.y *= resolution.y / 2; |
| 157 | + |
| 158 | + // create 2d segment |
| 159 | + line.start.copy( start ); |
| 160 | + line.start.z = 0; |
| 161 | + |
| 162 | + line.end.copy( end ); |
| 163 | + line.end.z = 0; |
| 164 | + |
| 165 | + // get closest point on ray to segment |
| 166 | + var param = line.closestPointToPointParameter( ssOrigin3, true ); |
| 167 | + line.at( param, closestPoint ); |
| 168 | + |
| 169 | + // check if the intersection point is within clip space |
| 170 | + var zPos = _Math.lerp( start.z, end.z, param ); |
| 171 | + var isInClipSpace = zPos >= -1 && zPos <= 1; |
| 172 | + |
| 173 | + var isInside = ssOrigin3.distanceTo( closestPoint ) < lineWidth * 0.5; |
| 174 | + |
| 175 | + if ( isInClipSpace && isInside ) { |
| 176 | + |
| 177 | + line.start.fromBufferAttribute( instanceStart, i ); |
| 178 | + line.end.fromBufferAttribute( instanceEnd, i ); |
| 179 | + |
| 180 | + line.start.applyMatrix4( matrixWorld ); |
| 181 | + line.end.applyMatrix4( matrixWorld ); |
| 182 | + |
| 183 | + var pointOnLine = new Vector3(); |
| 184 | + var point = new Vector3(); |
| 185 | + |
| 186 | + ray.distanceSqToSegment( line.start, line.end, point, pointOnLine ); |
| 187 | + |
| 188 | + intersects.push( { |
| 189 | + |
| 190 | + point: point, |
| 191 | + pointOnLine: pointOnLine, |
| 192 | + distance: ray.origin.distanceTo( point ), |
| 193 | + |
| 194 | + object: this, |
| 195 | + face: null, |
| 196 | + faceIndex: i, |
| 197 | + uv: null, |
| 198 | + uv2: null, |
| 199 | + |
| 200 | + } ); |
| 201 | + |
| 202 | + } |
| 203 | + |
| 204 | + } |
| 205 | + |
| 206 | + } |
| 207 | + |
| 208 | + } () ) |
65 | 209 |
|
66 | 210 | } ); |
67 | 211 |
|
|
0 commit comments