# Physics

> Reference for @irt/physics, Rapier 2D and 3D as a stepper shared by rooms and client prediction.

`@irt/physics` has one entry per engine, each needing its Rapier package
installed beside it:

| Entry | Needs |
|---|---|
| `@irt/physics/rapier2d` | `@dimforge/rapier2d-compat` 0.21.0 |
| `@irt/physics/rapier3d` | `@dimforge/rapier3d-compat` 0.21.0 |

Both export the same names. Rapier is loaded by the time the import
resolves. The [physics guide](/docs/guides/physics) explains how a tick
runs, prediction and saving.

```ts
import { physics, body, castRay, RAPIER, type BodyOptions, type BodySpec, type BodyType, type Control, type PhysicsOptions, type PhysicsStepper, type Pose } from "@irt/physics/rapier2d";
```

## physics

```ts
function physics<I>(options: {
  gravity: { x: number; y: number };            // 3D: { x, y, z }
  bodies: readonly BodySpec<I>[];
  parseInput?(raw: unknown): I;                 // as a stepper's parseInput, on the room only
}): PhysicsStepper<I>;

interface PhysicsStepper<I> extends Stepper<I> {
  rapierWorld(world: World): RAPIER.World | undefined;
  bodyOf(world: World, entity: Entity | number): RAPIER.RigidBody | undefined;
  entityOf(world: World, of: RAPIER.RigidBody | RAPIER.Collider): Entity | undefined;
}
```

The result is a [stepper](/docs/guides/prediction#the-stepper): pass it
as the room's `stepper` and to `room.predict`. It keeps one Rapier world
per room or prediction, keyed by the `world` it's stepped with, so one
stepper object serves every room in a room host.

- `rapierWorld(world)` is the Rapier world for `room.entities`, for ray
  casts and other queries. It's undefined until the room's first tick or
  wake, and after the room has left the room host. Forces and impulses
  can be applied to its bodies, but set poses through the entities'
  fields.
- `bodyOf(world, entity)` is an entity's body, by entity or ID, and
  `entityOf(world, bodyOrCollider)` the entity a body belongs to, or the
  entity of the body a collider is attached to, such as a ray cast's
  hit. Both are undefined for what has no body, including an entity in
  the tick it's created, before the stepper has run. A body can be made
  again, so look it up rather than keep it across ticks.
- `save` returns bytes when the world has a dynamic body: the Rapier
  world and which entity each body belongs to. `load` restores them and
  reconciles with the entities. Without dynamic bodies, `save` returns
  undefined and `load` makes every body from its entity.
- `release` frees a room's Rapier world; the room host calls it when the
  room leaves, and a prediction when it stops.

`physics` throws when a kind is listed twice, when a kind lacks a
position field or has only part of a group (`vx` without `vy`), when a
pose field isn't `f32` or `f64`, and when a pose field is writable.

## body

```ts
function body<K extends Kind, I>(kind: K, options: BodyOptions<K, I>): BodySpec<I>;

type BodyType = "dynamic" | "fixed" | "kinematic" | "character";

interface BodyOptions<K, I> {
  type: BodyType;
  colliders(entity: Entity<K>): RAPIER.ColliderDesc | RAPIER.ColliderDesc[];
  shapeKey?(entity: Entity<K>): unknown;
  reshape?(colliders: RAPIER.Collider[], entity: Entity<K>, before: Entity<K>): void;
  body?(desc: RAPIER.RigidBodyDesc, entity: Entity<K>): void;
  player?(entity: Entity<K>): string | undefined;
  control?(control: Control<K>, input: I | undefined, dt: number): void;
  character?(controller: RAPIER.KinematicCharacterController): void;
}
```

| Option | Meaning |
|---|---|
| `type` | `dynamic` is simulated; `fixed` never moves; `kinematic` moves where `control` sets it with `body.setNextKinematicTranslation`; `character` is kinematic and moves with `control.move` |
| `colliders` | The shapes of an entity's body, made with the body and again when `shapeKey` changes. A fixed body's colliders are made with density 0, since its mass is never used; the description itself is left as it was |
| `shapeKey` | A value compared with `!==` each tick, on the room and in prediction, that changes whenever `colliders` would return different shapes, such as an edit count. When it changes, the colliders are updated by `reshape`, or made again. Bodies in contact with them are woken |
| `reshape` | Updates the colliders in place after `shapeKey` changed, such as with `collider.setVoxel`, given the entity and `before`, a copy of the entity's values when the colliders were last made or reshaped. It mustn't add or remove colliders. Colliders restored from a saved world are made again instead |
| `body` | Adjusts the body description before the body is made: damping, CCD, gravity scale, initial velocity |
| `player` | The player whose input `control` gets for this entity |
| `control` | Runs each tick before the world steps: on the room for every body of the kind, on a client only for the predicted entity's. `input` is undefined when the player sent none for the tick, or there's no `player` |
| `character` | Configures the kind's character controller once per world: slopes, steps, snapping to the ground, pushing dynamic bodies |

Pose fields, by name:

| | 2D | 3D |
|---|---|---|
| Position, required | `x`, `y` | `x`, `y`, `z` |
| Rotation; without it the body doesn't rotate | `angle` | `qx`, `qy`, `qz`, `qw` |
| Velocity, `dynamic` only | `vx`, `vy` | `vx`, `vy`, `vz` |
| Angular velocity, `dynamic` only | `spin` | `wx`, `wy`, `wz` |

## Control

```ts
interface Control<K> {
  readonly tick: number;          // the room tick being stepped, the same on the room and in a replay
  readonly entity: Entity<K>;     // write the kind's other fields as game state
  readonly body: RAPIER.RigidBody;
  readonly world: RAPIER.World;
  move(dx: number, dy: number): boolean;   // 3D: move(dx, dy, dz)
}
```

`move` asks the character controller to move a `character` body by up
to the given distance this tick, sliding along what it hits, and
returns whether the body ends on the ground. Call it once per tick; a
second call replaces the first. It throws for other body types. A
movement no longer than twice the controller's normal nudge factor,
1e-4 by default, is dropped: the nudge and snapping to the ground
otherwise move a character standing across an edge up and down every
tick.

## castRay

```ts
function castRay<T extends Pose>(ray: RAPIER.Ray, maxToi: number, targets: Iterable<T>, shape: (target: T) => RAPIER.Shape): { target: T; toi: number } | undefined;

interface Pose { readonly x: number; readonly y: number; readonly angle?: number }   // 3D: x, y, z and optional qx, qy, qz, qw
```

The nearest of `targets` that the ray hits within `maxToi`, each placed
at its pose with the shape `shape` returns for it, such as
`new RAPIER.Capsule(0.5, 0.3)`, and the time of impact in units of
`ray.dir`; `undefined` when it hits none. A target without rotation
fields isn't rotated. A ray that starts inside a shape hits it at 0.

It needs no Rapier world, so it checks a shot against poses from
[`room.rewind`](/docs/reference/room-sdk#history), which are where the
shooter saw the targets; see [Lag compensation](/docs/guides/lag-compensation#with-physics).
It visits every target, so pass only the ones in range when there are
many.

## RAPIER

The Rapier module the entry loaded, for `ColliderDesc`, `RigidBodyDesc`,
ray casts and the rest of Rapier's API. Importing
`@dimforge/rapier2d-compat` directly gives the same module.
