diff --git a/docs/api/en/helpers/RaycasterHelper.html b/docs/api/en/helpers/RaycasterHelper.html new file mode 100644 index 00000000000000..f3640eb52224ae --- /dev/null +++ b/docs/api/en/helpers/RaycasterHelper.html @@ -0,0 +1,95 @@ + + + + + + + + + + [page:Object3D] → + +

[name]

+ +

+ A 3D object for visualizing a [page:Raycaster Raycaster]. +

+ +

Code Example

+ + + const origin = new THREE.Vector3( - 4, 0, 0 ); + const direction = new THREE.Vector3( 1, 0, 0 ); + const raycaster = new THREE.Raycaster( origin, direction ); + raycaster.near = 1; + raycaster.far = 8; + + const raycasterHelper = new THREE.RaycasterHelper( raycaster ); + scene.add( raycasterHelper ); + + function render() { + + const intersects = raycaster.intersectObjects( scene.children ); + + raycasterHelper.hits = intersects; + raycasterHelper.update(); + + renderer.render( scene, camera ); + + } + + window.requestAnimationFrame(render); + + +

Examples

+ +

[example:webgl_raycaster_helper WebGL / raycaster / helper]

+ +

Constructor

+ +

+ [name]([param:Raycaster raycaster], [param:Number numberOfHitsToVisualize], [param:Number sphereRadius], + [param:Number nearFarSize], [param:Array colors]) +

+

+ [page:Raycaster raycaster] -- Raycaster to visualize.
+ [page:Number numberOfHitsToVisualize] -- Maximum hits to visualize. Default is `20`.
+ [page:Number sphereRadius] -- Origin sphere radius. Default is `0.04`.
+ [page:Number nearFarSize] -- Near/Far plane size. Default is `0.1`.
+ [page:Array colors] -- Colors for the helper elements. Default is `{ near: 0xffffff, far: 0xffffff, originToNear: 0x333333, nearToFar: 0xffffff, origin: [ 0x0eec82, 0xff005b ] }`
+

+ +

Properties

+

See the base [page:Object3D] class for common properties.

+ +

[property:Array hits]

+

An array of [page:Raycaster Raycaster]'s intersections.

+ +

Methods

+

See the base [page:Object3D] class for common methods.

+ +

[method:undefined setColors]([param:Array colors])

+

+ colors -- A partial array of `colors`, as defined in the constructor's params. +

+ +

+ [method:undefined update]() +

+

+ Updates the helper's geometries based on the helper's `hits`. +

+ +

[method:undefined dispose]()

+

+ Frees the GPU-related resources allocated by this instance. Call this + method whenever this instance is no longer used in your app. +

+ +

Source

+ +

+ [link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js] +

+ + diff --git a/docs/list.json b/docs/list.json index eadf2f26603bb0..228f591e38d1e2 100644 --- a/docs/list.json +++ b/docs/list.json @@ -166,6 +166,7 @@ "HemisphereLightHelper": "api/en/helpers/HemisphereLightHelper", "PlaneHelper": "api/en/helpers/PlaneHelper", "PointLightHelper": "api/en/helpers/PointLightHelper", + "RaycasterHelper": "api/en/helpers/RaycasterHelper", "SkeletonHelper": "api/en/helpers/SkeletonHelper", "SpotLightHelper": "api/en/helpers/SpotLightHelper" }, diff --git a/examples/files.json b/examples/files.json index 1948537f3331e8..f89d739678e3c5 100644 --- a/examples/files.json +++ b/examples/files.json @@ -188,6 +188,7 @@ "webgl_points_waves", "webgl_portal", "webgl_raycaster_bvh", + "webgl_raycaster_helper", "webgl_raycaster_sprite", "webgl_raycaster_texture", "webgl_read_float_buffer", diff --git a/examples/screenshots/webgl_raycaster_helper.jpg b/examples/screenshots/webgl_raycaster_helper.jpg new file mode 100644 index 00000000000000..2c109ecaf757de Binary files /dev/null and b/examples/screenshots/webgl_raycaster_helper.jpg differ diff --git a/examples/webgl_raycaster_helper.html b/examples/webgl_raycaster_helper.html new file mode 100644 index 00000000000000..61c0f49eea008f --- /dev/null +++ b/examples/webgl_raycaster_helper.html @@ -0,0 +1,108 @@ + + + + three.js webgl - helpers + + + + + +
+ three.js - helpers +
+ + + + + + + diff --git a/src/Three.Core.js b/src/Three.Core.js index f7a10261f0014e..3e462645e31f5d 100644 --- a/src/Three.Core.js +++ b/src/Three.Core.js @@ -127,6 +127,7 @@ export { Quaternion } from './math/Quaternion.js'; export { Color } from './math/Color.js'; export { ColorManagement } from './math/ColorManagement.js'; export { SphericalHarmonics3 } from './math/SphericalHarmonics3.js'; +export { RaycasterHelper } from './helpers/RaycasterHelper.js'; export { SpotLightHelper } from './helpers/SpotLightHelper.js'; export { SkeletonHelper } from './helpers/SkeletonHelper.js'; export { PointLightHelper } from './helpers/PointLightHelper.js'; diff --git a/src/helpers/RaycasterHelper.js b/src/helpers/RaycasterHelper.js new file mode 100644 index 00000000000000..4ef29536d287e9 --- /dev/null +++ b/src/helpers/RaycasterHelper.js @@ -0,0 +1,188 @@ +import { BufferGeometry } from '../core/BufferGeometry.js'; +import { Float32BufferAttribute } from '../core/BufferAttribute.js'; +import { InstancedMesh } from '../objects/InstancedMesh.js'; +import { LineBasicMaterial } from '../materials/LineBasicMaterial.js'; +import { Line } from '../objects/Line.js'; +import { MeshBasicMaterial } from '../materials/MeshBasicMaterial.js'; +import { Mesh } from '../objects/Mesh.js'; +import { Object3D } from '../core/Object3D.js'; +import { SphereGeometry } from '../geometries/SphereGeometry.js'; +import { Vector3 } from '../math/Vector3.js'; + +const _o = /*@__PURE__*/ new Object3D(); +const _v = /*@__PURE__*/ new Vector3(); + +class RaycasterHelper extends Object3D { + + constructor( raycaster, numberOfHitsToVisualize = 20, sphereRadius = .04, nearFarSize = .1, colors = { + near: 0xffffff, + far: 0xffffff, + originToNear: 0x333333, + nearToFar: 0xffffff, + origin: [ 0x0eec82, 0xff005b ], + } ) { + + super(); + this.raycaster = raycaster; + this.numberOfHitsToVisualize = numberOfHitsToVisualize; + + this.hits = []; + + this.colors = colors; + + this.origin = new Mesh( + new SphereGeometry( sphereRadius, 32 ), + new MeshBasicMaterial() + ); + this.origin.name = 'RaycasterHelper_origin'; + this.origin.raycast = () => null; + + const geometry = new BufferGeometry(); + geometry.setAttribute( 'position', new Float32BufferAttribute( [ + - nearFarSize, nearFarSize, 0, + nearFarSize, nearFarSize, 0, + nearFarSize, - nearFarSize, 0, + - nearFarSize, - nearFarSize, 0, + - nearFarSize, nearFarSize, 0 + ], 3 ) ); + + this.near = new Line( geometry, new LineBasicMaterial() ); + this.near.name = 'RaycasterHelper_near'; + this.near.raycast = () => null; + + this.far = new Line( geometry, new LineBasicMaterial() ); + this.far.name = 'RaycasterHelper_far'; + this.far.raycast = () => null; + + this.nearToFar = new Line( new BufferGeometry(), new LineBasicMaterial() ); + this.nearToFar.name = 'RaycasterHelper_nearToFar'; + this.nearToFar.raycast = () => null; + + this.nearToFar.geometry.setFromPoints( [ _v, _v ] ); + + this.originToNear = new Line( + this.nearToFar.geometry.clone(), + new LineBasicMaterial() + ); + this.originToNear.name = 'RaycasterHelper_originToNear'; + this.originToNear.raycast = () => null; + + this.hitPoints = new InstancedMesh( + new SphereGeometry( sphereRadius ), + new MeshBasicMaterial(), + this.numberOfHitsToVisualize + ); + this.hitPoints.name = 'RaycasterHelper_hits'; + this.hitPoints.raycast = () => null; + + this.add( this.nearToFar ); + this.add( this.originToNear ); + + this.add( this.near ); + this.add( this.far ); + + this.add( this.origin ); + this.add( this.hitPoints ); + + this.setColors(); + + } + + setColors( colors ) { + + const _colors = { + ...this.colors, + ...colors, + }; + + this.near.material.color.set( _colors.near ); + this.far.material.color.set( _colors.far ); + this.nearToFar.material.color.set( _colors.nearToFar ); + this.originToNear.material.color.set( _colors.originToNear ); + + } + + update() { + + const origin = this.raycaster.ray.origin; + const direction = this.raycaster.ray.direction; + + this.origin.position.copy( origin ); + + this.near.position + .copy( origin ) + .add( direction.clone().multiplyScalar( this.raycaster.near ) ); + + this.far.position + .copy( origin ) + .add( direction.clone().multiplyScalar( this.raycaster.far ) ); + + this.far.lookAt( origin ); + this.near.lookAt( origin ); + + let pos = this.nearToFar.geometry.getAttribute( 'position' ); + pos.set( [ ...this.near.position, ...this.far.position ] ); + pos.needsUpdate = true; + + pos = this.originToNear.geometry.getAttribute( 'position' ); + pos.set( [ ...origin, ...this.near.position ] ); + pos.needsUpdate = true; + + /** + * Update hit points visualization + */ + + const hits = Array.isArray( this.hits ) ? this.hits : []; + + for ( let i = 0; i < this.numberOfHitsToVisualize; i ++ ) { + + const hit = hits[ i ]; + + if ( hit ) { + + const { point } = hit; + _o.position.copy( point ); + _o.scale.setScalar( 1 ); + + } else { + + _o.scale.setScalar( 0 ); + + } + + _o.updateMatrix(); + + this.hitPoints.setMatrixAt( i, _o.matrix ); + + } + + this.hitPoints.instanceMatrix.needsUpdate = true; + + /** + * Update the color of the origin based on wether there are hits. + */ + this.origin.material.color.set( + this.hits.length > 0 ? this.colors.origin[ 0 ] : this.colors.origin[ 1 ] + ); + + } + + dispose() { + + this.origin.geometry.dispose(); + this.origin.material.dispose(); + this.near.geometry.dispose(); + this.near.material.dispose(); + this.far.geometry.dispose(); + this.far.material.dispose(); + this.nearToFar.geometry.dispose(); + this.nearToFar.material.dispose(); + this.originToNear.geometry.dispose(); + this.originToNear.material.dispose(); + this.hitPoints.dispose(); + + } + +} + +export { RaycasterHelper };