Skip to content

@bluera/vue-threejs-rapier

Declarative rigid-body physics for vue-threejs, powered by Rapier.

Ported from @react-three/rapier — the React Three Fiber integration for the Rapier physics engine. The Vue version provides the same component and composable API adapted for Vue's reactivity system.

Availability

@bluera/vue-threejs-rapier is included in the monorepo. Install the core package to use it:

Plugin registration

Register as a fiber plugin to set global defaults:

ts
import { createRapierPlugin } from '@bluera/vue-threejs-rapier'

<Canvas :plugins="[createRapierPlugin({ gravity: [0, -9.81, 0] })]">
ts
// Or app-wide
import { registerFiberPlugin } from '@bluera/vue-threejs'
import { rapierFiberPlugin } from '@bluera/vue-threejs-rapier'

registerFiberPlugin(app, rapierFiberPlugin)

Usage

Wrap physics objects in a Physics world and add RigidBody components:

vue
<script setup>
import { Canvas } from '@bluera/vue-threejs'
import { Physics, RigidBody, CuboidCollider } from '@bluera/vue-threejs-rapier'
</script>

<template>
  <Canvas>
    <ambientLight />
    <Physics>
      <!-- Dynamic falling box -->
      <RigidBody>
        <mesh>
          <boxGeometry />
          <meshStandardMaterial color="orange" />
        </mesh>
      </RigidBody>

      <!-- Static ground plane -->
      <RigidBody type="fixed">
        <CuboidCollider :args="[10, 0.1, 10]" />
        <mesh>
          <boxGeometry :args="[20, 0.2, 20]" />
          <meshStandardMaterial color="green" />
        </mesh>
      </RigidBody>
    </Physics>
  </Canvas>
</template>

Components

Physics

The physics world container. All rigid bodies and colliders must be descendants of Physics.

PropTypeDefaultDescription
gravitynumber[][0, -9.81, 0]World gravity vector
timestepnumber1/60Fixed timestep
interpolatebooleantrueInterpolate between physics steps
pausedbooleanfalsePause the simulation
debugbooleanfalseRender debug wireframes

RigidBody

A physics-enabled rigid body. Children meshes are automatically synced to the body's transform.

PropTypeDefaultDescription
typestringdynamicdynamic, fixed, kinematicPosition, kinematicVelocity
positionnumber[][0,0,0]Initial position
rotationnumber[][0,0,0]Initial rotation (Euler)
gravityScalenumber1Per-body gravity multiplier
restitutionnumber0Bounciness (0 = no bounce, 1 = full bounce)
frictionnumber0.5Surface friction

Debug

Renders wireframe outlines for all colliders in the physics world.

vue
<Physics>
  <Debug />
  <!-- bodies -->
</Physics>

Colliders

Collider components define the physics shape. Place them inside a RigidBody.

ComponentShapeArgs
BallColliderSphere[radius]
CuboidColliderBox[halfX, halfY, halfZ]
CapsuleColliderCapsule[halfHeight, radius]
CylinderColliderCylinder[halfHeight, radius]
ConeColliderCone[halfHeight, radius]
TrimeshColliderTriangle mesh[vertices, indices]
HeightfieldColliderHeightfield[rows, cols, heights, scale]

If no explicit collider is provided, RigidBody auto-generates colliders from child mesh geometries.

vue
<RigidBody>
  <BallCollider :args="[0.5]" />
  <mesh>
    <sphereGeometry :args="[0.5]" />
    <meshStandardMaterial />
  </mesh>
</RigidBody>

Joints

Connect two rigid bodies with a constraint.

ComponentDescription
FixedJointLocks bodies together rigidly
SphericalJointBall-and-socket joint
RevoluteJointHinge joint (single axis rotation)
PrismaticJointSlider joint (single axis translation)
RopeJointMax-distance constraint
SpringJointDamped spring between bodies
vue
<RigidBody ref="bodyA">
  <!-- ... -->
</RigidBody>
<RigidBody ref="bodyB">
  <!-- ... -->
</RigidBody>
<SphericalJoint :body-a="bodyA" :body-b="bodyB" :anchor-a="[0, 1, 0]" :anchor-b="[0, -1, 0]" />

Composables

useRapier

Access the Rapier world and API from any descendant of Physics.

ts
import { useRapier } from '@bluera/vue-threejs-rapier'

const { world, rapier } = useRapier()

useRigidBody

Access the rigid body API from within a RigidBody.

ts
import { useRigidBody } from '@bluera/vue-threejs-rapier'

const { rigidBody, api } = useRigidBody()
// api.applyImpulse([0, 5, 0])

useCollider

Access collider data from within a collider component.

ts
import { useCollider } from '@bluera/vue-threejs-rapier'

const { collider } = useCollider()

useBeforePhysicsStep / useAfterPhysicsStep

Run logic before or after each physics step.

ts
import { useBeforePhysicsStep, useAfterPhysicsStep } from '@bluera/vue-threejs-rapier'

useBeforePhysicsStep((world) => {
  // apply forces, read positions, etc.
})

useAfterPhysicsStep((world) => {
  // read results after simulation
})

See also