Browse Source

Refine Mesh `raycast` (#25484)

* check oob up front

* scalar

* cmp against sq dist

* per-tri oob

* use `ray.intersectSphere`

* from `raycaster.near`

* Docs: Clean up. (#25481)

from `raycaster.near`

* fix 'sphere across near and far'

* add tests

---------

Co-authored-by: Michael Herzog <[email protected]>
ycw 2 năm trước cách đây
mục cha
commit
8f284f6534
2 tập tin đã thay đổi với 70 bổ sung1 xóa
  1. 10 1
      src/objects/Mesh.js
  2. 60 0
      test/unit/src/objects/Mesh.tests.js

+ 10 - 1
src/objects/Mesh.js

@@ -12,6 +12,7 @@ import { BufferGeometry } from '../core/BufferGeometry.js';
 const _inverseMatrix = /*@__PURE__*/ new Matrix4();
 const _ray = /*@__PURE__*/ new Ray();
 const _sphere = /*@__PURE__*/ new Sphere();
+const _sphereHitAt = /*@__PURE__*/ new Vector3();
 
 const _vA = /*@__PURE__*/ new Vector3();
 const _vB = /*@__PURE__*/ new Vector3();
@@ -163,7 +164,15 @@ class Mesh extends Object3D {
 		_sphere.copy( geometry.boundingSphere );
 		_sphere.applyMatrix4( matrixWorld );
 
-		if ( raycaster.ray.intersectsSphere( _sphere ) === false ) return;
+		_ray.copy( raycaster.ray ).recast( raycaster.near );
+
+		if ( _sphere.containsPoint( _ray.origin ) === false ) {
+
+			if ( _ray.intersectSphere( _sphere, _sphereHitAt ) === null ) return;
+
+			if ( _ray.origin.distanceToSquared( _sphereHitAt ) > ( raycaster.far - raycaster.near ) ** 2 ) return;
+
+		}
 
 		//
 

+ 60 - 0
test/unit/src/objects/Mesh.tests.js

@@ -4,9 +4,11 @@ import { Object3D } from '../../../../src/core/Object3D.js';
 import { Mesh } from '../../../../src/objects/Mesh.js';
 import { Raycaster } from '../../../../src/core/Raycaster.js';
 import { PlaneGeometry } from '../../../../src/geometries/PlaneGeometry.js';
+import { BoxGeometry } from '../../../../src/geometries/BoxGeometry.js';
 import { MeshBasicMaterial } from '../../../../src/materials/MeshBasicMaterial.js';
 import { Vector2 } from '../../../../src/math/Vector2.js';
 import { Vector3 } from '../../../../src/math/Vector3.js';
+import { DoubleSide } from '../../../../src/constants.js';
 
 export default QUnit.module( 'Objects', () => {
 
@@ -108,6 +110,64 @@ export default QUnit.module( 'Objects', () => {
 
 		} );
 
+		QUnit.test( 'raycast/range', ( assert ) => {
+
+			const geometry = new BoxGeometry( 1, 1, 1 );
+			const material = new MeshBasicMaterial( { side: DoubleSide } );
+			const mesh = new Mesh( geometry, material );
+			const raycaster = new Raycaster();
+			const intersections = [];
+
+			raycaster.ray.origin.set( 0, 0, 0 );
+			raycaster.ray.direction.set( 1, 0, 0 );
+			raycaster.near = 100;
+			raycaster.far = 200;
+
+			mesh.matrixWorld.identity();
+			mesh.position.setX( 150 );
+			mesh.updateMatrixWorld( true );
+			intersections.length = 0;
+			mesh.raycast( raycaster, intersections );
+			assert.ok( intersections.length > 0, 'bounding sphere between near and far' );
+
+			mesh.matrixWorld.identity();
+			mesh.position.setX( raycaster.near );
+			mesh.updateMatrixWorld( true );
+			intersections.length = 0;
+			mesh.raycast( raycaster, intersections );
+			assert.ok( intersections.length > 0, 'bounding sphere across near' );
+
+			mesh.matrixWorld.identity();
+			mesh.position.setX( raycaster.far );
+			mesh.updateMatrixWorld( true );
+			intersections.length = 0;
+			mesh.raycast( raycaster, intersections );
+			assert.ok( intersections.length > 0, 'bounding sphere across far' );
+
+			mesh.matrixWorld.identity();
+			mesh.position.setX( 150 );
+			mesh.scale.setY( 9999 );
+			mesh.updateMatrixWorld( true );
+			intersections.length = 0;
+			mesh.raycast( raycaster, intersections );
+			assert.ok( intersections.length > 0, 'bounding sphere across near and far' );
+
+			mesh.matrixWorld.identity();
+			mesh.position.setX( - 9999 );
+			mesh.updateMatrixWorld( true );
+			intersections.length = 0;
+			mesh.raycast( raycaster, intersections );
+			assert.ok( intersections.length === 0, 'bounding sphere behind near' );
+
+			mesh.matrixWorld.identity();
+			mesh.position.setX( 9999 );
+			mesh.updateMatrixWorld( true );
+			intersections.length = 0;
+			mesh.raycast( raycaster, intersections );
+			assert.ok( intersections.length === 0, 'bounding sphere beyond far' );
+
+		} );
+
 	} );
 
 } );