Skip to content
142 changes: 141 additions & 1 deletion examples/js/lines/LineSegments2.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,

} );

}

}

}

} () )

} );
148 changes: 146 additions & 2 deletions examples/jsm/lines/LineSegments2.js
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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,

} );

}

}

}

} () )

} );

Expand Down