angular-three-rapier
v4.0.9
Published
Physics Rapier for Angular Three
Maintainers
Readme
angular-three-rapier
This library provides Rapier physics integration for Angular Three. Rapier is a fast, cross-platform physics engine written in Rust with JavaScript bindings.
Documentation
All public APIs are documented with JSDoc comments. Your IDE will provide inline documentation, parameter hints, and examples as you code.
Installation
npm install angular-three-rapier @dimforge/rapier3d-compat
# yarn add angular-three-rapier @dimforge/rapier3d-compat
# pnpm add angular-three-rapier @dimforge/rapier3d-compatMake sure to already have
angular-threeinstalled
Usage
import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { extend, NgtArgs } from 'angular-three';
import { NgtrPhysics, NgtrRigidBody } from 'angular-three-rapier';
import * as THREE from 'three';
extend(THREE);
@Component({
selector: 'app-box',
template: `
<ngt-object3D rigidBody [position]="[0, 5, 0]">
<ngt-mesh>
<ngt-box-geometry />
<ngt-mesh-standard-material color="hotpink" />
</ngt-mesh>
</ngt-object3D>
`,
imports: [NgtrRigidBody],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class Box {}
@Component({
selector: 'app-ground',
template: `
<ngt-object3D rigidBody="fixed" [position]="[0, -1, 0]">
<ngt-mesh>
<ngt-box-geometry *args="[20, 1, 20]" />
<ngt-mesh-standard-material color="gray" />
</ngt-mesh>
</ngt-object3D>
`,
imports: [NgtrRigidBody, NgtArgs],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class Ground {}
@Component({
template: `
<ngtr-physics [options]="{ gravity: [0, -9.81, 0] }">
<ng-template>
<app-box />
<app-ground />
</ng-template>
</ngtr-physics>
`,
imports: [NgtrPhysics, Box, Ground],
})
export class SceneGraph {}Physics Options
| Property | Description | Default |
| ------------- | ----------------------------------------------- | --------------- |
| gravity | Gravity vector [x, y, z] | [0, -9.81, 0] |
| colliders | Default collider type for rigid bodies | 'cuboid' |
| paused | Whether physics simulation is paused | false |
| timeStep | Fixed timestep for physics simulation | 1/60 |
| debug | Enable debug visualization | false |
| interpolate | Enable transform interpolation | true |
| updateLoop | Update loop type: 'follow' or 'independent' | 'follow' |
Rigid Body
Use ngt-object3D with the rigidBody attribute. The rigid body type is specified as the attribute value:
<!-- Dynamic (default when empty) -->
<ngt-object3D rigidBody [position]="[0, 5, 0]">
<ngt-mesh>...</ngt-mesh>
</ngt-object3D>
<!-- Fixed (static) -->
<ngt-object3D rigidBody="fixed" [position]="[0, -1, 0]">
<ngt-mesh>...</ngt-mesh>
</ngt-object3D>
<!-- Kinematic -->
<ngt-object3D rigidBody="kinematicPosition">
<ngt-mesh>...</ngt-mesh>
</ngt-object3D>Rigid Body Types
''or'dynamic'- Affected by forces and collisions'fixed'- Static, immovable body'kinematicPosition'- Controlled by position, affects dynamic bodies'kinematicVelocity'- Controlled by velocity, affects dynamic bodies
Rigid Body Options
<ngt-object3D
rigidBody
[options]="{
colliders: 'ball',
ccd: true,
gravityScale: 0.5,
linearVelocity: [0, 10, 0],
angularVelocity: [0, 1, 0]
}"
>
...
</ngt-object3D>Rigid Body Events
<ngt-object3D
rigidBody
(collisionEnter)="onCollisionEnter($event)"
(collisionExit)="onCollisionExit($event)"
(intersectionEnter)="onIntersectionEnter($event)"
(intersectionExit)="onIntersectionExit($event)"
(contactForce)="onContactForce($event)"
(sleep)="onSleep()"
(wake)="onWake()"
>
...
</ngt-object3D>Colliders
Colliders use ngt-object3D with specific collider attributes. By default, rigid bodies auto-generate colliders from child meshes.
Explicit Colliders
<!-- Ball collider -->
<ngt-object3D [ballCollider]="[0.5]" [position]="[0, 2, 0]" />
<!-- Cuboid collider (half-extents) -->
<ngt-object3D [cuboidCollider]="[1, 0.5, 2]" [position]="[0, 0, 0]" />
<!-- Capsule collider (half-height, radius) -->
<ngt-object3D [capsuleCollider]="[0.5, 0.25]" [position]="[0, 1, 0]" />
<!-- Cylinder collider (half-height, radius) -->
<ngt-object3D [cylinderCollider]="[1, 0.5]" />
<!-- Cone collider (half-height, radius) -->
<ngt-object3D [coneCollider]="[1, 0.5]" />Available Collider Directives
| Directive | Args | Description |
| --------------------------- | --------------------------------- | ------------------- |
| NgtrBallCollider | [radius] | Sphere shape |
| NgtrCuboidCollider | [halfW, halfH, halfD] | Box shape |
| NgtrCapsuleCollider | [halfHeight, radius] | Capsule shape |
| NgtrCylinderCollider | [halfHeight, radius] | Cylinder shape |
| NgtrConeCollider | [halfHeight, radius] | Cone shape |
| NgtrConvexHullCollider | [vertices] | Convex hull |
| NgtrTrimeshCollider | [vertices, indices] | Triangle mesh |
| NgtrHeightfieldCollider | [width, height, heights, scale] | Terrain heightfield |
| NgtrRoundCuboidCollider | [halfW, halfH, halfD, radius] | Rounded box |
| NgtrRoundCylinderCollider | [halfH, radius, borderRadius] | Rounded cylinder |
| NgtrRoundConeCollider | [halfH, radius, borderRadius] | Rounded cone |
Disabling Auto-Colliders
<ngt-object3D rigidBody [options]="{ colliders: false }">
<!-- Manual colliders only -->
<ngt-object3D [ballCollider]="[0.5]" />
</ngt-object3D>Mesh Collider
Generate colliders from mesh geometry:
<ngt-object3D rigidBody [options]="{ colliders: false }">
<ngt-object3D [meshCollider]="'trimesh'">
<ngt-mesh>
<ngt-torus-geometry />
<ngt-mesh-standard-material />
</ngt-mesh>
</ngt-object3D>
</ngt-object3D>Joints
Create joints between rigid bodies using injectable functions:
import { Component, viewChild } from '@angular/core';
import {
NgtrRigidBody,
sphericalJoint,
revoluteJoint,
prismaticJoint,
fixedJoint,
ropeJoint,
springJoint,
} from 'angular-three-rapier';
@Component({
template: `
<ngt-object3D rigidBody="fixed" #bodyA="rigidBody">...</ngt-object3D>
<ngt-object3D rigidBody #bodyB="rigidBody">...</ngt-object3D>
`,
})
export class JointExample {
bodyA = viewChild.required<NgtrRigidBody>('bodyA');
bodyB = viewChild.required<NgtrRigidBody>('bodyB');
// Spherical joint (ball-and-socket)
joint = sphericalJoint(
() => this.bodyA().rigidBody(),
() => this.bodyB().rigidBody(),
{
data: {
body1Anchor: [0, -0.5, 0],
body2Anchor: [0, 0.5, 0],
},
},
);
// Revolute joint (hinge) with limits
hingeJoint = revoluteJoint(
() => this.bodyA().rigidBody(),
() => this.bodyB().rigidBody(),
{
data: {
body1Anchor: [0, 0, 0],
body2Anchor: [0, 1, 0],
axis: [0, 1, 0],
limits: [-Math.PI / 2, Math.PI / 2],
},
},
);
// Prismatic joint (slider)
sliderJoint = prismaticJoint(
() => this.bodyA().rigidBody(),
() => this.bodyB().rigidBody(),
{
data: {
body1Anchor: [0, 0, 0],
body2Anchor: [2, 0, 0],
axis: [1, 0, 0],
limits: [-1, 1],
},
},
);
// Rope joint (max distance constraint)
rope = ropeJoint(
() => this.bodyA().rigidBody(),
() => this.bodyB().rigidBody(),
{
data: {
body1Anchor: [0, 0, 0],
body2Anchor: [0, 0, 0],
length: 5,
},
},
);
// Spring joint
spring = springJoint(
() => this.bodyA().rigidBody(),
() => this.bodyB().rigidBody(),
{
data: {
body1Anchor: [0, 0, 0],
body2Anchor: [0, 0, 0],
restLength: 2,
stiffness: 100,
damping: 10,
},
},
);
}Instanced Rigid Bodies
For efficient physics with many identical objects:
import { NgtArgs } from 'angular-three';
import { NgtrInstancedRigidBodies } from 'angular-three-rapier';
@Component({
template: `
<ngt-object3D [instancedRigidBodies]="instances">
<ngt-instanced-mesh [count]="instances.length" castShadow>
<ngt-sphere-geometry *args="[0.5]" />
<ngt-mesh-standard-material color="orange" />
</ngt-instanced-mesh>
</ngt-object3D>
`,
imports: [NgtrInstancedRigidBodies, NgtArgs],
})
export class Spheres {
instances = Array.from({ length: 100 }, (_, i) => ({
key: i,
position: [Math.random() * 10, Math.random() * 10, 0] as [number, number, number],
}));
}Interaction Groups
Filter collisions between objects:
import { interactionGroups } from 'angular-three-rapier';
// Member of group 0, collides with groups 0 and 1
const groups = interactionGroups([0], [0, 1]);<!-- Using directive -->
<ngt-object3D rigidBody [interactionGroups]="[[0], [0, 1]]">...</ngt-object3D>Physics Hooks
import { beforePhysicsStep, afterPhysicsStep } from 'angular-three-rapier';
@Component({...})
export class MyComponent {
constructor() {
beforePhysicsStep((world) => {
// Run before each physics step
});
afterPhysicsStep((world) => {
// Run after each physics step
});
}
}Debug Visualization
Enable debug rendering via physics options:
<ngtr-physics [options]="{ debug: true }">
<ng-template>
<!-- your physics objects -->
</ng-template>
</ngtr-physics>Addons
Attractor
Apply gravitational or magnetic forces:
import { NgtrAttractor } from 'angular-three-rapier/addons';<!-- Attractor with default options -->
<ngt-object3D attractor />
<!-- Attractor with custom options -->
<ngt-object3D [attractor]="{ strength: 10, range: 20, type: 'linear' }" />
<!-- Repeller (negative strength) -->
<ngt-object3D [attractor]="{ strength: -10, range: 15 }" [position]="[5, 0, 0]" />
<!-- Newtonian gravity -->
<ngt-object3D
[attractor]="{
strength: 1000,
range: 50,
type: 'newtonian',
gravitationalConstant: 0.01
}"
/>