Physics

PhysicsBody is an axis-aligned box. Position is the feet, at the bottom center, not the center of mass. Default size is 0.6 m wide and 1.8 m tall, with the eye 1.62 m above the feet. Velocity and acceleration are mutable Vec3s. Acceleration is cleared at the end of each step.

One step

PhysicsEngine.step(body, world, dt) integrates that many seconds. Order is fixed: sample the blocks overlapping the body, add gravity (scaled in fluid, plus buoyancy), integrate acceleration, apply drag, clamp terminal velocity, then move X, Y, and Z separately. A colliding axis move is reverted and that axis velocity is zeroed. A downward hit sets onGround. A short downward probe (groundStick, default 0.08 m) keeps the body grounded on shallow slopes and stair lips.

applyMoveWish sets horizontal velocity from a desired XZ direction. Length is normalized, then scaled by walk or sprint speed and by the surface speed multiplier. In fluid the speed is reduced further and multiplied by the water block’s speed multiplier. With no wish, a grounded body stops. An airborne body keeps 92% of its horizontal velocity.

tryJump sets upward velocity from defaultJumpVelocity times the surface jump multiplier. In fluid, holding jump raises the body toward swimUpSpeed instead.

When the native library is loaded, these three methods run in Rust and match the Java formulas for that single dt. The 1 ms frame loop does not call step again. It integrates inside Rust up to frameNs. Sixteen 1 ms steps are not the same numbers as one 16 ms step. That difference is the point of the fast clock.

Defaults

PhysicsSettings starts at outdoor values. Change them on the instance you pass to the controller. They are not toml keys.

FieldDefault
Gravity−28 m/s²
Terminal velocity−55 m/s
Air drag0.03
Walk / sprint6.2 / 12.2 m/s
Jump velocity11.8 m/s
Swim-up speed6.2 m/s
Fluid gravity scale0.35

OptiShoot replaces walk and sprint with 8.4 and 13.5. Sandbox keeps the defaults. First person also uses a coyote window of 0.14 s and a jump buffer of 0.18 s. Those constants live on FirstPersonController and are copied into Rust when the sim is published. Third person jumps on the rising edge of the jump key and has no coyote window.

Voxels

VoxelPhysicsQuery.getBlock returns the block at integer coordinates, or null for air and for chunks that are not loaded. Solids stop the body. Fluids set drag and buoyancy from the block prototype in BlockRegistry. The block under the feet sets friction, speed, and jump multipliers for the next wish.

A one-off step against any query copies the nearby cells into a Rust scratch volume, then integrates. The 1 ms loop does not do that. It reads the voxel mirror: every chunk ChunkStore loads or edits is uploaded as raw bytes (0 is air, otherwise BlockType.ordinal() + 1). Unloaded space is air, same as a null query.

Chunks are 16³. ChunkStreamingManager loads a Chebyshev square in XZ times a Y range from render_distance and render_distance_y. Empty chunks are published with no GPU buffer. The load screen counts those as finished. It does not wait for a mesh that will never exist.

Controllers

FirstPersonController faces with camera yaw, builds a wish from WASD, steps, then writes the eye position back onto the camera. Falling below Y = −20 respawns at the stored point.

ThirdPersonController keeps its own yaw and pitch. The camera sits on the look ray behind the eyes. Default boom length is 4 m. A voxel ray shortens the boom when a wall is in the way. setMoveMul scales speed for aim and weapons. Crouch multiplies that by 0.55. The view cycle is cyclePerspective.