> Cinder 0.1.0 documentation: cinder.physics API
> Canonical page: https://cinder.ash10.dev/docs/api/physics/ · Source: `docs/api/physics.md` in the engine repository (engine commit 47c9352, imported 2026-11-09)
> Index of every page for agents: https://cinder.ash10.dev/llms.txt

# `cinder.physics`


## `cinder.physics`

Source: `cinder/physics/__init__.mojo`

Physics over Jolt: rigid-body world, queries, character controller and ECS synchronisation.


## `cinder.physics.character`

Source: `cinder/physics/character.mojo`

Character controller over Jolt's CharacterVirtual: slopes, steps and moving platforms. The character is a capsule whose position is at its feet. `update` applies the engine's standard velocity rules (stick to the ground and inherit its velocity, otherwise fall under gravity), then runs Jolt's extended update which walks stairs up to `step_height` and sticks to the floor when walking down. Contacts with dynamic bodies push them with the character's mass.

- `comptime PI_F32: Float32 = 3.14159265358979` — Pi as `Float32`, for converting `max_slope_degrees` to radians.
### struct `GroundState`

```mojo
struct GroundState(Equatable, ImplicitlyCopyable, Movable, Writable)
```

Ground contact state of a `CharacterController`, mirrored from Jolt's ground state.

Fields:

- `value`: UInt8

- **`on_ground`** `on_ground() -> GroundState`  
  Standing on a surface no steeper than `max_slope_degrees`.
- **`on_steep_ground`** `on_steep_ground() -> GroundState`  
  Touching a surface steeper than `max_slope_degrees`; `update` treats it like the air and applies gravity.
- **`not_supported`** `not_supported() -> GroundState`  
  Touching something that cannot hold the character (its edge, a falling body).
- **`in_air`** `in_air() -> GroundState`  
  Touching nothing; `update` applies gravity.
- **`write_to`** `write_to(self, mut w: Some[Writer])`

### struct `CharacterDesc`

```mojo
struct CharacterDesc(Copyable, Movable)
```

Parameters of a `CharacterController` in metres, kilograms and degrees. `radius` and `height` size the capsule (`height` must exceed `2 * radius + 0.01`); `max_slope_degrees` is the steepest walkable slope and `step_height` the tallest step walked up; `mass` and `max_strength` set how hard dynamic bodies are pushed; `layer` is the object layer the character collides as; `gravity` (m/s^2, default -9.81 on Y) applies while it is not standing on ground.

Fields:

- `radius`: Float32
- `height`: Float32 — Total standing height including both hemispheres.
- `max_slope_degrees`: Float32
- `step_height`: Float32
- `mass`: Float32
- `layer`: Int
- `gravity`: Vec3
- `max_strength`: Float32 — Maximum force (N) the character can push dynamic bodies with.

- **`__init__`** `__init__( out self, radius: Float32 = 0.3, height: Float32 = 1.8, layer: Int = 1, max_slope_degrees: Float32 = 50.0, step_height: Float32 = 0.4, mass: Float32 = 70.0, )`

### struct `CharacterController`

```mojo
struct CharacterController(Movable)
```

A capsule character over Jolt's CharacterVirtual, positioned at its feet. It is not a rigid body: it has no body id. It creates and owns its capsule shape and counts itself in `world.live_characters`; its destructor destroys the character and the shape and decrements the count. It keeps a raw pointer to the `PhysicsWorld`, so the world must not move and must outlive the controller; use it from the thread that steps the world. The constructor raises when `height` does not exceed `2 * radius + 0.01` or Jolt cannot create the character.

Fields:

- `world`: Pointer[PhysicsWorld, MutUntrackedOrigin]
- `desc`: CharacterDesc
- `shape`: Shape
- `character`: Optional[Pointer[JPH_CharacterVirtual, MutUntrackedOrigin]]
- `extended`: JPH_ExtendedUpdateSettings

- **`__init__`** `__init__( out self, world: Pointer[PhysicsWorld, MutUntrackedOrigin], desc: CharacterDesc, position: Vec3, ) raises`
- **`position`** `position(self) -> Vec3`  
  World position of the character's feet, in metres.
- **`set_position`** `set_position(self, position: Vec3)`  
  Teleports the character so its feet are at `position` (metres).
- **`rotation`** `rotation(self) -> Quat`  
  World rotation of the character.
- **`set_rotation`** `set_rotation(self, rotation: Quat)`  
  Sets the world rotation of the character.
- **`velocity`** `velocity(self) -> Vec3`  
  Linear velocity of the character in metres per second.
- **`ground_state`** `ground_state(self) -> GroundState`  
  Ground contact state from Jolt's last character update (see `GroundState`).
- **`is_supported`** `is_supported(self) -> Bool`  
  Whether Jolt considers the character supported by the ground it touches.
- **`ground_normal`** `ground_normal(self) -> Vec3`  
  World-space surface normal of the ground contact.
- **`ground_velocity`** `ground_velocity(self) -> Vec3`  
  Velocity of the ground under the character in metres per second (a moving platform's), refreshed at the start of `update`.
- **`ground_body`** `ground_body(self) -> UInt32`  
  Id of the body under the character's feet; `INVALID_BODY` when Jolt reports none.
- **`update`** `update( mut self, dt: Float32, desired_velocity: Vec3, jump_speed: Float32 = 0.0 )`  
  Moves the character for one step. `desired_velocity` is the horizontal velocity the player asks for (its Y is ignored); a positive `jump_speed` launches the character when it stands on the ground. Call before `PhysicsWorld.step` with the same `dt`.


## `cinder.physics.sync`

Source: `cinder/physics/sync.mojo`

ECS <-> physics synchronisation with interpolation. `RigidBody` links an entity to a Jolt body and stores the two most recent simulated poses. The fixed-step loop is: push_kinematics(world, physics, dt)   # moved LocalTransforms drive kinematic bodies physics.step(dt) pull_bodies(world, physics)           # dynamic bodies: previous <- current <- Jolt ... write_transforms(world, alpha)        # every frame: LocalTransform at alpha between the poses Kinematic bodies follow the entity; dynamic bodies drive it; static bodies are left alone.

- `comptime RIGID_BODY_COMPONENT_ID = 7` — ECS component id of `RigidBody`.
### struct `RigidBody`

```mojo
struct RigidBody(Component)
```

The physics side of an entity. `scale` is preserved when the body writes the transform (Jolt bodies carry no scale).

Fields:

- `body`: UInt32
- `motion`: MotionType
- `interpolate`: Bool
- `previous_position`: Vec3
- `previous_rotation`: Quat
- `position`: Vec3
- `rotation`: Quat
- `scale`: Vec3
- `last_pushed_tick`: UInt32 — Change tick of the LocalTransform last pushed to a kinematic body.

- **`__init__`** `__init__( out self, body: UInt32, motion: MotionType, position: Vec3, rotation: Quat, scale: Vec3 = Vec3.one(), interpolate: Bool = True, )`
- **`component_id`** `component_id() -> Int`

### struct `PhysicsSync`

```mojo
struct PhysicsSync(Movable)
```

Per-frame physics <-> ECS synchronisation with persistent query specs and scratch buffers, so steady-state frames allocate nothing. The free functions below are convenience wrappers that build a temporary instance.

Fields:


- **`__init__`** `__init__(out self)`
- **`pull_bodies`** `pull_bodies( mut self, mut world: World, physics: PhysicsWorld ) raises -> Int`  
  After a physics step: copies each dynamic body's pose into its `RigidBody`, keeping the previous pose for interpolation. Returns the number of bodies read.
- **`push_kinematics`** `push_kinematics( mut self, mut world: World, physics: PhysicsWorld, dt: Float32 ) raises -> Int`  
  Before a physics step: kinematic bodies whose `LocalTransform` changed since the last push are moved so they reach the new pose during this step. Returns the count.
- **`write_transforms`** `write_transforms( mut self, mut world: World, alpha: Float32 ) raises -> Int`  
  Writes `LocalTransform` for dynamic bodies at `alpha` in [0, 1] between the previous and current poses. Bodies whose id is `INVALID_BODY` are skipped, as in `pull_bodies`. Only changed transforms are marked. Returns the count updated.

### def `pull_bodies`

```mojo
def pull_bodies(mut world: World, physics: PhysicsWorld) raises -> Int
```

`PhysicsSync.pull_bodies` through a temporary `PhysicsSync`, so it allocates query scratch on every call. Returns the number of dynamic bodies read.


### def `push_kinematics`

```mojo
def push_kinematics( mut world: World, physics: PhysicsWorld, dt: Float32 ) raises -> Int
```

`PhysicsSync.push_kinematics` through a temporary `PhysicsSync`, so it allocates query scratch on every call. Returns the number of kinematic bodies moved.


### def `write_transforms`

```mojo
def write_transforms(mut world: World, alpha: Float32) raises -> Int
```

`PhysicsSync.write_transforms` through a temporary `PhysicsSync`, so it allocates scratch on every call. Returns the number of transforms written.



## `cinder.physics.world`

Source: `cinder/physics/world.mojo`

Rigid-body world over Jolt (joltc): layers, shapes, bodies, queries and queued events. Every Jolt object created here is counted; `live_objects()` must be zero before the world is destroyed. The destructor does not abort on leaks (an abort would hide the real failure of a test that raised): it prints an ERROR record naming the leaked counts (`leak_report()`), and tests and `App.live_objects()` assert zero explicitly. Destroying a body or shape the world does not know (twice, or an invalid id) leaves the counts alone and records an ERROR in `diagnostics`. Jolt runs its own thread pool for the simulation step; its listeners only post to the shim queue (ADR-0003) and `poll_events()` drains that queue on the calling thread, so Mojo never runs inside a Jolt callback. Units: metres, seconds, kilograms; the coordinate system is the engine's (right-handed, Y up).

- `comptime INVALID_BODY: UInt32 = 0xFFFFFFFF` — Jolt's invalid body id.
- `comptime MAX_LAYERS = 32` — Object layers are bits of a 32-bit mask in filters.
### def `to_jolt`

```mojo
def to_jolt(v: Vec3) -> JPH_Vec3
```

Converts an engine `Vec3` to a joltc `JPH_Vec3`.


### def `from_jolt`

```mojo
def from_jolt(v: JPH_Vec3) -> Vec3
```

Converts a joltc `JPH_Vec3` to an engine `Vec3`.


### def `quat_to_jolt`

```mojo
def quat_to_jolt(q: Quat) -> JPH_Quat
```

Converts an engine `Quat` to a joltc `JPH_Quat` (x, y, z, w).


### def `quat_from_jolt`

```mojo
def quat_from_jolt(q: JPH_Quat) -> Quat
```

Converts a joltc `JPH_Quat` to an engine `Quat`.


### def `mat_to_jolt`

```mojo
def mat_to_jolt(m: Mat4) -> JPH_Mat4
```

Column-major on both sides.


### struct `LayerConfig`

```mojo
struct LayerConfig(Copyable, Movable)
```

Object layers and which pairs collide. Layer `i` is bit `i` in query masks. Layers flagged static go to a separate broad-phase layer so static geometry is never tested against itself.

Fields:

- `count`: Int
- `static_mask`: UInt32
- `pairs`: List[Tuple[Int, Int]]

- **`__init__`** `__init__(out self, count: Int, static_mask: UInt32 = 1)`
- **`collide`** `collide(mut self, a: Int, b: Int)`  
  Enables collision between layers `a` and `b` (symmetric).
- **`default`** `default() -> LayerConfig`  
  Two layers: 0 static, 1 moving; moving collides with both.

### struct `MotionType`

```mojo
struct MotionType(Equatable, ImplicitlyCopyable, Movable)
```

How a body moves: static, kinematic (driven by the caller) or dynamic (simulated).

Fields:

- `value`: UInt8

- **`static`** `static() -> MotionType`  
  A body that never moves.
- **`kinematic`** `kinematic() -> MotionType`  
  A body moved by the caller (`move_kinematic`, `set_position_rotation`), not by forces or contacts.
- **`dynamic`** `dynamic() -> MotionType`  
  A body simulated by Jolt: gravity, forces, impulses and contacts move it.
- **`to_jolt`** `to_jolt(self) -> JPH_MotionType`  
  The matching joltc `JPH_MotionType`; any value other than static or kinematic maps to dynamic.

### struct `Shape`

```mojo
struct Shape(ImplicitlyCopyable, Movable)
```

A reference-counted Jolt shape that the world tracks; copy freely, destroy once.

Fields:

- `ptr`: Optional[Pointer[JPH_Shape, MutUntrackedOrigin]]


### struct `BodyDesc`

```mojo
struct BodyDesc(Copyable, Movable)
```

Creation parameters for `PhysicsWorld.create_body`. The constructor takes the shape, position (metres), motion type, object layer and rotation; the other fields keep their defaults until set: friction 0.5, restitution 0, linear and angular damping 0.05, gravity factor 1, not a sensor, discrete motion quality, sleeping allowed, user data 0, zero initial linear velocity (ignored for static bodies) and mass 0. The shape is borrowed: the caller still destroys it with `destroy_shape`.

Fields:

- `shape`: Shape
- `position`: Vec3
- `rotation`: Quat
- `motion`: MotionType
- `layer`: Int
- `friction`: Float32
- `restitution`: Float32
- `linear_damping`: Float32
- `angular_damping`: Float32
- `gravity_factor`: Float32
- `is_sensor`: Bool — Sensors report contacts but exert no forces (triggers).
- `continuous`: Bool — Linear-cast motion quality for fast small bodies.
- `allow_sleeping`: Bool
- `user_data`: UInt64
- `linear_velocity`: Vec3
- `mass`: Float32 — Dynamic bodies: 0 derives the mass from the shape volume and Jolt's default density (1000 kg/m^3); otherwise this mass with the shape's inertia tensor scaled to match.

- **`__init__`** `__init__( out self, shape: Shape, position: Vec3, motion: MotionType, layer: Int, rotation: Quat = Quat.identity(), )`

### struct `PhysicsEventKind`

```mojo
struct PhysicsEventKind(Equatable, ImplicitlyCopyable, Movable, Writable)
```

Kind of a `PhysicsEvent`; writes as its snake_case name.

Fields:

- `value`: UInt8

- **`contact_added`** `contact_added() -> PhysicsEventKind`  
  Two bodies started touching.
- **`contact_persisted`** `contact_persisted() -> PhysicsEventKind`  
  Two bodies that were touching are still touching.
- **`contact_removed`** `contact_removed() -> PhysicsEventKind`  
  Two bodies stopped touching; the event carries only the body ids.
- **`body_activated`** `body_activated() -> PhysicsEventKind`  
  A body woke up; the event carries the body and its `user_data`.
- **`body_deactivated`** `body_deactivated() -> PhysicsEventKind`  
  A body went to sleep; the event carries the body and its `user_data`.
- **`write_to`** `write_to(self, mut w: Some[Writer])`

### struct `PhysicsEvent`

```mojo
struct PhysicsEvent(ImplicitlyCopyable, Movable)
```

One queued Jolt event. Contacts: `body1`/`body2`, world `normal` (from body 1 to body 2), first contact `point` on body 1 and the manifold `contact_count`. Activation: `body1` and the body's `user_data`.

Fields:

- `kind`: PhysicsEventKind
- `body1`: UInt32
- `body2`: UInt32
- `normal`: Vec3
- `point`: Vec3
- `contact_count`: Int
- `user_data`: UInt64

- **`involves`** `involves(self, body: UInt32) -> Bool`  
  Whether `body` is `body1` or `body2`.
- **`pair`** `pair(self, a: UInt32, b: UInt32) -> Bool`  
  Whether the event is between bodies `a` and `b`, in either order.

### struct `RayHit`

```mojo
struct RayHit(ImplicitlyCopyable, Movable)
```

Closest hit of `PhysicsWorld.raycast`: the `body` id, `fraction` of the ray (the hit is at `origin + fraction * direction`), the world `point` and the hit surface `normal`.

Fields:

- `body`: UInt32
- `fraction`: Float32
- `point`: Vec3
- `normal`: Vec3


### struct `ShapeHit`

```mojo
struct ShapeHit(ImplicitlyCopyable, Movable)
```

Closest hit of `PhysicsWorld.shape_cast`: the `body` id, `fraction` of the sweep, the contact `point` on the hit body, that body's surface `normal`, and `penetration_depth`, which is greater than zero when the shape already overlaps at the start.

Fields:

- `body`: UInt32
- `fraction`: Float32
- `point`: Vec3
- `normal`: Vec3
- `penetration_depth`: Float32


### struct `PhysicsWorld`

```mojo
struct PhysicsWorld(Movable)
```

One Jolt physics system with its thread pool, layer filters and event queue. Shapes are `Shape` handles that the caller destroys once with `destroy_shape`; bodies are `UInt32` ids that the caller destroys with `destroy_body`. Shapes, bodies and characters are counted in `live_objects()`, which should be zero before the world is destroyed; the destructor prints `leak_report()` when it is not. Destroying an unknown shape or body is ignored and recorded as an ERROR in `diagnostics`. The world must not move while a `CharacterController` points at it, and the `NativePtr` library must outlive it. Call every method from one thread (the fixed-step thread); Jolt's workers run only inside `step`. The constructor raises on a layer count outside [1, `MAX_LAYERS`], a collision pair out of range, or a failed Jolt allocation.

Fields:

- `lib`: NativePtr
- `layers`: LayerConfig
- `queue`: Optional[Pointer[CnEventQueue, MutUntrackedOrigin]]
- `jobs`: Optional[Pointer[JPH_JobSystem, MutUntrackedOrigin]]
- `bp_layers`: Optional[
- `pair_filter`: Optional[
- `bp_filter`: Optional[
- `system`: Optional[Pointer[JPH_PhysicsSystem, MutUntrackedOrigin]]
- `bodies`: Optional[Pointer[JPH_BodyInterface, MutUntrackedOrigin]]
- `contact_listener`: Optional[
- `activation_listener`: Optional[
- `body_filter`: Optional[Pointer[CnPhysicsBodyFilter, MutUntrackedOrigin]] — One reusable body filter for queries that ignore a body.
- `filters`: List[_MaskFilter] — Layer-mask filters cached per distinct mask.
- `diagnostics`: List[LogRecord] — ERROR records for misuse the world survived (destroying an unknown shape or body).
- `live_shapes`: Int
- `live_bodies`: Int
- `live_characters`: Int — Maintained by `CharacterController`.
- `steps`: Int
- `dropped_events`: UInt64

- **`__init__`** `__init__( out self, lib: NativePtr, layers: LayerConfig = LayerConfig.default(), max_bodies: Int = 4096, worker_threads: Int = 2, event_capacity: Int = 1 << 14, ) raises`  
  Creates the Jolt system. `JPH_Init` is idempotent in joltc, so every world calls it; the process-wide Jolt state is never shut down while a world exists.
- **`set_gravity`** `set_gravity(self, g: Vec3)`  
  Sets the gravity acceleration in m/s^2 (scaled per body by `BodyDesc.gravity_factor`).
- **`optimize_broad_phase`** `optimize_broad_phase(self)`  
  Call after creating many static bodies and before the first step.
- **`live_objects`** `live_objects(self) -> Int`  
  Shapes, bodies and characters still alive. Should be zero before the world is destroyed; the destructor prints `leak_report()` when it is not, but does not abort.
- **`leak_report`** `leak_report(self) -> Optional[LogRecord]`  
  The ERROR record the destructor prints when `live_objects()` is not zero (category `physics`, with `shapes`, `bodies` and `characters` fields), or None without leaks.
- **`error_count`** `error_count(self) -> Int`  
  Number of ERROR records in `diagnostics`.
- **`box`** `box( mut self, half_extents: Vec3, convex_radius: Float32 = 0.05 ) raises -> Shape`  
  Creates a box shape with `half_extents` in metres; `convex_radius` (metres) rounds its edges. The caller destroys it with `destroy_shape`. Raises when Jolt fails to create it.
- **`sphere`** `sphere(mut self, radius: Float32) raises -> Shape`  
  Creates a sphere shape of `radius` metres. The caller destroys it with `destroy_shape`. Raises when Jolt fails to create it.
- **`capsule`** `capsule( mut self, half_height: Float32, radius: Float32 ) raises -> Shape`  
  Capsule along Y: a cylinder of `2 * half_height` capped by hemispheres of `radius`.
- **`cylinder`** `cylinder( mut self, half_height: Float32, radius: Float32 ) raises -> Shape`  
  Creates a cylinder along Y, `2 * half_height` tall with `radius` (metres). The caller destroys it with `destroy_shape`. Raises when Jolt fails to create it.
- **`convex_hull`** `convex_hull( mut self, points: List[Vec3], convex_radius: Float32 = 0.05 ) raises -> Shape`  
  Creates the convex hull of `points` (metres, shape space); `convex_radius` rounds it. The caller destroys it with `destroy_shape`. Raises with fewer than 4 points or when Jolt cannot build the hull.
- **`mesh`** `mesh( mut self, vertices: List[Vec3], indices: List[UInt32] ) raises -> Shape`  
  Static triangle mesh collider (Jolt mesh shapes only work on static or kinematic bodies).
- **`scaled`** `scaled(mut self, shape: Shape, scale: Vec3) raises -> Shape`  
  Creates a shape that is `shape` scaled per axis by `scale`. It is a new tracked shape: destroy it with `destroy_shape` in addition to the original. Raises when Jolt fails to create it.
- **`destroy_shape`** `destroy_shape(mut self, shape: Shape)`  
  Releases the caller's reference to `shape` and decrements the live count; bodies created from it keep their own reference. Call once per created shape and do not use its copies afterwards; a null shape is ignored. A shape this world did not create, or one already destroyed, is not released again: the count is left alone and an ERROR record is added to `diagnostics`.
- **`create_body`** `create_body( mut self, desc: BodyDesc, activate: Bool = True ) raises -> UInt32`  
  Creates a body from `desc`, adds it to the world (awake when `activate`) and returns its id; destroy it with `destroy_body`. The body keeps its own reference to the shape, so the shape handle may be destroyed right after. A positive `desc.mass` is applied to dynamic bodies only. Raises when the layer is out of range, the shape is null, or Jolt's body limit (`max_bodies`) is reached.
- **`destroy_body`** `destroy_body(mut self, id: UInt32)`  
  Removes body `id` from the world and destroys it. Jolt may reuse the index, but with a new sequence number, so a stale id never names the new body. An id that is not a live body of this world (`INVALID_BODY`, never created, already destroyed) is ignored: the live count is left alone and an ERROR record is added to `diagnostics`.
- **`position`** `position(self, id: UInt32) -> Vec3`  
  World position of body `id`, in metres.
- **`rotation`** `rotation(self, id: UInt32) -> Quat`  
  World rotation of body `id`.
- **`transform`** `transform(self, id: UInt32) -> Mat4`  
  World matrix of body `id`, with unit scale.
- **`set_position_rotation`** `set_position_rotation( self, id: UInt32, position: Vec3, rotation: Quat, activate: Bool = True )`  
  Teleports body `id` to `position` (metres) and `rotation`, waking it when `activate`. Kinematic bodies that should carry riders are moved with `move_kinematic` instead.
- **`move_kinematic`** `move_kinematic( self, id: UInt32, target_position: Vec3, target_rotation: Quat, dt: Float32, )`  
  Sets a kinematic body's velocity so it reaches the target over `dt` (next step).
- **`linear_velocity`** `linear_velocity(self, id: UInt32) -> Vec3`  
  Linear velocity of body `id`, in metres per second.
- **`set_linear_velocity`** `set_linear_velocity(self, id: UInt32, velocity: Vec3)`  
  Sets the linear velocity of body `id`, in metres per second.
- **`angular_velocity`** `angular_velocity(self, id: UInt32) -> Vec3`  
  Angular velocity of body `id`, in radians per second.
- **`set_angular_velocity`** `set_angular_velocity(self, id: UInt32, velocity: Vec3)`  
  Sets the angular velocity of body `id`, in radians per second.
- **`add_force`** `add_force(self, id: UInt32, force: Vec3)`  
  Adds `force` (newtons) to body `id` at its centre of mass.
- **`add_impulse`** `add_impulse(self, id: UInt32, impulse: Vec3)`  
  Applies `impulse` (newton seconds) to body `id` at its centre of mass; wakes a sleeping body.
- **`add_impulse_at`** `add_impulse_at(self, id: UInt32, impulse: Vec3, point: Vec3)`  
  Applies `impulse` (newton seconds) to body `id` at the world-space `point` (metres), which can also spin it.
- **`is_active`** `is_active(self, id: UInt32) -> Bool`  
  Whether body `id` is awake.
- **`activate`** `activate(self, id: UInt32)`  
  Wakes body `id`.
- **`set_motion_type`** `set_motion_type(self, id: UInt32, motion: MotionType)`  
  Changes the motion type of body `id` and wakes it. Mesh shapes work only on static or kinematic bodies.
- **`motion_type`** `motion_type(self, id: UInt32) -> MotionType`  
  Current motion type of body `id`.
- **`user_data`** `user_data(self, id: UInt32) -> UInt64`  
  The 64-bit user value stored on body `id` (`BodyDesc.user_data` or `set_user_data`).
- **`set_user_data`** `set_user_data(self, id: UInt32, value: UInt64)`  
  Stores a 64-bit user value on body `id`; activation events report it.
- **`body_count`** `body_count(self) -> Int`  
  Number of bodies in the Jolt system.
- **`step`** `step(mut self, dt: Float32, collision_steps: Int = 1) raises`  
  Advances the simulation by `dt`. Deterministic for a given sequence of calls and inputs (Jolt's deterministic mode is on by default).
- **`poll_events`** `poll_events(mut self) -> List[PhysicsEvent]`  
  Drains the listener queue (call once per step after `step`).
- **`raycast`** `raycast( mut self, origin: Vec3, direction: Vec3, layer_mask: UInt32 = 0xFFFFFFFF, ignore_body: UInt32 = INVALID_BODY, ) -> Optional[RayHit]`  
  Closest hit along `origin + t * direction`, t in [0, 1] (`direction` carries the range). `layer_mask` selects object layers; `ignore_body` is skipped.
- **`shape_cast`** `shape_cast( mut self, shape: Shape, transform: Mat4, direction: Vec3, layer_mask: UInt32 = 0xFFFFFFFF, ignore_body: UInt32 = INVALID_BODY, ) -> Optional[ShapeHit]`  
  Closest hit of `shape` at `transform` swept by `direction` (t in [0, 1]).
