use oxiphysics::pipeline::PhysicsPipeline;
use oxiphysics_collision::BroadPhase;
use oxiphysics_collision::broadphase::{BruteForceBroadPhase, SweepAndPrune};
use oxiphysics_core::math::Vec3;
use oxiphysics_core::{Aabb, Transform};
use oxiphysics_geometry::{Shape, Sphere};
use oxiphysics_rigid::{Collider, ColliderSet, RigidBody, RigidBodySet};
use std::sync::Arc;
use std::time::Instant;
struct Lcg(u64);
impl Lcg {
fn new(seed: u64) -> Self {
Self(seed)
}
fn next_f64(&mut self) -> f64 {
self.0 = self
.0
.wrapping_mul(6364136223846793005)
.wrapping_add(1442695040888963407);
let bits = (self.0 >> 33) as f64;
bits / (u32::MAX as f64 + 1.0)
}
fn range(&mut self, lo: f64, hi: f64) -> f64 {
lo + self.next_f64() * (hi - lo)
}
}
fn generate_aabbs(n: usize, rng: &mut Lcg) -> Vec<Aabb> {
(0..n)
.map(|_| {
let cx = rng.range(-50.0, 50.0);
let cy = rng.range(-50.0, 50.0);
let cz = rng.range(-50.0, 50.0);
let half = rng.range(0.1, 2.0);
Aabb::new(
Vec3::new(cx - half, cy - half, cz - half),
Vec3::new(cx + half, cy + half, cz + half),
)
})
.collect()
}
fn bench_broadphase<B: BroadPhase>(name: &str, bp: &B, aabbs: &[Aabb], iterations: usize) {
let _ = bp.find_pairs(aabbs);
let start = Instant::now();
let mut total_pairs = 0_usize;
for _ in 0..iterations {
let pairs = bp.find_pairs(aabbs);
total_pairs += pairs.len();
}
let elapsed = start.elapsed();
let avg_us = elapsed.as_micros() as f64 / iterations as f64;
let avg_pairs = total_pairs / iterations;
println!(
" {:<20} {:>8.1} us/iter ({} pairs, {} iters)",
name, avg_us, avg_pairs, iterations
);
}
fn bench_pipeline_step(n_bodies: usize, n_steps: usize) {
let mut pipeline = PhysicsPipeline::new();
let mut bodies = RigidBodySet::new();
let mut colliders = ColliderSet::new();
let mut floor = RigidBody::new_static();
floor.transform = Transform::from_position(Vec3::new(0.0, -0.5, 0.0));
let floor_h = bodies.insert(floor);
let floor_shape: Arc<dyn Shape> = Arc::new(oxiphysics_geometry::BoxShape::new(Vec3::new(
100.0, 0.5, 100.0,
)));
colliders.insert(Collider::new(floor_shape).with_body(floor_h));
let sphere_shape: Arc<dyn Shape> = Arc::new(Sphere::new(0.25));
let side = (n_bodies as f64).sqrt().ceil() as usize;
for i in 0..n_bodies {
let row = i / side;
let col = i % side;
let mut b = RigidBody::new(1.0);
b.transform = Transform::from_position(Vec3::new(
col as f64 * 0.7 - (side as f64 * 0.35),
2.0 + (row as f64) * 0.6,
0.0,
));
let h = bodies.insert(b);
colliders.insert(
Collider::new(Arc::clone(&sphere_shape))
.with_body(h)
.with_restitution(0.3),
);
}
let dt = 1.0 / 120.0;
for _ in 0..5 {
pipeline.step(dt, &mut bodies, &colliders);
}
let start = Instant::now();
for _ in 0..n_steps {
pipeline.step(dt, &mut bodies, &colliders);
}
let elapsed = start.elapsed();
let avg_us = elapsed.as_micros() as f64 / n_steps as f64;
println!(
" Pipeline ({} bodies) {:>8.1} us/step ({} steps)",
n_bodies, avg_us, n_steps
);
}
fn main() {
println!("=== benchmark_demo: physics performance comparison ===\n");
let mut rng = Lcg::new(42);
for &n in &[100, 500, 1000] {
let aabbs = generate_aabbs(n, &mut rng);
let iterations = if n <= 500 { 200 } else { 50 };
println!("Broadphase with {} AABBs:", n);
bench_broadphase("Brute Force", &BruteForceBroadPhase, &aabbs, iterations);
bench_broadphase(
"Sweep & Prune (X)",
&SweepAndPrune::x_axis(),
&aabbs,
iterations,
);
println!();
}
println!("Pipeline stepping (rigid body sim):");
bench_pipeline_step(10, 500);
bench_pipeline_step(50, 200);
bench_pipeline_step(100, 100);
println!();
println!("NOTE: These are wall-clock micro-benchmarks for illustration.");
println!("For rigorous benchmarks, use `cargo bench` with Criterion.");
}