Skip to content

Commit 6a21666

Browse files
authored
Merge pull request #17872 from gkjohnson/linesegments2-raycast
Examples: Add raycast support for LineSegments2
2 parents 6c05134 + dc96a86 commit 6a21666

File tree

2 files changed

+287
-3
lines changed

2 files changed

+287
-3
lines changed

examples/js/lines/LineSegments2.js

Lines changed: 141 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,146 @@ THREE.LineSegments2.prototype = Object.assign( Object.create( THREE.Mesh.prototy
5252

5353
};
5454

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

57197
} );

examples/jsm/lines/LineSegments2.js

Lines changed: 146 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,12 @@
66
import {
77
InstancedInterleavedBuffer,
88
InterleavedBufferAttribute,
9+
Line3,
10+
Math as _Math,
11+
Matrix4,
912
Mesh,
10-
Vector3
13+
Vector3,
14+
Vector4
1115
} from "../../../build/three.module.js";
1216
import { LineSegmentsGeometry } from "../lines/LineSegmentsGeometry.js";
1317
import { LineMaterial } from "../lines/LineMaterial.js";
@@ -61,7 +65,147 @@ LineSegments2.prototype = Object.assign( Object.create( Mesh.prototype ), {
6165

6266
};
6367

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+
} () )
65209

66210
} );
67211

0 commit comments

Comments
 (0)