From 18875d119d0512cfac8220376bb4735af673f90a Mon Sep 17 00:00:00 2001 From: gsimone Date: Tue, 10 Dec 2024 14:27:38 +0100 Subject: [PATCH] 1st commit Co-authored-by: abernier --- docs/api/en/helpers/RaycasterHelper.html | 95 +++++++++ docs/list.json | 1 + examples/files.json | 1 + .../screenshots/webgl_raycaster_helper.jpg | Bin 0 -> 9319 bytes examples/webgl_raycaster_helper.html | 108 ++++++++++ src/Three.Core.js | 1 + src/helpers/RaycasterHelper.js | 188 ++++++++++++++++++ 7 files changed, 394 insertions(+) create mode 100644 docs/api/en/helpers/RaycasterHelper.html create mode 100644 examples/screenshots/webgl_raycaster_helper.jpg create mode 100644 examples/webgl_raycaster_helper.html create mode 100644 src/helpers/RaycasterHelper.js 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 0000000000000000000000000000000000000000..2c109ecaf757deac87fb0fcb7ef49d6f033530b0 GIT binary patch literal 9319 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 literal 0 HcmV?d00001 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 };