Skip to content

Commit

Permalink
Merge pull request #658 from agargaro/master
Browse files Browse the repository at this point in the history
Add `near` and `far` check in intersectRay
  • Loading branch information
gkjohnson committed May 15, 2024
2 parents 4ded750 + 62fde15 commit aa63f76
Show file tree
Hide file tree
Showing 12 changed files with 85 additions and 59 deletions.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -370,10 +370,10 @@ Helper function for use when `indirect` is set to true. This function takes a tr
### .raycast

```js
raycast( ray : Ray, side : FrontSide | BackSide | DoubleSide = FrontSide ) : Array<RaycastHit>
raycast( ray : Ray, side : FrontSide | BackSide | DoubleSide = FrontSide, near : Number = 0, far : Number = Infinity ) : Array<RaycastHit>
```
```js
raycast( ray : Ray, material : Array<Material> | Material ) : Array<RaycastHit>
raycast( ray : Ray, material? : Array<Material> | Material, near : Number = 0, far : Number = Infinity ) : Array<RaycastHit>
```

Returns all raycast triangle hits in unsorted order. It is expected that `ray` is in the frame of the BVH already. Likewise the returned results are also provided in the local frame of the BVH. The `side` identifier is used to determine the side to check when raycasting or a material with the given side field can be passed. If an array of materials is provided then it is expected that the geometry has groups and the appropriate material side is used per group.
Expand All @@ -383,10 +383,10 @@ Note that unlike three.js' Raycaster results the points and distances in the int
### .raycastFirst

```js
raycastFirst( ray : Ray, side : FrontSide | BackSide | DoubleSide = FrontSide ) : RaycastHit
raycastFirst( ray : Ray, side : FrontSide | BackSide | DoubleSide = FrontSide, near : Number = 0, far : Number = Infinity ) : RaycastHit
```
```js
raycastFirst( ray : Ray, material : Array<Material> | Material ) : RaycastHit
raycastFirst( ray : Ray, material : Array<Material> | Material, near : Number = 0, far : Number = Infinity ) : RaycastHit
```

Returns the first raycast hit in the model. This is typically much faster than returning all hits. See [raycast](#raycast) for information on the side and material options as well as the frame of the returned intersections.
Expand Down
15 changes: 12 additions & 3 deletions example/raycast.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ let deltaTime = 0;
const params = {
raycasters: {
count: 150,
speed: 1
speed: 1,
near: 0,
far: pointDist
},

mesh: {
Expand Down Expand Up @@ -96,6 +98,8 @@ function init() {
const rcFolder = gui.addFolder( 'Raycasters' );
rcFolder.add( params.raycasters, 'count' ).min( 1 ).max( 1000 ).step( 1 ).onChange( () => updateFromOptions() );
rcFolder.add( params.raycasters, 'speed' ).min( 0 ).max( 20 );
rcFolder.add( params.raycasters, 'near' ).min( 0 ).max( pointDist ).onChange( () => updateFromOptions() );
rcFolder.add( params.raycasters, 'far' ).min( 0 ).max( pointDist ).onChange( () => updateFromOptions() );
rcFolder.open();

const meshFolder = gui.addFolder( 'Mesh' );
Expand Down Expand Up @@ -177,8 +181,10 @@ function addRaycaster() {

hitMesh.position.set( pointDist - length, 0, 0 );

cylinderMesh.position.set( pointDist - ( length / 2 ), 0, 0 );
cylinderMesh.scale.set( 1, length, 1 );
const lineLength = res.length ? length - raycaster.near : length - raycaster.near - ( pointDist - raycaster.far );

cylinderMesh.position.set( pointDist - raycaster.near - ( lineLength / 2 ), 0, 0 );
cylinderMesh.scale.set( 1, lineLength, 1 );

cylinderMesh.rotation.z = Math.PI / 2;

Expand All @@ -195,6 +201,9 @@ function addRaycaster() {

function updateFromOptions() {

raycaster.near = params.raycasters.near;
raycaster.far = params.raycasters.far;

// Update raycaster count
while ( rayCasterObjects.length > params.raycasters.count ) {

Expand Down
8 changes: 4 additions & 4 deletions src/core/MeshBVH.js
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ export class MeshBVH {
}

/* Core Cast Functions */
raycast( ray, materialOrSide = FrontSide ) {
raycast( ray, materialOrSide = FrontSide, near = 0, far = Infinity ) {

const roots = this._roots;
const geometry = this.geometry;
Expand All @@ -226,7 +226,7 @@ export class MeshBVH {
const materialSide = isArrayMaterial ? materialOrSide[ groups[ i ].materialIndex ].side : side;
const startCount = intersects.length;

raycastFunc( this, i, materialSide, ray, intersects );
raycastFunc( this, i, materialSide, ray, intersects, near, far );

if ( isArrayMaterial ) {

Expand All @@ -245,7 +245,7 @@ export class MeshBVH {

}

raycastFirst( ray, materialOrSide = FrontSide ) {
raycastFirst( ray, materialOrSide = FrontSide, near = 0, far = Infinity ) {

const roots = this._roots;
const geometry = this.geometry;
Expand All @@ -260,7 +260,7 @@ export class MeshBVH {
for ( let i = 0, l = roots.length; i < l; i ++ ) {

const materialSide = isArrayMaterial ? materialOrSide[ groups[ i ].materialIndex ].side : side;
const result = raycastFirstFunc( this, i, materialSide, ray );
const result = raycastFirstFunc( this, i, materialSide, ray, near, far );
if ( result != null && ( closestResult == null || result.distance < closestResult.distance ) ) {

closestResult = result;
Expand Down
18 changes: 9 additions & 9 deletions src/core/cast/raycast.template.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ import { BufferStack } from '../utils/BufferStack.js';
import { intersectTris } from '../utils/iterationUtils.generated.js';
import { intersectTris_indirect } from '../utils/iterationUtils_indirect.generated.js';

export function raycast/* @echo INDIRECT_STRING */( bvh, root, side, ray, intersects ) {
export function raycast/* @echo INDIRECT_STRING */( bvh, root, side, ray, intersects, near, far ) {

BufferStack.setBuffer( bvh._roots[ root ] );
_raycast( 0, bvh, side, ray, intersects );
_raycast( 0, bvh, side, ray, intersects, near, far );
BufferStack.clearBuffer();

}

function _raycast( nodeIndex32, bvh, side, ray, intersects ) {
function _raycast( nodeIndex32, bvh, side, ray, intersects, near, far ) {

const { float32Array, uint16Array, uint32Array } = BufferStack;
const nodeIndex16 = nodeIndex32 * 2;
Expand All @@ -24,27 +24,27 @@ function _raycast( nodeIndex32, bvh, side, ray, intersects ) {

/* @if INDIRECT */

intersectTris_indirect( bvh, side, ray, offset, count, intersects );
intersectTris_indirect( bvh, side, ray, offset, count, intersects, near, far );

/* @else */

intersectTris( bvh, side, ray, offset, count, intersects );
intersectTris( bvh, side, ray, offset, count, intersects, near, far );

/* @endif */

} else {

const leftIndex = LEFT_NODE( nodeIndex32 );
if ( intersectRay( leftIndex, float32Array, ray ) ) {
if ( intersectRay( leftIndex, float32Array, ray, near, far ) ) {

_raycast( leftIndex, bvh, side, ray, intersects );
_raycast( leftIndex, bvh, side, ray, intersects, near, far );

}

const rightIndex = RIGHT_NODE( nodeIndex32, uint32Array );
if ( intersectRay( rightIndex, float32Array, ray ) ) {
if ( intersectRay( rightIndex, float32Array, ray, near, far ) ) {

_raycast( rightIndex, bvh, side, ray, intersects );
_raycast( rightIndex, bvh, side, ray, intersects, near, far );

}

Expand Down
18 changes: 9 additions & 9 deletions src/core/cast/raycastFirst.template.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,17 @@ import { intersectClosestTri_indirect } from '../utils/iterationUtils_indirect.g

const _xyzFields = [ 'x', 'y', 'z' ];

export function raycastFirst/* @echo INDIRECT_STRING */( bvh, root, side, ray ) {
export function raycastFirst/* @echo INDIRECT_STRING */( bvh, root, side, ray, near, far ) {

BufferStack.setBuffer( bvh._roots[ root ] );
const result = _raycastFirst( 0, bvh, side, ray );
const result = _raycastFirst( 0, bvh, side, ray, near, far );
BufferStack.clearBuffer();

return result;

}

function _raycastFirst( nodeIndex32, bvh, side, ray ) {
function _raycastFirst( nodeIndex32, bvh, side, ray, near, far ) {

const { float32Array, uint16Array, uint32Array } = BufferStack;
let nodeIndex16 = nodeIndex32 * 2;
Expand All @@ -29,11 +29,11 @@ function _raycastFirst( nodeIndex32, bvh, side, ray ) {

/* @if INDIRECT */

return intersectClosestTri_indirect( bvh, side, ray, offset, count );
return intersectClosestTri_indirect( bvh, side, ray, offset, count, near, far );

/* @else */

return intersectClosestTri( bvh, side, ray, offset, count );
return intersectClosestTri( bvh, side, ray, offset, count, near, far );

/* @endif */

Expand All @@ -60,8 +60,8 @@ function _raycastFirst( nodeIndex32, bvh, side, ray ) {

}

const c1Intersection = intersectRay( c1, float32Array, ray );
const c1Result = c1Intersection ? _raycastFirst( c1, bvh, side, ray ) : null;
const c1Intersection = intersectRay( c1, float32Array, ray, near, far );
const c1Result = c1Intersection ? _raycastFirst( c1, bvh, side, ray, near, far ) : null;

// if we got an intersection in the first node and it's closer than the second node's bounding
// box, we don't need to consider the second node because it couldn't possibly be a better result
Expand All @@ -84,8 +84,8 @@ function _raycastFirst( nodeIndex32, bvh, side, ray ) {

// either there was no intersection in the first node, or there could still be a closer
// intersection in the second, so check the second node and then take the better of the two
const c2Intersection = intersectRay( c2, float32Array, ray );
const c2Result = c2Intersection ? _raycastFirst( c2, bvh, side, ray ) : null;
const c2Intersection = intersectRay( c2, float32Array, ray, near, far );
const c2Result = c2Intersection ? _raycastFirst( c2, bvh, side, ray, near, far ) : null;

if ( c1Result && c2Result ) {

Expand Down
8 changes: 3 additions & 5 deletions src/core/utils/intersectUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* This function performs intersection tests similar to Ray.intersectBox in three.js,
* with the difference that the box values are read from an array to improve performance.
*/
export function intersectRay( nodeIndex32, array, ray ) {
export function intersectRay( nodeIndex32, array, ray, near, far ) {

let tmin, tmax, tymin, tymax, tzmin, tzmax;

Expand Down Expand Up @@ -67,14 +67,12 @@ export function intersectRay( nodeIndex32, array, ray ) {

if ( ( tmin > tzmax ) || ( tzmin > tmax ) ) return false;

// if ( tzmin > tmin || tmin !== tmin ) tmin = tzmin; // Uncomment this line if add the distance check
if ( tzmin > tmin || tmin !== tmin ) tmin = tzmin;

if ( tzmax < tmax || tmax !== tmax ) tmax = tzmax;

//return point closest to the ray (positive side)

if ( tmax < 0 ) return false;

return true;
return tmin <= far && tmax >= near;

}
12 changes: 6 additions & 6 deletions src/core/utils/iterationUtils.template.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,27 @@
import { intersectTri } from '../../utils/ThreeRayIntersectUtilities.js';
import { setTriangle } from '../../utils/TriangleUtilities.js';

export function intersectTris/* @echo INDIRECT_STRING */( bvh, side, ray, offset, count, intersections ) {
export function intersectTris/* @echo INDIRECT_STRING */( bvh, side, ray, offset, count, intersections, near, far ) {

const { geometry, _indirectBuffer } = bvh;
for ( let i = offset, end = offset + count; i < end; i ++ ) {

/* @if INDIRECT */

let vi = _indirectBuffer ? _indirectBuffer[ i ] : i;
intersectTri( geometry, side, ray, vi, intersections );
intersectTri( geometry, side, ray, vi, intersections, near, far );

/* @else */

intersectTri( geometry, side, ray, i, intersections );
intersectTri( geometry, side, ray, i, intersections, near, far );

/* @endif */

}

}

export function intersectClosestTri/* @echo INDIRECT_STRING */( bvh, side, ray, offset, count ) {
export function intersectClosestTri/* @echo INDIRECT_STRING */( bvh, side, ray, offset, count, near, far ) {

const { geometry, _indirectBuffer } = bvh;
let dist = Infinity;
Expand All @@ -32,11 +32,11 @@ export function intersectClosestTri/* @echo INDIRECT_STRING */( bvh, side, ray,
let intersection;
/* @if INDIRECT */

intersection = intersectTri( geometry, side, ray, _indirectBuffer ? _indirectBuffer[ i ] : i );
intersection = intersectTri( geometry, side, ray, _indirectBuffer ? _indirectBuffer[ i ] : i, null, near, far );

/* @else */

intersection = intersectTri( geometry, side, ray, i );
intersection = intersectTri( geometry, side, ray, i, null, near, far );

/* @endif */

Expand Down
4 changes: 2 additions & 2 deletions src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,9 @@ export class MeshBVH {

constructor( geometry: BufferGeometry, options?: MeshBVHOptions );

raycast( ray: Ray, materialOrSide: Side | Array<Material> | Material ): Array<Intersection>
raycast( ray: Ray, materialOrSide?: Side | Array<Material> | Material, near?: number, far?: number ): Array<Intersection>

raycastFirst( ray: Ray, materialOrSide: Side | Array<Material> | Material ): Intersection;
raycastFirst( ray: Ray, materialOrSide?: Side | Array<Material> | Material, near?: number, far?: number ): Intersection;

intersectsSphere( sphere: Sphere ): boolean;

Expand Down
15 changes: 12 additions & 3 deletions src/utils/ExtensionUtilities.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { Ray, Matrix4, Mesh } from 'three';
import { Ray, Matrix4, Mesh, Vector3 } from 'three';
import { convertRaycastIntersect } from './GeometryRayIntersectUtilities.js';
import { MeshBVH } from '../core/MeshBVH.js';

const ray = /* @__PURE__ */ new Ray();
const direction = /* @__PURE__ */ new Vector3();
const tmpInverseMatrix = /* @__PURE__ */ new Matrix4();
const worldScale = /* @__PURE__ */ new Vector3();
const origMeshRaycastFunc = Mesh.prototype.raycast;

export function acceleratedRaycast( raycaster, intersects ) {
Expand All @@ -15,10 +17,17 @@ export function acceleratedRaycast( raycaster, intersects ) {
tmpInverseMatrix.copy( this.matrixWorld ).invert();
ray.copy( raycaster.ray ).applyMatrix4( tmpInverseMatrix );

this.getWorldScale( worldScale );
direction.copy( ray.direction ).multiply( worldScale );

const scaleFactor = direction.length();
const near = raycaster.near / scaleFactor;
const far = raycaster.far / scaleFactor;

const bvh = this.geometry.boundsTree;
if ( raycaster.firstHitOnly === true ) {

const hit = convertRaycastIntersect( bvh.raycastFirst( ray, this.material ), this, raycaster );
const hit = convertRaycastIntersect( bvh.raycastFirst( ray, this.material, near, far ), this, raycaster );
if ( hit ) {

intersects.push( hit );
Expand All @@ -27,7 +36,7 @@ export function acceleratedRaycast( raycaster, intersects ) {

} else {

const hits = bvh.raycast( ray, this.material );
const hits = bvh.raycast( ray, this.material, near, far );
for ( let i = 0, l = hits.length; i < l; i ++ ) {

const hit = convertRaycastIntersect( hits[ i ], this, raycaster );
Expand Down
10 changes: 1 addition & 9 deletions src/utils/GeometryRayIntersectUtilities.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,6 @@ export function convertRaycastIntersect( hit, object, raycaster ) {
hit.distance = hit.point.distanceTo( raycaster.ray.origin );
hit.object = object;

if ( hit.distance < raycaster.near || hit.distance > raycaster.far ) {

return null;

} else {

return hit;

}
return hit;

}

0 comments on commit aa63f76

Please sign in to comment.