Skip to main content

Physics

XR Blocks runs Rapier in the engine lifecycle. The application supplies one compatible Rapier module before xb.init(). XR Blocks then creates and steps one xb.core.physics.blendedWorld.

Configure Rapier

Use the same package in JavaScript and in the import map or bundler graph. The in-tree examples use the SIMD-compatible build:

import RAPIER from '@dimforge/rapier3d-simd-compat';
import * as xb from 'xrblocks';

const options = new xb.Options();
options.physics.RAPIER = RAPIER;

xb.add(new PhysicsScene());
await xb.init(options);

The browser import map must also map @dimforge/rapier3d-simd-compat. See templates/10_environment_physics/index.html for an aligned example. Do not load a second Rapier variant through another URL.

For TypeScript, map the virtual rapier3d type import to the installed Rapier package:

{
"compilerOptions": {
"paths": {
"rapier3d": ["./node_modules/@dimforge/rapier3d-simd-compat/rapier"]
}
}
}

Use the script lifecycle

Create bodies and colliders in initPhysics(physics). Copy simulated poses to Three.js objects in physicsStep(). The physics argument is the initialized manager; use its RAPIER module and blendedWorld instead of creating another world.

import * as THREE from 'three';
import * as xb from 'xrblocks';

class FallingCube extends xb.Script {
init() {
this.mesh = new THREE.Mesh(
new THREE.BoxGeometry(0.2, 0.2, 0.2),
new THREE.MeshStandardMaterial({color: 0x4285f4})
);
this.mesh.position.set(0, 1.5, -0.8);
this.add(this.mesh);
}

initPhysics(physics) {
this.physics = physics;
const {RAPIER, blendedWorld} = physics;

this.body = blendedWorld.createRigidBody(
RAPIER.RigidBodyDesc.dynamic().setTranslation(
this.mesh.position.x,
this.mesh.position.y,
this.mesh.position.z
)
);
blendedWorld.createCollider(
RAPIER.ColliderDesc.cuboid(0.1, 0.1, 0.1),
this.body
);
}

physicsStep() {
if (!this.body) return;
this.mesh.position.copy(this.body.translation());
this.mesh.quaternion.copy(this.body.rotation());
}

dispose() {
if (this.body) this.physics?.blendedWorld.removeRigidBody(this.body);
this.mesh.geometry.dispose();
this.mesh.material.dispose();
}
}

Rapier collider half-extents and Three.js geometry dimensions are different: BoxGeometry(0.2, 0.2, 0.2) matches ColliderDesc.cuboid(0.1, 0.1, 0.1).

Add a static floor

A fixed body does not move under gravity:

const floorBody = physics.blendedWorld.createRigidBody(
physics.RAPIER.RigidBodyDesc.fixed().setTranslation(0, 0, 0)
);
physics.blendedWorld.createCollider(
physics.RAPIER.ColliderDesc.cuboid(2, 0.05, 2),
floorBody
);

Add a matching Three.js mesh when the floor must be visible. A collider alone has no rendered surface.

Physics and manipulation

Automatic manipulation changes the Three.js transform. Rapier changes a rigid body transform. If both systems own the same object at the same time, they compete.

Choose one explicit policy:

  • Make manipulation move a kinematic body, then return it to the intended body mode when manipulation ends.
  • Disable automatic manipulation and apply forces or impulses from interaction events.
  • Use direct-hand collision logic when the hand must physically strike an object.

Read Interaction.md for manipulation ownership and event phases. A generic grab callback does not automatically update a Rapier body.

Options and ownership

options.physics defaults are:

options.physics.fps = 45;
options.physics.gravity = {x: 0, y: -9.81, z: 0};
options.physics.worldStep = true;
options.physics.useEventQueue = false;

Set worldStep = false only when application code will step the world. Set useEventQueue = true before initialization when collision events require a Rapier event queue.

The engine owns the Rapier world and event queue. Each application script owns the bodies and colliders it creates and must remove them during dispose(). The script also owns its Three.js geometry and material resources.

Depth-mesh collision

Depth sensing and physics are separate switches. A depth preset can configure depth-mesh colliders, but options.physics.RAPIER still enables physics. Start from templates/10_environment_physics when virtual projectiles must collide with reconstructed room geometry.