@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>
<div>
<Canvas>
<World gravity={[0, 0, 0]}>
<Scene />
</World>
</Canvas>
<VRButton />
</div>
<style>
div {
height: 100%;
}
</style>
<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 { useTask } 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 } = useTask(
() => {
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
onpinchend={() => (debug = !debug)}
/>
<Hand
right
onpinchend={() => (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]}
oncreate={(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]}
/>