# @ferret/physics: API

Layers, triggers and raycasts; the rigid-body kernels (light and Havok); the capability host with scoped configuration; budgets and tiers.

95 exports. Generated from the source (`packages/physics/src`), so it matches the code.

## Classes

### Collider

*class* · `packages/physics/src/physics.ts:27`

```ts
class Collider extends Component<ColliderParams>
```

Anything on a layer that can be overlapped: triggers, interactables, characters. Static level geometry uses `markStatic` instead.

- `shape: ShapeKind`
- `layerBit: number`
- `maskBits: number`
- `physics: PhysicsService` — Set by the physics service when the component is attached.
- `onAttach()`
- `onDetach()`
- `get isTrigger()`
- `center(out = new BABYLON.Vector3()): BABYLON.Vector3`

### CollisionLayers

*class* · `packages/physics/src/layers.ts:2`

```ts
class CollisionLayers
```

UE-style object channels: every collider is on one layer and tests against a mask of layers.

- `constructor(names: string[] = [])`
- `define(name: string): number`
- `bit(name: string): number`
- `mask(...names: string[]): number` — A mask from layer names; '*' = all layers.
- `names(): string[]`
- `namesOf(mask: number): string[]`

### Governor

*class* · `packages/physics/src/budget.ts:57`

```ts
class Governor
```

Watches the world step and gives up detail in the order of `rules` when it stays over budget, then takes it back when there is
room again. It acts on the numbers the capabilities already read and on switching capabilities off; the events say what happened
(`physics:degrade` { level, rule, meanMs, budgetMs }), so a game can tell the player or log it.

- `readonly budget: TierBudget`
- `readonly rules: DegradeRule[]`
- `level: number`
- `constructor(private host: PhysicsHost, readonly opts: GovernorOptions = {})`
- `observe(ms: number)` — Called with the time of each step.

### HavokKernel

*class* · `packages/physics/src/havok-kernel.ts:36`

```ts
class HavokKernel implements PhysicsKernel
```

The rigid-body kernel on Havok's own API (no Babylon scene needed, so it runs headless and in tests): bodies of any shape that move,
turn with their full inertia, stack and rest; joints that report their force and break; swept collision. The game loads the WASM
(`await HavokPhysics()`) and hands the module in, so a game that does not want rigid bodies does not carry it.

What Havok does not do the same way as the light kernel: contacts cannot be changed before they are solved (`caps.contactHooks` is
false: friction and restitution come from the materials, combined by the library's rules; a pair table is not used), and there is
no rolling resistance (a capability adds it as a torque).

- `readonly name: "havok"`
- `readonly caps: KernelCaps`
- `gravity: Vec3`
- `readonly materials: MaterialLibrary`
- `substeps: number`
- `readonly bodies: Body[]`
- `readonly constraints: Constraint[]`
- `readonly world: HP_WorldId`
- `constructor(readonly hk: HavokPhysicsWithBindings, o: HavokKernelOptions = {})`
- `dispose()` — Free the Havok world and everything in it.
- `addBody(def: BodyDef): Body`
- `removeBody(b: Body)`
- `setMassProperties(b: Body, mass: number, centreOfMass: Vec3, inertia: Vec3)` — Mass, centre of mass (body space) and principal inertia set from outside (a vessel whose tanks fill); where the kernel can.
- `setSurface(b: Body, sf: { friction: number; kineticFriction: number; restitution: number })` — A body's own friction and restitution, over its material's: how a kernel without contact hooks takes a capability's dials.
- `centreOf(b: Body): Vec3` — World position of the body's centre of mass.
- `applyForce(b: Body, f: Vec3, at?: Vec3)`
- `applyImpulse(b: Body, j: Vec3, at?: Vec3)`
- `applyTorque(b: Body, t: Vec3)`
- `setVelocity(b: Body, v: Vec3)`
- `setPosition(b: Body, p: Vec3)`
- `sleep(b: Body)`
- `wake(b: Body)`
- `addForceField(fn: (b: Body, h: number) => Vec3 | FieldForce | null)` — A force that depends on where a body is (gravity, drag, buoyancy): evaluated at the position of every sub-step, so a fast or orbiting body gets the force it feels where it is, not where it was at the start of the step. Returns newtons, or null for none.
- `beforeSolve(_cb: (c: Contact) => void)` — Havok solves contacts inside its step: a hook is accepted and never called (`caps.contactHooks` is false).
- `onContact(cb: (e: ContactEvent) => void)` — Called after the step with what each contact did.
- `addConstraint(def: ConstraintDef): Constraint`
- `removeConstraint(c: Constraint)`
- `onConstraintBreak(cb: (c: Constraint) => void)` — Called after the step for each constraint whose force passed its limit; the constraint is already removed.
- `step(dt: number): void`
- `rayCast(origin: Vec3, dir: Vec3, maxDistance: number): KernelRayHit | null`

### LightKernel

*class* · `packages/physics/src/light-kernel.ts:29`

```ts
class LightKernel implements PhysicsKernel
```

A small deterministic rigid-body kernel: spheres that move, roll, slide and bounce against static planes, boxes and spheres and
against each other. No stacking of boxes and no joints; for projectiles, balls, debris, rolling and sliding tests, and as the
reference the capabilities are checked against. The same `PhysicsKernel` surface as a heavier backend, so a capability does not know.

- `readonly name: "light"`
- `readonly caps: KernelCaps`
- `gravity: Vec3`
- `readonly materials: MaterialLibrary`
- `substeps: number`
- `maxSubsteps: number`
- `iterations: number`
- `restitutionThreshold: number`
- `readonly bodies: Body[]`
- `readonly constraints: Constraint[]`
- `constructor(o: LightKernelOptions = {})`
- `addBody(def: BodyDef): Body`
- `removeBody(b: Body)`
- `addConstraint(def: ConstraintDef): Constraint`
- `removeConstraint(c: Constraint)`
- `onConstraintBreak(cb: (c: Constraint) => void)` — Called after the step for each constraint whose force passed its limit; the constraint is already removed.
- `applyForce(b: Body, f: Vec3, at?: Vec3)`
- `applyImpulse(b: Body, j: Vec3, at?: Vec3)`
- `applyTorque(b: Body, t: Vec3)`
- `setVelocity(b: Body, v: Vec3)`
- `setPosition(b: Body, p: Vec3)`
- `sleep(b: Body)`
- `wake(b: Body)`
- `addForceField(fn: (b: Body, h: number) => Vec3 | FieldForce | null)` — A force that depends on where a body is (gravity, drag, buoyancy): evaluated at the position of every sub-step, so a fast or orbiting body gets the force it feels where it is, not where it was at the start of the step. Returns newtons, or null for none.
- `beforeSolve(cb: (c: Contact) => void)` — Called for every contact before it is solved, so a capability can change friction or restitution.
- `onContact(cb: (e: ContactEvent) => void)` — Called after the step with what each contact did.
- `step(dt: number): void`
- `rayCast(origin: Vec3, dir: Vec3, maxDistance: number): KernelRayHit | null`

### MaterialLibrary

*class* · `packages/physics/src/materials.ts:57`

```ts
class MaterialLibrary
```

- `combine: CombineRules`
- `add(name: string, m: PhysicsMaterial): this`
- `get(name: string | undefined): PhysicsMaterial | undefined`
- `has(name: string)`
- `names()`
- `load(table: Record<string, PhysicsMaterial>): this` — Load `{name: material}`; throws with every problem listed when the table is invalid.
- `setPair(a: string, b: string, values: PhysicsMaterial): this` — The values for one pair of materials that override the combine rule (ice on rubber is not the mean of the two).
- `property(a: string | undefined, b: string | undefined, key: string): number | undefined` — One property of a pair: the pair table first, then the combine rule; a value only one side has is used as it is.
- `contact(a?: string, b?: string)` — What a contact between two materials uses.

### PhysicsHost

*class* · `packages/physics/src/host.ts:210`

```ts
class PhysicsHost implements Service
```

The home of the capabilities: the kernel, the materials, the registry and the fixed order of the step. A game registers the
capabilities it wants and switches them on (in code, in the manifest, or by command); nothing realistic runs otherwise.

- `readonly name: "physics.host"`
- `readonly kernel: PhysicsKernel`
- `readonly materials: MaterialLibrary`
- `readonly bodies: Set<RigidBody>`
- `readonly fields: PhysicsFields` — Fields that other code (a character, a vessel) may read when the capability behind them is on; each is set and cleared by its capability.
- `readonly capabilities: Map<string, Capability>`
- `world: World`
- `zoneAt(p: Vec3): string | null` — Which zone a point is in (none when the world has no ZoneService).
- `lastStepMs: number` — Seconds the last step's physics took (ms), for the budget view.
- `constructor(o: PhysicsHostOptions = {})`
- `init(world: World)`
- `dispose()`
- `register(cap: Capability): this`
- `config(name: string): ScopedConfig`
- `isEnabled(name: string)`
- `enabled(): string[]`
- `enable(name: string, values?: Record<string, unknown>): this`
- `disable(name: string): this`
- `configure(c: PhysicsConfig, scope: 'world' | 'level' = 'level')` — Apply a `physics` block (from a level manifest or a game): materials, pair rules, combine rules, then the capabilities with their values.
- `readonly fractures: Map<string, LevelFracture>` — Pre-fractured bodies of the level, by the name of the intact body's node (read by the strength capability when that body breaks).
- `governor: Governor | null` — Gives up detail when the step runs over its tier's budget (see `Governor`); none until a game sets one.
- `beginLevel(c: PhysicsConfig): string[]` — A level's physics block: applied now, undone by `endLevel` (the capabilities it switched on go off, its scoped values go). Constraints and fractures name nodes, so the level's bodies must exist already. Returns what could not be linked.
- `endLevel()`
- `fixedUpdate(dt: number)` — Called before the components' fixed update, at the fixed rate.

### PhysicsService

*class* · `packages/physics/src/physics.ts:58`

```ts
class PhysicsService implements Service
```

Collision layers, trigger volumes with enter/exit events, raycasts and overlap queries. Static level geometry is Babylon
meshes with `checkCollisions`; characters move against it (see

- `readonly name: "physics"`
- `readonly layers: CollisionLayers`
- `readonly colliders: Set<Collider>`
- `constructor(opts: PhysicsOptions = {})`
- `init(world: World)`
- `markStatic(root: BABYLON.TransformNode | BABYLON.AbstractMesh, layer = 'World')` — Make a node's meshes static world geometry: blocking for characters and hit by raycasts.
- `raycast(origin: BABYLON.Vector3, dir: BABYLON.Vector3, maxDistance: number, opts: { mask?: number; ignore?: (m: BABYLON.AbstractMesh) => boolean } = {}): RayHit | null` — First hit of a ray against static geometry (and optionally entity colliders' meshes) on the masked layers.
- `overlapSphere(center: BABYLON.Vector3, radius: number, mask = -1): Collider[]` — Colliders whose volume overlaps a sphere.
- `dropColliderPairs(c: Collider)`
- `fixedUpdate()` — Called before the components' fixed update, at the fixed rate.
- `occupants(trigger: Collider): Entity[]` — Who is inside a trigger right now.
- `dispose()`

### RigidBody

*class* · `packages/physics/src/host.ts:103`

```ts
class RigidBody extends Component<RigidBodyParams>
```

A node that the physics kernel moves: the node's position and rotation follow the body after every step.

- `host: PhysicsHost`
- `body: Body`
- `onAttach()`
- `onDetach()`
- `get velocity(): Vec3`

### ScopedConfig

*class* · `packages/physics/src/host.ts:42`

```ts
class ScopedConfig
```

A capability's values at four scopes; the narrowest that has the key wins: entity, zone, level, world, then the defaults.

- `constructor(private host: PhysicsHost, readonly defaults: Record<string, unknown> = {})`
- `set(scope: 'world' | 'level', key: string, value: unknown): void`
- `set(scope: 'zone' | 'entity', key: string, value: unknown, id: string): void`
- `set(scope: Scope, key: string, value: unknown, id?: string)`
- `assign(scope: 'world' | 'level', values: Record<string, unknown>)`
- `clearLevel()`
- `where(key: string, ctx: ScopeContext = {}): Scope | 'default' | undefined` — Which layer a value comes from for this context: the narrowest scope that has the key, or `default` / undefined when none has it.
- `get<T = number>(key: string, ctx: ScopeContext = {}): T`

## Functions

### add

*function* · `packages/physics/src/vec.ts:7`

```ts
function add: (a: Vec3, b: Vec3) => Vec3
```

### addTo

*function* · `packages/physics/src/vec.ts:15`

```ts
function addTo: (a: Vec3, b: Vec3, s?: number) => void
```

### boundingRadius

*function* · `packages/physics/src/shapes.ts:103`

```ts
function boundingRadius(s: Shape): number
```

Radius of the sphere around the body's origin that holds the shape.

### bounds

*function* · `packages/physics/src/shapes.ts:36`

```ts
function bounds(s: Shape): [Vec3, Vec3]
```

Axis-aligned bounds in body space.

### combineValues

*function* · `packages/physics/src/materials.ts:47`

```ts
function combineValues(rule: CombineRule, a: number, b: number): number
```

### contactScope

*function* · `packages/physics/src/host.ts:434`

```ts
function contactScope(a: Body, b: Body, point: Vec3): ScopeContext
```

Where to look up a capability's scoped values for a contact: the dynamic body's entity (else the other one), and the contact point (so a zone applies).

### copy

*function* · `packages/physics/src/vec.ts:16`

```ts
function copy: (a: Vec3) => Vec3
```

### cross

*function* · `packages/physics/src/vec.ts:11`

```ts
function cross: (a: Vec3, b: Vec3) => Vec3
```

### defaultRules

*function* · `packages/physics/src/budget.ts:34`

```ts
function defaultRules: (t: TierBudget) => DegradeRule[]
```

### dot

*function* · `packages/physics/src/vec.ts:10`

```ts
function dot: (a: Vec3, b: Vec3) => number
```

### entityOf

*function* · `packages/physics/src/host.ts:428`

```ts
function entityOf(b: Body): Entity | undefined
```

The entity a body belongs to (the RigidBody entity), when it has one.

### len

*function* · `packages/physics/src/vec.ts:12`

```ts
function len: (a: Vec3) => number
```

### len2

*function* · `packages/physics/src/vec.ts:13`

```ts
function len2: (a: Vec3) => number
```

### localSdf

*function* · `packages/physics/src/shapes.ts:8`

```ts
function localSdf(s: Shape): (x: number, y: number, z: number) => number
```

Geometry of the body shapes in body space, for capabilities that need more than a radius: a signed distance (negative inside), the
volume, the bounding box, and a lattice of sample points that stand for the volume (buoyancy, drag, heat, a liquid's walls).

### lowestBelow

*function* · `packages/physics/src/shapes.ts:145`

```ts
function lowestBelow(s: Shape, q: [number, number, number, number]): number
```

Height of the shape's lowest point below the body's origin, the body turned by q (the radius, for a sphere), m.

### norm

*function* · `packages/physics/src/vec.ts:14`

```ts
function norm: (a: Vec3) => Vec3
```

### overlapsSphere

*function* · `packages/physics/src/physics.ts:160`

```ts
function overlapsSphere(c: Collider, center: BABYLON.Vector3, radius: number): boolean
```

Is a world-space sphere inside the collider's volume (box: oriented; sphere; capsule)?

### overlapsTrigger

*function* · `packages/physics/src/physics.ts:174`

```ts
function overlapsTrigger(t: Collider, o: Collider): boolean
```

Does collider `o` overlap the trigger volume `t`? A capsule is tested as a swept sphere along its axis (several samples).

### projectedArea

*function* · `packages/physics/src/shapes.ts:110`

```ts
function projectedArea(s: Shape, q: [number, number, number, number], dir: Vec3): number
```

Area the shape shows to a direction (world `dir`, the body turned by `q`), m²: exact for a sphere and a box, from the volume otherwise.

### qFromAxisAngle

*function* · `packages/physics/src/vec.ts:25`

```ts
function qFromAxisAngle(axis: Vec3, angle: number): Quat
```

### qIdentity

*function* · `packages/physics/src/vec.ts:18`

```ts
function qIdentity: () => Quat
```

### qIntegrate

*function* · `packages/physics/src/vec.ts:38`

```ts
function qIntegrate(q: Quat, w: Vec3, dt: number): Quat
```

Integrate an orientation by an angular velocity over dt (exact for constant w).

### qInverse

*function* · `packages/physics/src/vec.ts:24`

```ts
function qInverse: (q: Quat) => Quat
```

### qMul

*function* · `packages/physics/src/vec.ts:29`

```ts
function qMul(a: Quat, b: Quat): Quat
```

### qRotate

*function* · `packages/physics/src/vec.ts:19`

```ts
function qRotate(q: Quat, v: Vec3): Vec3
```

### sampleShape

*function* · `packages/physics/src/shapes.ts:97`

```ts
function sampleShape(s: Shape, n = 4): ShapeSamples
```

n cells along each axis of the bounds, the ones whose centre is inside; each carries an equal share of the volume.

### scale

*function* · `packages/physics/src/vec.ts:9`

```ts
function scale: (a: Vec3, s: number) => Vec3
```

### scopedSurface

*function* · `packages/physics/src/host.ts:443`

```ts
function scopedSurface(host: PhysicsHost, b: Body): { friction: number; kineticFriction: number; restitution: number }
```

A body's friction and restitution where it is now: its material's, times the friction capability's `scale` and the impact capability's
`scale` at its scope when they are on. What a kernel without contact hooks is given through `setSurface`.

### seededRng

*function* · `packages/physics/src/vec.ts:48`

```ts
function seededRng(seed: number): () => number
```

A seeded generator (mulberry32): the same seed gives the same sequence on every machine.

### shapeVolume

*function* · `packages/physics/src/shapes.ts:77`

```ts
function shapeVolume(s: Shape): number
```

Exact volume where the shape has a formula; otherwise from a fine lattice.

### splitShape

*function* · `packages/physics/src/shapes.ts:126`

```ts
function splitShape(s: Shape, n: number): Piece[]
```

Cut a shape into about n box pieces (body space): the bounds cut along their longest edges until there are n cells, the cells whose centre
lies outside the shape dropped and their volume shared among the rest, so the pieces hold the whole volume. A sphere is cut the same way.

### sub

*function* · `packages/physics/src/vec.ts:8`

```ts
function sub: (a: Vec3, b: Vec3) => Vec3
```

### syncSurfaces

*function* · `packages/physics/src/host.ts:451`

```ts
function syncSurfaces(host: PhysicsHost, seen: Map<Body, string>)
```

Give every body whose scoped surface changed its new surface (kernels without contact hooks); `seen` keeps what each one has.

### v3

*function* · `packages/physics/src/vec.ts:6`

```ts
function v3: (x?: number, y?: number, z?: number) => Vec3
```

### validateMaterials

*function* · `packages/physics/src/materials.ts:33`

```ts
function validateMaterials(table: unknown, base = 'materials'): Issue[]
```

Check a materials table: numbers only, inside sane ranges. Returns the problems (empty when fine).

### validatePhysicsConfig

*function* · `packages/physics/src/host.ts:159`

```ts
function validatePhysicsConfig(c: unknown, known: string[] = []): Issue[]
```

### volumeBelow

*function* · `packages/physics/src/shapes.ts:156`

```ts
function volumeBelow(s: Shape, p: Vec3, q: [number, number, number, number], y: number): number
```

Volume of the shape below a horizontal surface at height y, the body at p turned by q: by its sample cells (exact for a sphere).

## Interfaces

### Body

*interface* · `packages/physics/src/kernel.ts:39`

```ts
interface Body
```

- `readonly id: number`
- `readonly kind: BodyKind`
- `readonly shape: Shape`
- `mass: number`
- `invMass: number`
- `invInertia: number`
- `inertia?: Vec3` — principal moments of inertia, kg·m², where the kernel knows them (a box, a hull); a sphere's three are equal
- `position: Vec3`
- `orientation: Quat`
- `velocity: Vec3`
- `angularVelocity: Vec3`
- `force: Vec3`
- `torque: Vec3`
- `material?: string`
- `linearDamping: number`
- `angularDamping: number`
- `gravityScale: number`
- `sleeping: boolean`
- `userData?: unknown`
- `removed: boolean`
- `exempt?: string[]` — capabilities that leave this body alone (a vessel computes its own weight and buoyancy: ['gravity', 'media'])

### BodyDef

*interface* · `packages/physics/src/kernel.ts:20`

```ts
interface BodyDef
```

- `shape: Shape`
- `kind?: BodyKind`
- `mass?: number`
- `position?: Vec3`
- `orientation?: Quat`
- `velocity?: Vec3`
- `angularVelocity?: Vec3`
- `material?: string`
- `linearDamping?: number`
- `angularDamping?: number`
- `gravityScale?: number`
- `lockRotation?: boolean` — No spin: the sphere slides like a block (a crate, a puck) instead of rolling.
- `noCollide?: boolean` — Touches nothing: other bodies pass through it (a pivot pin that only anchors joints). Joints still hold it. A rigid-body kernel only.
- `userData?: unknown`

### Capability

*interface* · `packages/physics/src/host.ts:13`

```ts
interface Capability
```

- `readonly name: string`
- `readonly phase: Phase`
- `readonly order?: number` — Position among capabilities of the same phase (lower first); ties go by name, never by registration.
- `readonly cost?: string` — What it costs, in words, for the inspector and the budget notes.
- `readonly defaults?: Record<string, unknown>` — Values at every scope unless overridden.
- `readonly needsBodies?: boolean` — Needs rigid bodies from the kernel; it refuses to switch on when the kernel has none.
- `readonly requires?: string[]` — Names of capabilities that must be on first.
- `load?(host: PhysicsHost, value: unknown[], scope: 'world' | 'level'): void` — A list in the level's physics block under this capability's name (actuators): handed over when the block is applied.
- `loadBindings?(host: PhysicsHost, value: unknown[]): void` — The level's `physics.bindings` list (actuators): what drives its commands from input, interactables, zones and events. Dropped with the level.
- `unloadBindings?(): void`
- `enable?(host: PhysicsHost): void`
- `disable?(host: PhysicsHost): void`
- `step?(dt: number, host: PhysicsHost, contacts: readonly ContactEvent[]): void` — Phase 'field': add forces for this step. Phases 'strength' and 'stretch': act on what the step did.

### ColliderParams

*interface* · `packages/physics/src/physics.ts:6`

```ts
interface ColliderParams
```

- `shape?: ShapeKind`
- `size?: [number, number, number]` — box: full size (x, y, z)
- `radius?: number` — sphere / capsule radius
- `height?: number` — capsule total height (feet to head)
- `offset?: [number, number, number]` — centre offset from the entity's origin
- `layer?: string`
- `mask?: string[]` — layers this collider overlaps with (triggers) or collides with
- `trigger?: boolean` — a trigger fires enter/exit events instead of blocking

### CombineRules

*interface* · `packages/physics/src/materials.ts:20`

```ts
interface CombineRules
```

- `friction: CombineRule`
- `restitution: CombineRule`
- `rollingFriction: CombineRule`
- `[property: string]: CombineRule`

### Constraint

*interface* · `packages/physics/src/kernel.ts:160`

```ts
interface Constraint
```

- `readonly id: number`
- `type: ConstraintType`
- `readonly a: Body`
- `readonly b: Body`
- `length: number`
- `stiffness: number`
- `damping: number`
- `maxForce: number`
- `userData?: unknown`
- `force: number` — force over the last step, N: positive in tension (it pulls the two bodies together), negative in compression
- `stretch: number` — present distance less the length, m
- `broken: boolean`
- `anchor?: Vec3`
- `axis?: Vec3`

### ConstraintDef

*interface* · `packages/physics/src/kernel.ts:140`

```ts
interface ConstraintDef
```

- `type: ConstraintType`
- `a: Body`
- `b: Body`
- `length?: number` — the distance (rod), the longest distance (rope) or the rest length (spring); the present distance when missing
- `stiffness?: number` — N/m (spring)
- `damping?: number` — N·s/m (spring)
- `maxForce?: number` — the constraint breaks when the force on it (N) passes this; never when missing
- `anchor?: Vec3` — ball, hinge, fixed: the shared point in world space (the middle between the bodies when missing)
- `axis?: Vec3` — hinge: the axis in world space (up when missing)
- `limits?: [number, number]` — hinge: the angle from the pose it was made in, radians, from the lower to the upper limit; free when missing
- `userData?: unknown`

### Contact

*interface* · `packages/physics/src/kernel.ts:66`

```ts
interface Contact
```

One contact between two bodies during a step. `normal` points from b toward a.

- `a: Body`
- `b: Body`
- `point: Vec3`
- `normal: Vec3`
- `depth: number`
- `friction: number` — Mutable before the solve: a hook may change what this contact uses.
- `kineticFriction: number`
- `rollingFriction: number`
- `pivotFriction: number`
- `restitution: number`
- `restitutionThreshold: number` — Speed below which the bounce is a rest.
- `readonly closing: number` — Closing speed along the normal when the contact is made (m/s, positive = approaching); read-only for hooks.
- `anisotropy?: { axis: Vec3; across: number }` — Friction that differs along a direction (skates, a rail): the coefficient across `axis` is `across` times the one along it.
- `sticky?: boolean` — A capability's request that the two bodies leave this contact as one (a perfectly inelastic hit).

### ContactEvent

*interface* · `packages/physics/src/kernel.ts:89`

```ts
interface ContactEvent
```

What happened at one contact over a step, delivered after it.

- `a: Body`
- `b: Body`
- `point: Vec3`
- `normal: Vec3`
- `impulse: number` — Total normal impulse over the step (N·s).
- `speed: number` — Largest closing speed along the normal during the step (m/s).
- `slide: number` — Sliding speed along the surface at the largest impact (m/s).
- `restitution: number` — The restitution the contact was solved with, and whether a capability asked the bodies to stick.
- `sticky: boolean`

### DegradeRule

*interface* · `packages/physics/src/budget.ts:32`

```ts
interface DegradeRule
```

One step down: values to set at world scope (`capability`.`key` = `value`; a number may be a share of the tier's own: `of`) and capabilities to switch off.

- `name: string`
- `set?: { capability: string; key: string; value: number }[]`
- `disable?: string[]`

### FieldForce

*interface* · `packages/physics/src/kernel.ts:108`

```ts
interface FieldForce
```

What a force field may return: a force, a torque, or both.

- `force?: Vec3`
- `torque?: Vec3`

### GovernorOptions

*interface* · `packages/physics/src/budget.ts:40`

```ts
interface GovernorOptions
```

- `tier?: Tier`
- `stepMs?: number` — override the tier's step budget, ms
- `rules?: DegradeRule[]`
- `window?: number` — steps averaged before a decision (default 30)
- `down?: number` — windows over budget in a row before stepping down (default 2) and windows well under it before stepping back up (default 4)
- `up?: number`

### HavokKernelOptions

*interface* · `packages/physics/src/havok-kernel.ts:6`

```ts
interface HavokKernelOptions
```

- `materials?: MaterialLibrary`
- `gravity?: Vec3`
- `substeps?: number` — Havok steps per world step; force fields are evaluated before each (1 = once a step)

### Issue

*interface* · `packages/physics/src/materials.ts:30`

```ts
interface Issue
```

- `path: string`
- `message: string`

### KernelCaps

*interface* · `packages/physics/src/kernel.ts:110`

```ts
interface KernelCaps
```

- `friction: boolean` — Static and kinetic friction, restitution with a threshold and combine rules from materials.
- `restitutionCombine: boolean`
- `rollingFriction: boolean`
- `contactImpulses: boolean`
- `constraints: boolean`
- `breakableConstraints: boolean`
- `ccd: 'none' | 'substep' | 'native'` — 'substep' = small steps scaled by speed; 'native' = swept collision.
- `shapes: string[]`
- `contactHooks?: boolean` — `beforeSolve` can change a contact before it is solved; false: hooks see nothing and per-pair changes go through materials
- `rigidBodies?: boolean` — bodies of any shape move, rotate with a full inertia tensor and stack
- `joints?: boolean` — ball, hinge and fixed constraints

### KernelRayHit

*interface* · `packages/physics/src/kernel.ts:105`

```ts
interface KernelRayHit
```

- `body: Body`
- `point: Vec3`
- `normal: Vec3`
- `distance: number`

### LevelConstraint

*interface* · `packages/physics/src/host.ts:154`

```ts
interface LevelConstraint
```

- `id?: string`
- `type: 'rod' | 'rope' | 'spring' | 'ball' | 'hinge' | 'fixed'`
- `a: string`
- `b: string`
- `length?: number`
- `stiffness?: number`
- `damping?: number`
- `maxForce?: number`
- `anchor?: Vec3`
- `axis?: Vec3`
- `limits?: [number, number]`

### LevelFracture

*interface* · `packages/physics/src/host.ts:155`

```ts
interface LevelFracture
```

- `node: string`
- `pieces: string[]`
- `material?: string`
- `toughness?: number`

### LightKernelOptions

*interface* · `packages/physics/src/light-kernel.ts:5`

```ts
interface LightKernelOptions
```

- `materials?: MaterialLibrary`
- `gravity?: Vec3`
- `substeps?: number` — Smallest number of sub-steps per step; fast bodies get more (up to maxSubsteps) so they do not tunnel.
- `maxSubsteps?: number`
- `iterations?: number`
- `restitutionThreshold?: number` — Closing speed below which a bounce is a rest (m/s).

### MediumSample

*interface* · `packages/physics/src/host.ts:136`

```ts
interface MediumSample
```

- `name: string`
- `density: number`
- `viscosity: number`
- `flow: Vec3`

### PhysicsConfig

*interface* · `packages/physics/src/host.ts:142`

```ts
interface PhysicsConfig
```

What a `physics` block of a level or a game may hold.

- `capabilities?: string[]`
- `materials?: Record<string, PhysicsMaterial>`
- `pairs?: Array<{ a: string; b: string; values: PhysicsMaterial }>`
- `combine?: Record<string, CombineRule>`
- `gravity?: { vector?: Vec3 }`
- `constraints?: LevelConstraint[]` — links between two bodies of the level, by the names of their nodes (a Physics Constraint of Unreal where the kernel has an equivalent)
- `fractured?: LevelFracture[]` — bodies authored in pieces: when the intact one breaks, its pieces (nodes of the level) take over
- `[capability: string]: unknown`

### PhysicsFields

*interface* · `packages/physics/src/host.ts:129`

```ts
interface PhysicsFields
```

Read access to what capabilities provide: gravity at a point (m/s², a vector) and, later, the medium.

- `gravity?: (p: Vec3, ctx?: ScopeContext) => Vec3`
- `medium?: (p: Vec3, ctx?: ScopeContext) => MediumSample` — The medium at a point (air by default): density kg/m³, dynamic viscosity Pa·s, the flow velocity m/s (wind, current).
- `column?: (p: Vec3, height: number, ctx?: ScopeContext) => { fraction: number; medium: MediumSample | null }` — The part of a vertical segment of `height` standing at `p` that is inside a dense medium (water, mud), and that medium.

### PhysicsHostOptions

*interface* · `packages/physics/src/host.ts:139`

```ts
interface PhysicsHostOptions
```

- `kernel?: PhysicsKernel`
- `materials?: MaterialLibrary`
- `capabilities?: Capability[]`

### PhysicsKernel

*interface* · `packages/physics/src/kernel.ts:179`

```ts
interface PhysicsKernel
```

- `readonly name: string`
- `readonly caps: KernelCaps`
- `gravity: Vec3`
- `readonly bodies: readonly Body[]`
- `readonly constraints: readonly Constraint[]`
- `step(dt: number): void`
- `addBody(def: BodyDef): Body`
- `removeBody(b: Body): void`
- `addConstraint(def: ConstraintDef): Constraint`
- `removeConstraint(c: Constraint): void`
- `onConstraintBreak(cb: (c: Constraint) => void): () => void` — Called after the step for each constraint whose force passed its limit; the constraint is already removed.
- `applyForce(b: Body, f: Vec3, at?: Vec3): void`
- `applyImpulse(b: Body, j: Vec3, at?: Vec3): void`
- `applyTorque(b: Body, t: Vec3): void`
- `setVelocity(b: Body, v: Vec3): void`
- `setPosition(b: Body, p: Vec3): void`
- `sleep(b: Body): void`
- `wake(b: Body): void`
- `addForceField(fn: (b: Body, h: number) => Vec3 | FieldForce | null): () => void` — A force that depends on where a body is (gravity, drag, buoyancy): evaluated at the position of every sub-step, so a fast or orbiting body gets the force it feels where it is, not where it was at the start of the step. Returns newtons, or null for none.
- `beforeSolve(cb: (c: Contact) => void): () => void` — Called for every contact before it is solved, so a capability can change friction or restitution.
- `onContact(cb: (e: ContactEvent) => void): () => void` — Called after the step with what each contact did.
- `rayCast(origin: Vec3, dir: Vec3, maxDistance: number): KernelRayHit | null`
- `setMassProperties?(b: Body, mass: number, centreOfMass: Vec3, inertia: Vec3): void` — Mass, centre of mass (body space) and principal inertia set from outside (a vessel whose tanks fill); where the kernel can.
- `setSurface?(b: Body, s: { friction: number; kineticFriction: number; restitution: number }): void` — A body's own friction and restitution, over its material's: how a kernel without contact hooks takes a capability's dials.

### PhysicsMaterial

*interface* · `packages/physics/src/materials.ts:5`

```ts
interface PhysicsMaterial
```

Physical materials as data. A value that is not given means "this capability does not apply to it", not a default: a body with
no strength cannot break. The few values a contact cannot do without (friction, restitution) have library defaults, stated here.

- `density?: number`
- `friction?: number`
- `kineticFriction?: number`
- `rollingFriction?: number`
- `pivotFriction?: number`
- `restitution?: number`
- `youngModulus?: number`
- `yieldStrength?: number`
- `tensileStrength?: number`
- `toughness?: number`
- `[property: string]: number | undefined`

### PhysicsOptions

*interface* · `packages/physics/src/physics.ts:51`

```ts
interface PhysicsOptions
```

- `layers?: string[]`

### Piece

*interface* · `packages/physics/src/shapes.ts:121`

```ts
interface Piece
```

- `shape: Shape`
- `position: Vec3`
- `volume: number`

### RayHit

*interface* · `packages/physics/src/physics.ts:24`

```ts
interface RayHit
```

- `point: BABYLON.Vector3`
- `normal: BABYLON.Vector3`
- `distance: number`
- `mesh: BABYLON.AbstractMesh`
- `entity?: Entity`

### RigidBodyParams

*interface* · `packages/physics/src/host.ts:81`

```ts
interface RigidBodyParams
```

- `shape?: 'sphere' | 'box' | 'plane' | 'capsule' | 'cylinder'`
- `radius?: number`
- `height?: number` — capsule and cylinder: the length along y (a capsule's between the centres of its caps)
- `geometry?: Shape` — any shape the kernel has (a hull, a compound, a mesh): wins over `shape`
- `size?: [number, number, number]` — box: full size, like Collider
- `normal?: Vec3`
- `offset?: number`
- `mass?: number` — kg; taken from the material density and the shape when missing
- `material?: string`
- `kind?: BodyKind`
- `linearDamping?: number`
- `angularDamping?: number`
- `gravityScale?: number`
- `velocity?: Vec3`
- `angularVelocity?: Vec3`
- `lockRotation?: boolean`
- `rotate?: boolean` — Turn the node with the body (a rolling ball); off for a body whose node is a model that should stay upright.

### ScopeContext

*interface* · `packages/physics/src/host.ts:38`

```ts
interface ScopeContext
```

Where a value is asked from: an entity, or just a place (a zone covers it).

- `entity?: Entity | string`
- `position?: Vec3`

### ShapeSamples

*interface* · `packages/physics/src/shapes.ts:67`

```ts
interface ShapeSamples
```

- `points: Vec3[]` — cell centres inside the shape, body space
- `cell: Vec3` — the cell's edges along x, y, z, m
- `volume: number` — the volume each point stands for, m³ (the shape's volume over the count, so the sum is exact where the volume is known)

### TierBudget

*interface* · `packages/physics/src/budget.ts:12`

```ts
interface TierBudget
```

- `stepMs: number` — the share of a frame the whole physics step may take, milliseconds
- `maxPieces: number` — pieces alive at once (the strength capability's `maxPieces`)
- `fluidParticles: number` — particles of all liquids, and of all cloths: for the game to give the capabilities that take such a budget
- `clothParticles: number`
- `flowSources: number` — flow sources the level's field takes (`FlowField.maxSources`) and the cells of flow boxes solved per physics step, all boxes together
- `flowBoxCells: number`

### TriggerEvent

*interface* · `packages/physics/src/physics.ts:23`

```ts
interface TriggerEvent
```

- `trigger: Entity`
- `other: Entity`

## Types

### BodyKind

*type* · `packages/physics/src/kernel.ts:18`

```ts
type BodyKind = 'static' | 'dynamic' | 'kinematic'
```

### CombineRule

*type* · `packages/physics/src/materials.ts:19`

```ts
type CombineRule = 'mean' | 'geometric' | 'min' | 'max' | 'multiply'
```

### ConstraintType

*type* · `packages/physics/src/kernel.ts:139`

```ts
type ConstraintType = 'rod' | 'rope' | 'spring' | 'ball' | 'hinge' | 'fixed'
```

A link between two bodies' centres.
 - rod:    a rigid distance (pushes and pulls)
 - rope:   a maximum distance (pulls only; slack when closer)
 - spring: Hooke's law with damping (implicit, so stable at any stiffness)
 - ball:   the two bodies share a point and turn freely about it (a shoulder, a hook)
 - hinge:  they share a point and turn about one axis only, within optional limits (a door, a wheel)
 - fixed:  they are welded in the pose they have (a bracket that can break)
The last three need a kernel with `caps.joints` (rigid bodies).

### Phase

*type* · `packages/physics/src/host.ts:10`

```ts
type Phase = 'field' | 'strength' | 'stretch'
```

The fixed order of the step: fields add forces, the kernel integrates and resolves contacts, strength reads them, stretch integrates soft things.

### Quat

*type* · `packages/physics/src/vec.ts:4`

```ts
type Quat = [number, number, number, number]
```

### Scope

*type* · `packages/physics/src/host.ts:39`

```ts
type Scope = 'world' | 'level' | 'zone' | 'entity'
```

### Shape

*type* · `packages/physics/src/kernel.ts:7`

```ts
type Shape = | { type: 'sphere'; radius: number } | { type: 'box'; half: Vec3 } // static or kinematic only in the light kernel | { type: 'plane'; normal: Vec3; offset: number } // static only: dot(normal, p) = offset // the shapes below need a rigid-body kernel (Havok); the light kernel refuses them | { type: 'capsule'; radius: number; halfHeight: number } // along the body's y, halfHeight to the centre of each cap | { type: 'cylinder'; radius: number; halfHeight: number } // along the body's y | { type: 'convex'; points: Vec3[] } // the hull of the points, in body space | { type: 'mesh'; positions: number[]; indices: number[] } // triangles in body space; static or kinematic only | { type: 'compound'; children: { shape: Shape; position?: Vec3; orientation?: Quat }[] }
```

The part of the dynamics every capability is written against. Havok, the light kernel of this package, or a game's own backend
implements it; a capability asks `caps` what is native and does the rest in a force or contact hook.

### ShapeKind

*type* · `packages/physics/src/physics.ts:4`

```ts
type ShapeKind = 'box' | 'sphere' | 'capsule'
```

### Tier

*type* · `packages/physics/src/budget.ts:11`

```ts
type Tier = 'desktop' | 'mobile' | 'console' | 'low'
```

What a hardware tier can afford, and what to give up when the world step runs over its share of the frame.
The numbers in `TIERS` come from `tools/bench` (2026-11-02), run on the dev machine in Node, Chromium and WebKit: a full step share of 6 ms holds
about 200 resting pieces, or about 170 to 290 liquid particles, or about 230 cloth particles when each takes 40 / 30 / 30 % of it. The console,
mobile and low tiers are scaled down from the same table; they are not measured on those machines: run `tools/bench` there and replace them.
`flowBoxCells` comes from the flow box's measured 0.9 microseconds per cell per step (A13) at 30 % of the step share: 1.8 ms / 0.9 us = 2000 cells
stepped every physics step on the desktop (a box stepped every n-th step may have n times as many).

### Vec3

*type* · `packages/physics/src/vec.ts:3`

```ts
type Vec3 = [number, number, number]
```

Plain-number vectors for the physics code: no allocation-heavy classes, no engine dependency, and the same arithmetic everywhere
so a replay gives the same bits. A vector is [x, y, z]; a quaternion is [x, y, z, w].

## Constants

### DEFAULT_COMBINE

*const* · `packages/physics/src/materials.ts:22`

```ts
const DEFAULT_COMBINE: CombineRules
```

### DEFAULT_CONTACT

*const* · `packages/physics/src/materials.ts:23`

```ts
const DEFAULT_CONTACT: { friction: number; restitution: number; rollingFriction: number; pivotFriction: number; }
```

### DEFAULT_LAYERS

*const* · `packages/physics/src/layers.ts:35`

```ts
const DEFAULT_LAYERS: string[]
```

The layers a game gets unless it defines its own (the common UE object channels).

### EXAMPLE_MATERIALS

*const* · `packages/physics/src/materials.ts:106`

```ts
const EXAMPLE_MATERIALS: Record<string, PhysicsMaterial>
```

A few common materials, as examples and for tests. A game defines its own.

### TIERS

*const* · `packages/physics/src/budget.ts:24`

```ts
const TIERS: Record<Tier, TierBudget>
```

