FERRETGame engine over Babylon.js
Ferret

Materials

A material is a table of numbers about a substance. Capabilities read the numbers they need; the kernel reads friction and restitution. Materials are how realism is authored: the same code makes ice slippery and rubber bounce because the data says so.

What it is for#

Every body can name a material (material: 'ice'). The host's MaterialLibrary holds the table. Use materials, not per-body hacks, for anything that is a property of stuff: density (so mass follows from shape), friction, bounce, stiffness and strength, heat capacity.

The key rule: a missing value means "does not apply"#

A material without a tensileStrength is not "weak"; it is unbreakable, because the strength capability has nothing to compare against. A material without a heat capacity does not heat. The few numbers a contact cannot do without are the exception and have library defaults:

PropertyUnitMeaningWhen missing
densitykg/m³mass per volumemass must be given on the body
friction–static Coulomb coefficient0.5
kineticFriction–sliding coefficient0.8 × friction
rollingFriction–rolling resistance: torque = coefficient × normal force × radius0
pivotFriction–resistance to spinning about the contact normal0
restitution–0 sticks, 1 perfectly elastic0
youngModulusPastiffnessstiffness-based behaviour does not apply
yieldStrengthPawhere it starts to deform for gooddoes not yield
tensileStrengthPawhere it tearsdoes not tear
toughness–energy it takes to crackdoes not crack

Other capabilities add their own keys (heat capacity, conductivity, melting point, resistivity, flammability...). Their guides list them. Any numeric key is allowed; validateMaterials checks the known ones against sane ranges.

import { MaterialLibrary, EXAMPLE_MATERIALS, validateMaterials } from '@ferret/physics';

const lib = new MaterialLibrary().load(EXAMPLE_MATERIALS);
lib.add('glass', { density: 2500, friction: 0.4, restitution: 0.6, tensileStrength: 3e7 });
const bad = validateMaterials({ plastic: { friction: 99, density: -5 } });

load throws with every problem listed when the table is invalid, so a bad level never half-loads. EXAMPLE_MATERIALS (iron, wood, stone, ice, rubber) are for examples and tests; a game defines its own.

Two materials meet#

A contact has two sides. What it uses for each property comes from, in this order:

  1. a pair table entry for exactly these two materials (setPair), else
  2. the combine rule of that property, applied to both sides' values, else
  3. the one value that exists, if only one side has it.
RuleResultDefault for
geometric√(a·b)friction
maxthe largerrestitution
mean(a+b)/2rolling friction and anything else
minthe smaller
multiplya·b
import { MaterialLibrary, EXAMPLE_MATERIALS } from '@ferret/physics';

const lib = new MaterialLibrary().load(EXAMPLE_MATERIALS);
const iceOnRubber = lib.contact('ice', 'rubber');
lib.setPair('ice', 'rubber', { friction: 0.9 });                // studded tyres on ice: not the geometric mean
const studded = lib.contact('ice', 'rubber');

Pair tables work on the light kernel. Havok does not let a game change a contact before it is solved, so it takes friction and restitution from the combine rules only; to get a different result there, change the materials themselves.

Recipes#

  • A level's own materials: put them in the physics.materials block of the level manifest (level block); they load with the level.
  • Ice, mud, rubber floors: give the floor entity a material; tune friction. Or dial it per scope with the friction capability's scale without touching the material.
  • A bounce that fades with speed: restitution is applied above restitutionThreshold; below it the contact is a rest. Raise the threshold for less jitter.
  • Mass from density: leave mass off a body with a material that has density; the kernel uses density × the shape's volume.

Debugging#

SymptomCause
invalid materials: … is outside …a number out of range: units (friction is a coefficient, not a percentage)
A thing never breaksits material has no tensileStrength (or the capability's own key): unbreakable by definition
Friction looks like the geometric mean of the two when you set a pairyou are on Havok, which does not read pair tables
physics.materials commandprints the table the host sees right now

Reference#

MaterialLibrary, PhysicsMaterial, CombineRule, validateMaterials, EXAMPLE_MATERIALS, DEFAULT_CONTACT.