@threlte/rapier
useRevoluteJoint
Use this hook to initialize a RevoluteImpulseJoint.
A revolute joint is a hinge — it pins two rigid bodies together at a shared point and allows rotation around a single axis, locking the other five degrees of freedom. Reach for it for doors, wheel axles, pendulums, levers, and articulated arms.
<script lang="ts">
import { Canvas } from '@threlte/core'
import { HTML } from '@threlte/extras'
import { World } from '@threlte/rapier'
import { Button, Checkbox, Pane } from 'svelte-tweakpane-ui'
import Scene from './Scene.svelte'
let debug = $state(false)
let resetKey = $state(0)
</script>
<Pane
position="fixed"
title="Revolute Joint"
>
<Button
title="Reset"
on:click={() => resetKey++}
/>
<Checkbox
bind:value={debug}
label="Debug"
/>
</Pane>
<div>
<Canvas>
<World>
<Scene
{debug}
{resetKey}
/>
{#snippet fallback()}
<HTML transform>
<p class="text-xs">
It seems your browser<br />
doesn't support WASM.<br />
I'm sorry.
</p>
</HTML>
{/snippet}
</World>
</Canvas>
</div>
<style>
div {
height: 100%;
}
</style>
<script lang="ts">
import { T } from '@threlte/core'
import { Collider, CollisionGroups, RigidBody, useRevoluteJoint } from '@threlte/rapier'
const { rigidBodyA, rigidBodyB } = useRevoluteJoint([0, 0, 0], [0, 2, 0], [0, 0, 1])
</script>
<CollisionGroups
memberships={[1]}
filter={[0]}
>
<T.Group position={[0, 6, 0]}>
<RigidBody
type="fixed"
bind:rigidBody={$rigidBodyA}
>
<T.Mesh>
<T.BoxGeometry args={[0.3, 0.3, 0.3]} />
<T.MeshStandardMaterial color="#222" />
</T.Mesh>
</RigidBody>
</T.Group>
<T.Group
position={[1.732, 5, 0]}
rotation={[0, 0, Math.PI / 3]}
>
<RigidBody bind:rigidBody={$rigidBodyB}>
<Collider
shape="cuboid"
args={[0.2, 1.5, 0.2]}
density={1}
/>
<T.Group position.y={-1.7}>
<Collider
shape="ball"
args={[0.5]}
density={20}
/>
</T.Group>
<T.Mesh castShadow>
<T.BoxGeometry args={[0.4, 3, 0.4]} />
<T.MeshStandardMaterial color="#8B5A2B" />
</T.Mesh>
<T.Mesh
castShadow
position.y={-1.7}
>
<T.SphereGeometry args={[0.5, 24, 16]} />
<T.MeshStandardMaterial
color="#222"
metalness={0.8}
roughness={0.3}
/>
</T.Mesh>
</RigidBody>
</T.Group>
</CollisionGroups>
<script lang="ts">
import { T } from '@threlte/core'
import { OrbitControls, SoftShadows } from '@threlte/extras'
import { Collider, Debug, RigidBody } from '@threlte/rapier'
import Pendulum from './Pendulum.svelte'
interface Props {
debug: boolean
resetKey: number
}
let { debug, resetKey }: Props = $props()
const stack = Array.from({ length: 4 }, (_, i) => i)
</script>
<T.PerspectiveCamera
makeDefault
position={[6, 4, 10]}
fov={50}
>
<OrbitControls
enableDamping
enableZoom={false}
target={[0, 2.5, 0]}
/>
</T.PerspectiveCamera>
<T.DirectionalLight
castShadow
intensity={2}
position={[8, 20, -3]}
shadow.camera.top={-20}
shadow.camera.bottom={20}
shadow.mapSize.width={1024}
shadow.mapSize.height={1024}
/>
<T.AmbientLight intensity={1} />
<SoftShadows />
{#if debug}
<Debug />
{/if}
{#key resetKey}
<Pendulum />
{#each stack as i}
<T.Group position={[-0.5, 0.5 + i, 0]}>
<RigidBody>
<Collider
shape="cuboid"
args={[0.5, 0.5, 0.5]}
/>
<T.Mesh castShadow>
<T.BoxGeometry args={[1, 1, 1]} />
<T.MeshStandardMaterial color="#335086" />
</T.Mesh>
</RigidBody>
</T.Group>
{/each}
{/key}
<T.Group position={[0, -0.5, 0]}>
<RigidBody type="fixed">
<Collider
shape="cuboid"
args={[10, 0.5, 5]}
/>
<T.Mesh receiveShadow>
<T.BoxGeometry args={[20, 1, 10]} />
<T.MeshStandardMaterial color="#888" />
</T.Mesh>
</RigidBody>
</T.Group>
<script>
import { useRevoluteJoint, RigidBody, Collider } from '@threlte/rapier'
const { joint, rigidBodyA, rigidBodyB } = useRevoluteJoint({ x: 1 }, {}, { y: 1 })
</script>
<RigidBody bind:rigidBody={$rigidBodyA}>
<Collider
shape="cuboid"
args={[1, 1, 1]}
/>
</RigidBody>
<RigidBody bind:rigidBody={$rigidBodyB}>
<Collider
shape="cuboid"
args={[1, 1, 1]}
/>
</RigidBody>
Signature
const {
joint: Writable<RevoluteImpulseJoint>
rigidBodyA: Writable<RAPIER.RigidBody>
rigidBodyB: Writable<RAPIER.RigidBody>
} = useRevoluteJoint(
anchorA, // Position
anchorB, // Position
axis, // Rotation
limits // [min, max] | undefined
)