@threlte/xr
useHandJoint
Provides a reference to a requested hand joint, once available.
<script>
import { useHandJoint } from '@threlte/xr'
const wristJoint = useHandJoint('left', 'wrist')
</script>
Reading hand joint positions in real time can be very useful, for example in providing rigid bodies for hands:
<script lang='ts'>
import { Canvas } from '@threlte/core'
import { World } from '@threlte/rapier'
import { VRButton } from '@threlte/xr'
import Scene from './Scene.svelte'
</script>
<Canvas>
<World gravity={[0, 0, 0]}>
<Scene />
</World>
</Canvas>
<VRButton />
<script lang='ts'>
import { T } from '@threlte/core'
import { InstancedMesh, Instance } from '@threlte/extras'
import { Collider, RigidBody } from '@threlte/rapier'
const size = 0.02
const limit = 100
</script>
<T.Group position={[0, 1.7, 0]}>
<InstancedMesh {limit}>
<T.BoxGeometry args={[size, size, size]} />
<T.MeshStandardMaterial roughness={0} metalness={0.2} />
{#each { length: limit } as _, index (index)}
<RigidBody>
<Collider shape='cuboid' args={[size / 2, size / 2, size / 2]} />
<Instance color='hotpink' />
</RigidBody>
{/each}
</InstancedMesh>
</T.Group>
<script lang='ts'>
import { useFrame } from '@threlte/core'
import { handJoints, useHandJoint } from '@threlte/xr'
import type { RigidBody as RapierRigidBody } from '@dimforge/rapier3d-compat'
import { Collider, RigidBody } from '@threlte/rapier'
export let jointIndex: number
export let hand: 'left' | 'right'
let body: RapierRigidBody
const joint = useHandJoint(hand, handJoints[jointIndex]!)
const { start, stop } = useFrame(() => {
if (joint.current === undefined || body === undefined) return
const { x, y, z } = joint.current.position
body.setNextKinematicTranslation({ x, y, z })
}, { autostart: false })
$: radius = $joint?.jointRadius
$: if (body && radius && $joint) {
start()
} else {
stop()
}
</script>
{#if radius}
<RigidBody bind:rigidBody={body} type='kinematicPosition'>
<Collider shape='ball' args={[radius]} />
</RigidBody>
{/if}
<script lang='ts'>
import { T } from '@threlte/core'
import { Hand, XR, useXR } from '@threlte/xr'
import { Text } from '@threlte/extras'
import { Attractor, Debug } from '@threlte/rapier'
import JointCollider from './JointBody.svelte'
import Cubes from './Cube.svelte'
const { isHandTracking } = useXR()
let debug = false
</script>
{#if debug}
<Debug />
{/if}
<XR>
<Hand left on:pinchend={() => (debug = !debug)} />
<Hand right on:pinchend={() => (debug = !debug)} />
{#if $isHandTracking}
{#each { length: 25 } as _, jointIndex}
<JointCollider {jointIndex} hand='left' />
<JointCollider {jointIndex} hand='right' />
{/each}
{/if}
<Text
position={[0, 1.7, -1]}
text='Pinch to toggle physics debug.'
/>
</XR>
<Cubes />
<T.PerspectiveCamera
makeDefault
position={[0, 1, 1]}
on:create={({ ref }) => ref.lookAt(0, 1.8, 0)}
/>
<T.AmbientLight />
<T.SpotLight
position={[1, 8, 1]}
angle={0.3}
penumbra={1}
intensity={30}
castShadow
target.x={0}
target.y={1.8}
target.z={0}
/>
<Attractor
range={50}
strength={0.000001}
position={[0, 1.7, 0]}
/>