use bullet_physics::dynamics::{Constraint, JointFrame, PhysicsWorld, RigidBody};
use bullet_physics::math::{Quaternion, Real, Vector3};
use bullet_physics::shapes::CollisionShape;
fn main() {
let mut world = PhysicsWorld::new();
world.set_gravity(Vector3::new(0.0, -9.81, 0.0));
let anchor_a = world.add_body(RigidBody::new_static(
CollisionShape::box_shape(Vector3::splat(0.2)),
Vector3::new(0.0, 2.0, -2.0),
));
let anchor_b = world.add_body(RigidBody::new_static(
CollisionShape::box_shape(Vector3::splat(0.2)),
Vector3::new(0.0, 2.0, 2.0),
));
let bob = world.add_body(RigidBody::new_dynamic(
CollisionShape::sphere(0.15),
Vector3::new(0.354, 2.146, -2.0),
1.0,
)
.orientation(Quaternion::from_rotation_z(std::f32::consts::FRAC_PI_4 as Real)));
world.add_constraint(Constraint::point2point(
bob,
anchor_a,
Vector3::new(0.0, 0.5, 0.0),
Vector3::new(0.0, 0.5, 0.0),
));
let upper = world.add_body(RigidBody::new_dynamic(
CollisionShape::sphere(0.15),
Vector3::new(0.0, 2.0, 2.0),
1.0,
));
let lower = world.add_body(RigidBody::new_dynamic(
CollisionShape::sphere(0.15),
Vector3::new(0.0, 1.0, 2.0),
1.0,
));
world.add_constraint(Constraint::point2point(
upper,
anchor_b,
Vector3::new(0.0, 0.5, 0.0),
Vector3::new(0.0, 0.5, 0.0),
));
world.add_constraint(Constraint::point2point(
lower,
upper,
Vector3::new(0.0, 0.5, 0.0),
Vector3::new(0.0, -0.5, 0.0),
));
world.body_mut(lower).linear_velocity = Vector3::new(2.0, 0.0, 0.0);
let anchor_c = world.add_body(RigidBody::new_static(
CollisionShape::box_shape(Vector3::splat(0.2)),
Vector3::new(0.0, 2.0, 6.0),
));
let door = world.add_body(RigidBody::new_dynamic(
CollisionShape::box_shape(Vector3::new(0.5, 0.5, 0.2)),
Vector3::ZERO,
2.0,
));
let a60 = (std::f32::consts::PI / 3.0) as Real;
world.body_mut(door).position = Vector3::new(a60.cos() * 0.5, a60.sin() * 0.5 + 2.0, 6.0);
world
.body_mut(door)
.orientation = Quaternion::from_rotation_z(a60);
let mut door_hinge = Constraint::hinge(
door,
anchor_c,
Vector3::new(-0.5, 0.0, 0.0),
Vector3::Z,
Vector3::ZERO,
Vector3::Z,
);
door_hinge.hinge_mut().set_limit(-0.7, 0.7);
world.add_constraint(door_hinge);
let anchor_f = world.add_body(RigidBody::new_static(
CollisionShape::box_shape(Vector3::splat(0.2)),
Vector3::new(0.0, 2.0, -4.0),
));
let fixed_box = world.add_body(RigidBody::new_dynamic(
CollisionShape::box_shape(Vector3::splat(0.3)),
Vector3::new(0.0, 1.0, -4.0),
1.0,
));
world.add_constraint(Constraint::fixed(
fixed_box,
anchor_f,
JointFrame::new(Vector3::new(0.0, 0.5, 0.0), bullet_physics::math::Matrix3::IDENTITY),
JointFrame::new(Vector3::new(0.0, -0.5, 0.0), bullet_physics::math::Matrix3::IDENTITY),
));
world.body_mut(fixed_box).linear_velocity = Vector3::new(1.0, 0.0, 0.0);
let anchor_ct = world.add_body(RigidBody::new_static(
CollisionShape::box_shape(Vector3::splat(0.2)),
Vector3::new(0.0, 0.0, 4.0),
));
let cone_bob = world.add_body(RigidBody::new_dynamic(
CollisionShape::sphere(0.15),
Vector3::new(0.0, 0.0, 4.0),
1.0,
));
world.add_constraint(Constraint::cone_twist(
cone_bob,
anchor_ct,
JointFrame::new(Vector3::ZERO, bullet_physics::math::Matrix3::IDENTITY),
JointFrame::new(Vector3::ZERO, bullet_physics::math::Matrix3::IDENTITY),
0.52,
0.52,
-1.0,
));
world.body_mut(cone_bob).angular_velocity = Vector3::new(0.0, 0.0, 3.0);
let anchor_s = world.add_body(RigidBody::new_static(
CollisionShape::box_shape(Vector3::splat(0.2)),
Vector3::new(0.0, 0.0, 8.0),
));
let slider_box = world.add_body(RigidBody::new_dynamic(
CollisionShape::box_shape(Vector3::splat(0.3)),
Vector3::new(0.0, 0.0, 8.0),
1.0,
));
let mut slider = Constraint::slider(
anchor_s,
slider_box,
JointFrame::new(Vector3::ZERO, bullet_physics::math::Matrix3::IDENTITY),
JointFrame::new(Vector3::ZERO, bullet_physics::math::Matrix3::IDENTITY),
);
slider.slider_mut().enable_lin_motor(1.5, 100.0);
world.add_constraint(slider);
let anchor_d = world.add_body(RigidBody::new_static(
CollisionShape::box_shape(Vector3::splat(0.2)),
Vector3::new(0.0, 0.0, 12.0),
));
let dof_box = world.add_body(RigidBody::new_dynamic(
CollisionShape::box_shape(Vector3::splat(0.3)),
Vector3::new(0.0, 0.0, 12.0),
1.0,
));
let mut dof = Constraint::generic_6dof(
dof_box,
anchor_d,
JointFrame::new(Vector3::ZERO, bullet_physics::math::Matrix3::IDENTITY),
JointFrame::new(Vector3::ZERO, bullet_physics::math::Matrix3::IDENTITY),
);
dof.generic_6dof_mut().set_angular_limits(
Vector3::new(-1.0, -1.0, -0.52),
Vector3::new(1.0, 1.0, 0.52),
);
world.add_constraint(dof);
world.body_mut(dof_box).angular_velocity = Vector3::new(0.0, 0.0, 3.0);
{
let u = world.body(upper);
let l = world.body(lower);
let top_u = u.position + u.orientation * Vector3::new(0.0, 0.5, 0.0);
let top_l = l.position + l.orientation * Vector3::new(0.0, 0.5, 0.0);
let bot_u = u.position + u.orientation * Vector3::new(0.0, -0.5, 0.0);
assert!((top_u - Vector3::new(0.0, 2.5, 2.0)).length() < 1e-4, "upper pivot off");
assert!((top_l - bot_u).length() < 1e-4, "lower pivot off");
}
let dt: Real = 1.0 / 60.0;
let mut max_pivot_err: Real = 0.0;
let mut min_lower_x: Real = Real::INFINITY;
let mut door_min_angle: Real = Real::INFINITY;
let mut fixed_pivot_err: Real = 0.0;
let mut fixed_rel_angle: Real = 0.0;
let mut cone_max_swing: Real = 0.0;
let mut slider_off_axis: Real = 0.0;
let mut slider_rel_angle: Real = 0.0;
let mut dof_max_angle: Real = 0.0;
println!("t bob.x bob.y lower.x lower.y pivot_err");
for step in 0..900 {
world.step_simulation(dt);
let bob_b = world.body(bob);
let u = world.body(upper);
let l = world.body(lower);
let e1 = (bob_b.position + bob_b.orientation * Vector3::new(0.0, 0.5, 0.0)
- Vector3::new(0.0, 2.5, -2.0))
.length();
let e2 = (u.position + u.orientation * Vector3::new(0.0, 0.5, 0.0)
- Vector3::new(0.0, 2.5, 2.0))
.length();
let e3 = (l.position + l.orientation * Vector3::new(0.0, 0.5, 0.0)
- (u.position + u.orientation * Vector3::new(0.0, -0.5, 0.0)))
.length();
max_pivot_err = max_pivot_err.max(e1).max(e2).max(e3);
min_lower_x = min_lower_x.min(l.position.x);
let door_angle = world.constraint(3).kind().hinge().hinge_angle();
door_min_angle = door_min_angle.min(door_angle);
{
let f = world.body(fixed_box);
let pivot = f.position + f.orientation * Vector3::new(0.0, 0.5, 0.0);
fixed_pivot_err = fixed_pivot_err.max((pivot - Vector3::new(0.0, 1.5, -4.0)).length());
fixed_rel_angle = fixed_rel_angle
.max(2.0 * f.orientation.w.clamp(-1.0, 1.0).acos());
}
{
let ax_a = world.body(anchor_ct).orientation * Vector3::X;
let ax_b = world.body(cone_bob).orientation * Vector3::X;
cone_max_swing = cone_max_swing.max(ax_a.dot(ax_b).clamp(-1.0, 1.0).acos());
}
{
let s = world.body(slider_box);
slider_off_axis = slider_off_axis.max(s.position.y.abs()).max(s.position.z.abs() - 8.0);
slider_rel_angle = slider_rel_angle.max(2.0 * s.orientation.w.clamp(-1.0, 1.0).acos());
}
dof_max_angle = dof_max_angle.max(world.constraint(7).kind().generic_6dof().get_angle(2).abs());
if step % 60 == 0 {
println!(
"{:.2} {:.3} {:.3} {:.3} {:.3} {:.4}",
step as Real * dt,
bob_b.position.x,
bob_b.position.y,
l.position.x,
l.position.y,
e1.max(e2).max(e3)
);
}
}
let bob_b = world.body(bob);
let l = world.body(lower);
let door_angle = world.constraint(3).kind().hinge().hinge_angle();
let slider_x = world.body(slider_box).position.x;
println!(
"final bob = ({:.2}, {:.2}), lower = ({:.2}, {:.2}), max pivot err = {:.4}, min lower x = {:.2}, door angle = {:.2}, slider x = {:.1}, cone swing = {:.3}, dof angle = {:.3}",
bob_b.position.x,
bob_b.position.y,
l.position.x,
l.position.y,
max_pivot_err,
min_lower_x,
door_angle,
slider_x,
cone_max_swing,
dof_max_angle
);
assert!(max_pivot_err < 0.2, "pivot error too large: {max_pivot_err}");
assert!(min_lower_x < -0.5, "double pendulum did not swing: {min_lower_x}");
assert!(
door_min_angle > -0.75,
"hinged door passed the low limit: min angle {door_min_angle}"
);
assert!(
(door_angle + 0.7).abs() < 0.05,
"hinged door did not settle on its limit: {door_angle}"
);
assert!(fixed_pivot_err < 0.05, "weld stretched: {fixed_pivot_err}");
assert!(fixed_rel_angle < 0.1, "weld rotated: {fixed_rel_angle}");
assert!(cone_max_swing < 0.6, "cone exceeded: {cone_max_swing}");
assert!((slider_x - 22.5).abs() < 2.0, "slider did not travel: x = {slider_x}");
assert!(slider_off_axis < 0.05, "slider left the rail: {slider_off_axis}");
assert!(slider_rel_angle < 0.15, "slider rotated: {slider_rel_angle}");
assert!(dof_max_angle < 0.6, "6DOF passed its limit: {dof_max_angle}");
println!("OK: point-to-point, hinge, fixed, cone-twist, slider and generic-6DOF joints all behave.");
}