rsbullet 0.3.10

Compiles bullet3 and exposes rust bindings to the C API
Documentation
use std::{f64::consts::FRAC_2_PI, thread::sleep, time::Duration};

use nalgebra as na;
use rsbullet::{
    BulletResult, CameraImageOptions, CollisionGeometry, CollisionId, CollisionShapeOptions,
    ControlMode, DebugVisualizerFlag, DebugVisualizerOptions, DynamicsUpdate, JointType, Mode,
    MultiBodyBase, MultiBodyCreateOptions, MultiBodyLink, PhysicsClient, Renderer,
    ResetDebugVisualizerCameraOptions,
};

const VERTICES: [[f64; 3]; 8] = [
    [-0.246350, -0.246483, -0.000624],
    [-0.151407, -0.176325, 0.172867],
    [-0.246350, 0.249205, -0.000624],
    [-0.151407, 0.129477, 0.172867],
    [0.249338, -0.246483, -0.000624],
    [0.154395, -0.176325, 0.172867],
    [0.249338, 0.249205, -0.000624],
    [0.154395, 0.129477, 0.172867],
];
const INDICES: [i32; 36] = [
    0, 3, 2, 3, 6, 2, 7, 4, 6, 5, 0, 4, 6, 0, 2, 3, 5, 7, 0, 1, 3, 3, 7, 6, 7, 5, 4, 5, 1, 0, 6, 4,
    0, 3, 1, 5,
];

fn main() -> BulletResult<()> {
    let mut client = PhysicsClient::connect(Mode::Gui)?;
    client
        .set_default_search_path()?
        .set_gravity([0., 0., -10.])?
        .reset_simulation()?;

    client
        .reset_debug_visualizer_camera(&ResetDebugVisualizerCameraOptions {
            distance: 15.,
            yaw: -346.,
            pitch: -16.,
            target: [-15., 0., 1.],
        })?
        .configure_debug_visualizer(&DebugVisualizerOptions::Flag(
            DebugVisualizerFlag::CovEnableWireframe,
            false,
        ))?;

    let col_sphere = client.create_collision_shape(
        &CollisionGeometry::Sphere { radius: 0.05 },
        None::<CollisionShapeOptions>,
    )?;
    let stone = client.create_collision_shape(
        &CollisionGeometry::Mesh { vertices: &VERTICES, indices: &INDICES, scale: [0.; 3] },
        None::<CollisionShapeOptions>,
    )?;
    let col_box = client.create_collision_shape(
        &CollisionGeometry::Box { half_extents: [0.5, 2.5, 00.1] },
        None::<CollisionShapeOptions>,
    )?;

    let mut segment_start = 0.;

    for _ in 0..5 {
        client.create_multi_body(&MultiBodyCreateOptions {
            base: MultiBodyBase {
                mass: 0.,
                collision_shape: CollisionId(col_box),
                pose: na::Isometry3::translation(segment_start, 0., -0.1),
                ..Default::default()
            },
            ..Default::default()
        })?;
        segment_start -= 1.;
    }

    for i in 0..5 {
        client.create_multi_body(&MultiBodyCreateOptions {
            base: MultiBodyBase {
                mass: 0.,
                collision_shape: CollisionId(col_box),
                pose: na::Isometry3::translation(segment_start, 0., -0.1 + 1. * (i % 2) as f64),
                ..Default::default()
            },
            ..Default::default()
        })?;
        segment_start -= 1.;
    }

    for i in 0..5 {
        client.create_multi_body(&MultiBodyCreateOptions {
            base: MultiBodyBase {
                mass: 0.,
                collision_shape: CollisionId(col_box),
                pose: na::Isometry3::translation(segment_start, 0., -0.1),
                ..Default::default()
            },
            ..Default::default()
        })?;
        segment_start -= 1.;
        if i % 2 == 0 {
            let transform = [segment_start, (i % 3) as f64, -0.1 + 2.5].into();
            let rotation = na::UnitQuaternion::from_euler_angles(FRAC_2_PI, 0., FRAC_2_PI);
            client.create_multi_body(&MultiBodyCreateOptions {
                base: MultiBodyBase {
                    mass: 0.,
                    collision_shape: CollisionId(col_box),
                    pose: na::Isometry3::from_parts(transform, rotation),
                    ..Default::default()
                },
                ..Default::default()
            })?;
        }
    }

    for i in 0..5 {
        client.create_multi_body(&MultiBodyCreateOptions {
            base: MultiBodyBase {
                mass: 0.,
                collision_shape: CollisionId(col_box),
                pose: na::Isometry3::translation(segment_start, 0., -0.1),
                ..Default::default()
            },
            ..Default::default()
        })?;
        for j in 0..4 {
            let pose = [segment_start, (i % 2) as f64 / 2. + j as f64 - 2.0, 0.].into();
            client.create_multi_body(&MultiBodyCreateOptions {
                base: MultiBodyBase {
                    mass: 0.,
                    collision_shape: CollisionId(stone),
                    pose,
                    ..Default::default()
                },
                ..Default::default()
            })?;
        }
        segment_start -= 1.;
    }

    let base_pose = [segment_start, 0., -0.1].into();

    for _ in 0..5 {
        let box_id = client.create_multi_body(&MultiBodyCreateOptions {
            base: MultiBodyBase {
                mass: 0.,
                pose: base_pose,
                collision_shape: CollisionId(col_sphere),
                ..Default::default()
            },
            links: &[MultiBodyLink {
                mass: 1.,
                joint_type: JointType::Revolute,
                collision_shape: CollisionId(col_box),
                parent_index: Some(0),
                joint_axis: [1., 0., 0.],
                ..Default::default()
            }],
            ..Default::default()
        })?;
        client.change_dynamics(
            box_id,
            -1,
            &DynamicsUpdate {
                spinning_friction: Some(0.001),
                rolling_friction: Some(0.001),
                linear_damping: Some(0.),
                ..Default::default()
            },
        )?;
        for joint in 0..client.get_num_joints(box_id) {
            let target_velocity = if joint % 2 == 0 { 10. } else { -10. };
            client.set_joint_motor_control(
                box_id,
                joint,
                ControlMode::Velocity(target_velocity),
                None,
            )?;
        }
    }

    loop {
        let cam = client.get_debug_visualizer_camera()?;
        client.get_camera_image(&CameraImageOptions {
            width: 256,
            height: 256,
            view_matrix: Some(cam.view_matrix),
            projection_matrix: Some(cam.projection_matrix),
            renderer: Some(Renderer::BulletHardwareOpenGl),
            ..Default::default()
        })?;
        client.step_simulation()?;
        sleep(Duration::from_secs_f64(0.01));
    }
}