FERRETGame engine over Babylon.js
Ferret

Physics overview

Out of the box a Ferret game has the collision system of Babylon: a capsule against meshes with checkCollisions, layers, triggers and raycasts (PhysicsService). Realistic physics is a separate set of capabilities. A game, a level, a zone or one entity uses them or does not; nothing realistic runs unless asked, and a game that needs a cannonball does not carry cloth.

Units are SI: metres, kilograms, seconds, newtons, pascals. The world's fixed step drives everything, so a run repeats with the same inputs.

The host and the kernel#

PhysicsHost is the home of the capabilities: it owns the kernel, the material library, the registry and the fixed order of the step. A kernel is a rigid-body engine behind PhysicsKernel:

KernelWhat it is
LightKernelsmall, deterministic, no dependencies: spheres, boxes and capsules against planes and each other; joints; enough for most games
HavokKernelHavok through its own API (no Babylon scene needed, so it runs headless): full shapes, ball / hinge / fixed joints, noCollide; the WASM comes from physics-havok
import { PhysicsHost } from '@ferret/physics';
import { gravity } from '@ferret/physics-gravity';
import { friction } from '@ferret/physics-friction';
import { impact } from '@ferret/physics-impact';

const host = world.addService(new PhysicsHost({ capabilities: [gravity(), friction(), impact()] }));
host.enable('gravity');
host.enable('friction', { scale: 1.2 });

const e = world.spawn('ball', { position: new BABYLON.Vector3(0, 3, 0) });
e.add('RigidBody', { shape: 'sphere', radius: 0.25, mass: 1 });

A RigidBody component puts a node under the kernel: the node follows the body after every step. Registering a capability makes it available; enable(name, values) turns it on, disable(name) off. A capability that needs bodies refuses to switch on when the kernel has none, and requires names the capabilities that must be on first.

Capabilities#

A Capability has a name, a phase, an optional order inside the phase, defaults, a declared cost, and hooks (enable, disable, step). Capabilities are independent (each works alone) and composable (they meet in the shared material data and in the order of the step, never in each other's code). A game may write its own against the same interface.

Phases#

The step has a fixed order (Phase):

  1. field: capabilities add forces (gravity, drag, buoyancy, explosions, springs...).
  2. The kernel integrates and resolves contacts.
  3. strength: capabilities act on what the contacts did (damage, breakage).
  4. stretch: capabilities fix up constraints and soft bodies.

Inside a phase, capabilities run by order, ties by name, never by registration order, so the result does not depend on how a game assembled them.

Scoped values#

Every value of a capability has a default and can be overridden at four scopes, narrowest wins: entity, zone, level, world, then the default (ScopedConfig).

host.config('gravity').set('world', 'scale', 1);
host.config('gravity').set('zone', 'scale', 0.16, 'moon-pit');   // low gravity inside one zone
host.config('friction').set('entity', 'scale', 0.05, 'ice-block');

A level manifest does the same declaratively in its physics block (PhysicsConfig):

"physics": {
  "capabilities": ["gravity", "media", "friction", "impact"],
  "materials": { "ice": { "friction": 0.03, "restitution": 0.1 } },
  "gravity": { "vector": [0, -9.81, 0] },
  "constraints": [{ "type": "rope", "a": "Beam", "b": "Weight", "length": 3 }]
}

Values from the manifest apply while the level is loaded and are cleared when it goes.

Materials#

A PhysicsMaterial is data: density, friction, restitution, strengths, heat capacity, and so on. A value that is not given means "this capability does not apply to it", not a default: a material without a strength is unbreakable. Two materials meet under a combine rule (CombineRule: mean, geometric, min, max, multiply), or under an explicit pair rule. validateMaterials checks a table for numbers in sane ranges.

Budgets and tiers#

What a machine can afford is data too: TIERS gives each Tier (desktop, console, mobile, low) a share of the frame for the whole physics step (stepMs), a cap on live pieces, and particle budgets for liquids and cloth. The numbers for desktop come from tools/bench; the other tiers are scaled from them.

The Governor watches the measured step time. When it stays over budget it gives up detail in the order of its rules (fewer pieces, then no particle liquids, ...), and takes it back when there is room. Everything the host does is visible in physics.info (Commands).