Lucky Robots Blog Open Roles

2.5 · Physics (Jolt)

Three-dimensional rigid-body physics, exposed through a thin PhysicsAPI abstraction so the engine can swap backends without rewriting every call site.

Module: Hazel/src/Hazel/Physics/ PhysicsAPI.h PhysicsScene.h PhysicsShapes.h SceneQueries.h Backend: Jolt
Backend-agnostic interface Jolt implementation PhysicsAPI PhysicsAPI.h PhysicsScene one per Scene PhysicsBody wraps body id PhysicsShape PhysicsShapes.h JoltAPI static facade init JoltScene JPH::PhysicsSystem JoltBody JPH::BodyID key JoltShapes Box/Sphere/…/Compound PhysicsSystem static facade PhysicsLayerManager collision filtering SceneQueries ray / cast / overlap
Layered design: a stable, backend-neutral interface on the left, the active Jolt implementation on the right, plus the cross-cutting facade and query helpers in the middle.

Overview

Jolt is the only registered 3D backend today, but every call site interacts with it through the PhysicsAPI family of interfaces. That gives the engine room to add a second backend without rewriting gameplay, recording, or editor code.

Key types

TypeHeaderRole
PhysicsAPIPhysicsAPI.hBackend interface. Currently only JoltAPI.
PhysicsScene / JoltScenePhysicsScene.h / JoltScene.hThe simulation world. One instance per Hazel Scene.
PhysicsBody / JoltBodyPhysicsBody.h / JoltBody.hRigid body. The Jolt impl wraps a JPH::BodyID.
PhysicsShapePhysicsShapes.hShape hierarchy — see below.
PhysicsSystemPhysicsSystem.hStatic facade for init / shutdown / mesh cooking / scene factory.
PhysicsLayer, PhysicsLayerManagerPhysicsLayer.hCollision filtering — which layers collide with which.
SceneQueriesSceneQueries.hRayCastInfo, BoxCastInfo, SphereCastInfo, CapsuleCastInfo, overlap variants, SceneQueryHit.

Shape hierarchy

PhysicsShape PhysicsShapes.h BoxShape SphereShape CapsuleShape ConvexMeshShape TriangleMeshShape static only CompoundShape Immutable CompoundShape Mutable
Shape taxonomy. Primitives are cheap; convex meshes need cooking; triangle meshes are static-only; compounds group children, with an immutable variant for cooking once and a mutable variant for runtime edits.

Phased stepping

JoltScene doesn't own its tick — it registers callbacks with the Scene's TimeManager, one per phase. That keeps the order of operations identical to MuJoCo and observable from outside the physics module.

PhaseJolt callbackWhat it does
AcquisitionUpdateAcquisitionRead entity TransformComponent values into Jolt bodies.
ControlUpdateControlApply forces / torques set by scripts during the previous tick.
PhysicsUpdatePhysicsCalls JPH::PhysicsSystem::Update(dt) — the actual integration step.
ValidationUpdateValidationDrain contact events from the contact listener, fire script callbacks.
ExportUpdateExportWrite the integrated body transforms back to entity TransformComponents.

Default settings

Tunables live in PhysicsSettings (serialized as part of SceneSettings):

Gravity

Default {0, -9.81, 0} (Y-up, m/s²). Override per scene through SetGravity.

Max bodies

Default 5700. Sets the size of the Jolt body pool; raising it costs memory up front.

Solver iterations

Position and velocity iteration counts — the usual stability-vs-cost knob.

Layers & queries

PhysicsLayerManager owns the layer table; bodies declare which layer they belong to and which layers they collide with. Scene queries (SceneQueries.h) accept the same layer mask, so picking, casting, and simulation share one filter source.

QueryUse
RayCastInfoPick a single body along a ray (mouse-pick, line-of-sight, lidar).
BoxCastInfo / SphereCastInfo / CapsuleCastInfoSweep a shape — character motion, projectile prediction.
Overlap variantsInstantaneous overlap test — trigger volumes, AOE queries.
SceneQueryHitHit record returned by all of the above.

Pitfalls

Triangle meshes are static

TriangleMeshShape cannot be attached to a dynamic body. For moving concave geometry, use ConvexMeshShape (cook once) or a CompoundShape of primitives.

Mutate transforms in Acquisition, read them in Export

Setting TransformComponent after the Physics phase is a one-frame teleport that the simulator overwrites. Use Acquisition for writes from scripts, Export for reads.

Layer rule

Physics may depend on Scene (read), Core, and Math. It must not depend on Renderer, Editor, or ScriptEngine. Scripts call into physics through ScriptGlue, never the other way around.

Extending

  • New shape type: add to ShapeType enum, derive PhysicsShape, and implement the Jolt-specific path in JoltShapes.h.
  • New backend: implement PhysicsAPI, PhysicsScene, and PhysicsBody for the target library. Register it through PhysicsSystem.
  • New query: extend SceneQueries.h, then implement PhysicsScene::CastShape / OverlapShape in the active backend.

For the 2D counterpart, see Physics (Box2D). For robotics-grade simulation, see Physics (MuJoCo).