jugar-physics

Physics simulation with tiered backend selection.

Backend Tiers

TierBackendCapability
1WebGPU compute shaders10,000+ rigid bodies
2WASM SIMD 128-bit1,000+ rigid bodies
3Scalar fallbackBasic physics

Backend is selected automatically at runtime.

Rigid Bodies

#![allow(unused)]
fn main() {
use jugar_physics::prelude::*;

let mut physics = PhysicsWorld::new();

// Create static body
let ground = physics.create_static_body(
    Position::new(400.0, 550.0),
    Collider::box_shape(800.0, 100.0),
);

// Create dynamic body
let ball = physics.create_dynamic_body(
    Position::new(400.0, 100.0),
    Collider::circle(20.0),
    RigidBodyConfig {
        mass: 1.0,
        restitution: 0.8,
        friction: 0.3,
    },
);
}

Collision Detection

Spatial hashing for broad-phase:

#![allow(unused)]
fn main() {
// Check collisions
for contact in physics.get_contacts() {
    match (contact.body_a, contact.body_b) {
        (a, b) if a == player && is_coin(b) => {
            collect_coin(b);
        }
        _ => {}
    }
}
}

Forces and Impulses

#![allow(unused)]
fn main() {
// Apply force (continuous)
physics.apply_force(body, Vec2::new(0.0, -100.0));

// Apply impulse (instant)
physics.apply_impulse(body, Vec2::new(500.0, 0.0));

// Set velocity directly
physics.set_velocity(body, Vec2::new(10.0, 0.0));
}

Raycasting

#![allow(unused)]
fn main() {
let ray = Ray::new(
    Vec2::new(100.0, 100.0),  // origin
    Vec2::new(1.0, 0.0),       // direction
);

if let Some(hit) = physics.raycast(ray, 500.0) {
    println!("Hit body {:?} at {:?}", hit.body, hit.point);
}
}

Configuration

#![allow(unused)]
fn main() {
let config = PhysicsConfig {
    gravity: Vec2::new(0.0, 980.0),  // pixels/s²
    iterations: 8,                     // solver iterations
    sleep_threshold: 0.1,              // velocity threshold
};

let mut physics = PhysicsWorld::with_config(config);
}