Skip to content

Latest commit

 

History

History
409 lines (336 loc) · 10.1 KB

File metadata and controls

409 lines (336 loc) · 10.1 KB

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-compat

Make sure to already have angular-three installed

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
	}"
/>