@threlte/xr
useHitTest
Provides a hit test result on each frame during an immersive-ar
session.
Hit testing lets you position virtual items in a real-world view.
<script>
import { useHitTest } from '@threlte/xr'
let ref
useHitTest((hitMatrix, hit) => {
if (!ref) return
if (hit) {
ref.visible = true
ref.matrix.copy(hitMatrix)
} else {
ref.visible = false
}
})
</script>
<T.Mesh bind:ref>
<T.SphereGeometry args={[0.1]}>
<T.MeshBasicMaterial />
</T.Mesh>
This hook can optionally specify one of three origins from which to cast the hit test ray: viewer
(the default), leftInput
or rightInput
.
useHitTest((hitMatrix, hit) => {
// Perform a hit test from the left controller or hand.
}, { source: 'leftInput' })
In the following example, hit testing is set up from both controllers and hands.
<script lang="ts">
import { Canvas } from '@threlte/core'
import { ARButton } from '@threlte/xr'
import Scene from './Scene.svelte'
</script>
<div>
<Canvas>
<Scene />
</Canvas>
<ARButton />
</div>
<style>
div {
height: 100%;
}
</style>
<script lang="ts">
import * as THREE from 'three'
import { T } from '@threlte/core'
import { XR, Controller, Hand, useHitTest } from '@threlte/xr'
const geometry = new THREE.CylinderGeometry(0.1, 0.1, 0.2, 32).translate(0, 0.1, 0)
let meshes: THREE.Mesh[] = []
let cursors = { left: undefined! as THREE.Mesh, right: undefined! as THREE.Mesh }
const hands = ['left', 'right'] as const
type Hands = (typeof hands)[number]
const handleSelect = (hand: Hands) => () => {
if (!cursors[hand].visible) return
const material = new THREE.MeshPhongMaterial({ color: 0xffffff * Math.random() })
const mesh = new THREE.Mesh(geometry, material)
cursors[hand].matrix.decompose(mesh.position, mesh.quaternion, mesh.scale)
mesh.scale.y = Math.random() * 2 + 1
meshes.push(mesh)
meshes = meshes
}
const handleHitTest =
(hand: Hands) => (hitMatrix: THREE.Matrix4, hit: XRHitTestResult | undefined) => {
if (!cursors[hand]) return
if (hit) {
cursors[hand].visible = true
cursors[hand].matrix.copy(hitMatrix)
} else {
cursors[hand].visible = false
}
}
useHitTest(handleHitTest('left'), { source: 'leftInput' })
useHitTest(handleHitTest('right'), { source: 'rightInput' })
</script>
<XR>
{#each hands as hand}
<Controller
{hand}
onselect={handleSelect(hand)}
/>
<Hand
{hand}
onpinchend={handleSelect(hand)}
/>
{/each}
</XR>
<T.Mesh
bind:ref={cursors.left}
matrixAutoUpdate={false}
>
<T.RingGeometry
args={[0.15, 0.2, 32]}
oncreate={({ ref }) => ref.rotateX(-Math.PI / 2)}
/>
<T.MeshBasicMaterial />
</T.Mesh>
<T.Mesh
bind:ref={cursors.right}
matrixAutoUpdate={false}
>
<T.RingGeometry
args={[0.15, 0.2, 32]}
oncreate={({ ref }) => ref.rotateX(-Math.PI / 2)}
/>
<T.MeshBasicMaterial />
</T.Mesh>
<T.HemisphereLight
args={[0xffffff, 0xbbbbff, 1]}
position={[0.5, 1, 0.25]}
/>
<T.AmbientLight intensity={0.5} />
{#each meshes as mesh, index (index)}
<T is={mesh} />
{/each}
Signature
useHitTest((hitMatrix: THREE.Matrix4, hit: XRHitTestResult | undefined) => {})