diff options
| author | Sébastien Crozet <sebcrozet@dimforge.com> | 2024-01-21 21:02:23 +0100 |
|---|---|---|
| committer | Sébastien Crozet <sebcrozet@dimforge.com> | 2024-01-21 21:02:27 +0100 |
| commit | 9b87f06a856c4d673642e210f8b0986cfdbac3af (patch) | |
| tree | b4f4eaac0e5004f8ba3fccd42e5aea4fd565dcc6 /examples3d | |
| parent | 9ac3503b879f95fcdf5414470ba5aedf195b9a97 (diff) | |
| download | rapier-9b87f06a856c4d673642e210f8b0986cfdbac3af.tar.gz rapier-9b87f06a856c4d673642e210f8b0986cfdbac3af.tar.bz2 rapier-9b87f06a856c4d673642e210f8b0986cfdbac3af.zip | |
feat: implement new "small-steps" solver + joint improvements
Diffstat (limited to 'examples3d')
| -rw-r--r-- | examples3d/all_examples3.rs | 16 | ||||
| -rw-r--r-- | examples3d/debug_chain_high_mass_ratio3.rs | 73 | ||||
| -rw-r--r-- | examples3d/debug_cube_high_mass_ratio3.rs | 96 | ||||
| -rw-r--r-- | examples3d/debug_long_chain3.rs | 63 | ||||
| -rw-r--r-- | examples3d/joints3.rs | 1 | ||||
| -rw-r--r-- | examples3d/one_way_platforms3.rs | 2 | ||||
| -rw-r--r-- | examples3d/platform3.rs | 24 | ||||
| -rw-r--r-- | examples3d/rope_joints3.rs | 4 | ||||
| -rw-r--r-- | examples3d/spring_joints3.rs | 64 | ||||
| -rw-r--r-- | examples3d/vehicle_joints3.rs | 227 |
10 files changed, 549 insertions, 21 deletions
diff --git a/examples3d/all_examples3.rs b/examples3d/all_examples3.rs index ad4d226..851bc8a 100644 --- a/examples3d/all_examples3.rs +++ b/examples3d/all_examples3.rs @@ -34,7 +34,10 @@ mod heightfield3; mod joints3; // mod joints3; mod character_controller3; +mod debug_chain_high_mass_ratio3; +mod debug_cube_high_mass_ratio3; mod debug_internal_edges3; +mod debug_long_chain3; mod joint_motor_position3; mod keva3; mod locked_rotations3; @@ -45,8 +48,10 @@ mod primitives3; mod restitution3; mod rope_joints3; mod sensor3; +mod spring_joints3; mod trimesh3; mod vehicle_controller3; +mod vehicle_joints3; fn demo_name_from_command_line() -> Option<String> { let mut args = std::env::args(); @@ -105,8 +110,10 @@ pub fn main() { ("Restitution", restitution3::init_world), ("Rope Joints", rope_joints3::init_world), ("Sensor", sensor3::init_world), + ("Spring Joints", spring_joints3::init_world), ("TriMesh", trimesh3::init_world), ("Vehicle controller", vehicle_controller3::init_world), + ("Vehicle joints", vehicle_joints3::init_world), ("Keva tower", keva3::init_world), ("Newton cradle", newton_cradle3::init_world), ("(Debug) multibody_joints", debug_articulations3::init_world), @@ -122,6 +129,15 @@ pub fn main() { ), ("(Debug) friction", debug_friction3::init_world), ("(Debug) internal edges", debug_internal_edges3::init_world), + ("(Debug) long chain", debug_long_chain3::init_world), + ( + "(Debug) high mass ratio: chain", + debug_chain_high_mass_ratio3::init_world, + ), + ( + "(Debug) high mass ratio: cube", + debug_cube_high_mass_ratio3::init_world, + ), ("(Debug) triangle", debug_triangle3::init_world), ("(Debug) trimesh", debug_trimesh3::init_world), ("(Debug) cylinder", debug_cylinder3::init_world), diff --git a/examples3d/debug_chain_high_mass_ratio3.rs b/examples3d/debug_chain_high_mass_ratio3.rs new file mode 100644 index 0000000..809311e --- /dev/null +++ b/examples3d/debug_chain_high_mass_ratio3.rs @@ -0,0 +1,73 @@ +use rapier3d::prelude::*; +use rapier_testbed3d::Testbed; + +pub fn init_world(testbed: &mut Testbed) { + /* + * World + */ + let mut bodies = RigidBodySet::new(); + let mut colliders = ColliderSet::new(); + let mut impulse_joints = ImpulseJointSet::new(); + let mut multibody_joints = MultibodyJointSet::new(); + let use_articulations = false; + + /* + * Create a chain with a very heavy ball at the end. + */ + let num = 17; + let rad = 0.2; + + let mut body_handles = Vec::new(); + + for i in 0..num { + let fi = i as f32; + + let status = if i == 0 { + RigidBodyType::Fixed + } else { + RigidBodyType::Dynamic + }; + + let ball_rad = if i == num - 1 { rad * 10.0 } else { rad }; + let shift1 = rad * 1.1; + let shift2 = ball_rad + rad * 0.1; + let z = if i == 0 { + 0.0 + } else { + (fi - 1.0) * 2.0 * shift1 + shift1 + shift2 + }; + + let rigid_body = RigidBodyBuilder::new(status) + .translation(vector![0.0, 0.0, z]) + .additional_solver_iterations(16); + let child_handle = bodies.insert(rigid_body); + let collider = ColliderBuilder::ball(ball_rad); + colliders.insert_with_parent(collider, child_handle, &mut bodies); + + // Vertical joint. + if i > 0 { + let parent_handle = *body_handles.last().unwrap(); + let joint = if i == 1 { + SphericalJointBuilder::new().local_anchor2(point![0.0, 0.0, -shift1 * 2.0]) + } else { + SphericalJointBuilder::new() + .local_anchor1(point![0.0, 0.0, shift1]) + .local_anchor2(point![0.0, 0.0, -shift2]) + }; + + if use_articulations { + multibody_joints.insert(parent_handle, child_handle, joint, true); + } else { + impulse_joints.insert(parent_handle, child_handle, joint, true); + } + } + + body_handles.push(child_handle); + } + + /* + * Set up the testbed. + */ + testbed.set_world(bodies, colliders, impulse_joints, multibody_joints); + testbed.look_at(point![10.0, 10.0, 10.0], Point::origin()); +} diff --git a/examples3d/debug_cube_high_mass_ratio3.rs b/examples3d/debug_cube_high_mass_ratio3.rs new file mode 100644 index 0000000..ffaf793 --- /dev/null +++ b/examples3d/debug_cube_high_mass_ratio3.rs @@ -0,0 +1,96 @@ +use rapier3d::prelude::*; +use rapier_testbed3d::Testbed; + +pub fn init_world(testbed: &mut Testbed) { + /* + * World + */ + let mut bodies = RigidBodySet::new(); + let mut colliders = ColliderSet::new(); + let impulse_joints = ImpulseJointSet::new(); + let multibody_joints = MultibodyJointSet::new(); + + let num_levels = 4; + let stick_len = 2.0; + let stick_rad = 0.2; + + /* + * Floor. + */ + let floor_body = + RigidBodyBuilder::fixed().translation(vector![0.0, -stick_len - stick_rad, 0.0]); + let floor_handle = bodies.insert(floor_body); + let floor_cube = ColliderBuilder::cuboid(stick_len, stick_len, stick_len); + colliders.insert_with_parent(floor_cube, floor_handle, &mut bodies); + + /* + * Create a stack of capsule with a very heavy cube on top. + */ + for i in 0..num_levels { + let fi = i as f32; + + let body = RigidBodyBuilder::dynamic().translation(vector![ + 0.0, + fi * stick_rad * 4.0, + -(stick_len / 2.0 - stick_rad) + ]); + let handle = bodies.insert(body); + let capsule = ColliderBuilder::cuboid(stick_len / 2.0, stick_rad, stick_rad); + colliders.insert_with_parent(capsule, handle, &mut bodies); + + let body = RigidBodyBuilder::dynamic().translation(vector![ + 0.0, + fi * stick_rad * 4.0, + (stick_len / 2.0 - stick_rad) + ]); + let handle = bodies.insert(body); + let capsule = ColliderBuilder::cuboid(stick_len / 2.0, stick_rad, stick_rad); + colliders.insert_with_parent(capsule, handle, &mut bodies); + + let body = RigidBodyBuilder::dynamic().translation(vector![ + -(stick_len / 2.0 - stick_rad), + (fi + 0.5) * stick_rad * 4.0, + 0.0 + ]); + let handle = bodies.insert(body); + let capsule = ColliderBuilder::cuboid(stick_rad, stick_rad, stick_len / 2.0); + colliders.insert_with_parent(capsule, handle, &mut bodies); + + let body = RigidBodyBuilder::dynamic().translation(vector![ + (stick_len / 2.0 - stick_rad), + (fi + 0.5) * stick_rad * 4.0, + 0.0 + ]); + let handle = bodies.insert(body); + let capsule = ColliderBuilder::cuboid(stick_rad, stick_rad, stick_len / 2.0); + colliders.insert_with_parent(capsule, handle, &mut bodies); + } + + /* + * Big cube on top. + */ + let cube_len = stick_len * 2.0; + let floor_body = RigidBodyBuilder::dynamic() + .translation(vector![ + 0.0, + cube_len / 2.0 + (num_levels as f32 - 0.25) * stick_rad * 4.0, + 0.0 + ]) + .additional_solver_iterations(36); + let floor_handle = bodies.insert(floor_body); + let floor_cube = ColliderBuilder::cuboid(cube_len / 2.0, cube_len / 2.0, cube_len / 2.0); + colliders.insert_with_parent(floor_cube, floor_handle, &mut bodies); + + let small_mass = + MassProperties::from_cuboid(1.0, vector![stick_rad, stick_rad, stick_len / 2.0]).mass(); + let big_mass = + MassProperties::from_cuboid(1.0, vector![cube_len / 2.0, cube_len / 2.0, cube_len / 2.0]) + .mass(); + println!("debug_cube_high_mass_ratio3: small stick mass: {small_mass}, big cube mass: {big_mass}, mass_ratio: {}", big_mass / small_mass); + + /* + * Set up the testbed. + */ + testbed.set_world(bodies, colliders, impulse_joints, multibody_joints); + testbed.look_at(point![10.0, 10.0, 10.0], Point::origin()); +} diff --git a/examples3d/debug_long_chain3.rs b/examples3d/debug_long_chain3.rs new file mode 100644 index 0000000..e2bb990 --- /dev/null +++ b/examples3d/debug_long_chain3.rs @@ -0,0 +1,63 @@ +use rapier3d::prelude::*; +use rapier_testbed3d::Testbed; + +pub fn init_world(testbed: &mut Testbed) { + /* + * World + */ + let mut bodies = RigidBodySet::new(); + let mut colliders = ColliderSet::new(); + let mut impulse_joints = ImpulseJointSet::new(); + let mut multibody_joints = MultibodyJointSet::new(); + let use_articulations = false; + + /* + * Create the long chain. + */ + let num = 100; + let rad = 0.2; + let shift = rad * 2.2; + + let mut body_handles = Vec::new(); + + for i in 0..num { + let fi = i as f32; + + let status = if i == 0 { + RigidBodyType::Fixed + } else { + RigidBodyType::Dynamic + }; + + let rigid_body = RigidBodyBuilder::new(status).translation(vector![0.0, 0.0, fi * shift]); + let child_handle = bodies.insert(rigid_body); + let collider = ColliderBuilder::ball(rad); + colliders.insert_with_parent(collider, child_handle, &mut bodies); + + // Vertical joint. + if i > 0 { + let parent_handle = *body_handles.last().unwrap(); + let joint = if i == 1 { + SphericalJointBuilder::new().local_anchor2(point![0.0, 0.0, -shift]) + } else { + SphericalJointBuilder::new() + .local_anchor1(point![0.0, 0.0, shift / 2.0]) + .local_anchor2(point![0.0, 0.0, -shift / 2.0]) + }; + + if use_articulations { + multibody_joints.insert(parent_handle, child_handle, joint, true); + } else { + impulse_joints.insert(parent_handle, child_handle, joint, true); + } + } + + body_handles.push(child_handle); + } + + /* + * Set up the testbed. + */ + testbed.set_world(bodies, colliders, impulse_joints, multibody_joints); + testbed.look_at(point![10.0, 10.0, 10.0], Point::origin()); +} diff --git a/examples3d/joints3.rs b/examples3d/joints3.rs index dc4deed..18a11e7 100644 --- a/examples3d/joints3.rs +++ b/examples3d/joints3.rs @@ -20,7 +20,6 @@ fn create_coupled_joints( let joint1 = GenericJointBuilder::new(JointAxesMask::empty()) .limits(JointAxis::X, [-3.0, 3.0]) .limits(JointAxis::Y, [0.0, 3.0]) - .limits(JointAxis::Z, [0.0, 3.0]) .coupled_axes(JointAxesMask::Y | JointAxesMask::Z); if use_articulations { diff --git a/examples3d/one_way_platforms3.rs b/examples3d/one_way_platforms3.rs index 3b2715a..1a5704c 100644 --- a/examples3d/one_way_platforms3.rs +++ b/examples3d/one_way_platforms3.rs @@ -90,7 +90,7 @@ pub fn init_world(testbed: &mut Testbed) { * depending on their position. */ testbed.add_callback(move |graphics, physics, _, run_state| { - if run_state.timestep_id % 50 == 0 && physics.bodies.len() <= 7 { + if run_state.timestep_id % 200 == 0 && physics.bodies.len() <= 7 { // Spawn a new cube. let collider = ColliderBuilder::cuboid(1.0, 2.0, 1.5); let body = RigidBodyBuilder::dynamic().translation(vector![0.0, 6.0, 20.0]); diff --git a/examples3d/platform3.rs b/examples3d/platform3.rs index d42cb76..9025afa 100644 --- a/examples3d/platform3.rs +++ b/examples3d/platform3.rs @@ -29,12 +29,12 @@ pub fn init_world(testbed: &mut Testbed) { let shift = rad * 2.0; let centerx = shift * num as f32 / 2.0; - let centery = shift / 2.0 + 3.04; let centerz = shift * num as f32 / 2.0; for i in 0usize..num { for j in 0usize..num { for k in 0usize..num { + let centery = if j >= num / 2 { 5.0 } else { 3.0 }; let x = i as f32 * shift - centerx; let y = j as f32 * shift + centery; let z = k as f32 * shift - centerz; @@ -51,11 +51,8 @@ pub fn init_world(testbed: &mut Testbed) { /* * Setup a velocity-based kinematic rigid body. */ - let platform_body = RigidBodyBuilder::kinematic_velocity_based().translation(vector![ - 0.0, - 1.5 + 0.8, - -10.0 * rad - ]); + let platform_body = + RigidBodyBuilder::kinematic_velocity_based().translation(vector![0.0, 1.5 + 0.8, 0.0]); let velocity_based_platform_handle = bodies.insert(platform_body); let collider = ColliderBuilder::cuboid(rad * 10.0, rad, rad * 10.0); colliders.insert_with_parent(collider, velocity_based_platform_handle, &mut bodies); @@ -65,8 +62,8 @@ pub fn init_world(testbed: &mut Testbed) { */ let platform_body = RigidBodyBuilder::kinematic_position_based().translation(vector![ 0.0, - 2.0 + 1.5 + 0.8, - -10.0 * rad + 3.0 + 1.5 + 0.8, + 0.0 ]); let position_based_platform_handle = bodies.insert(platform_body); let collider = ColliderBuilder::cuboid(rad * 10.0, rad, rad * 10.0); @@ -75,22 +72,17 @@ pub fn init_world(testbed: &mut Testbed) { /* * Setup a callback to control the platform. */ - let mut count = 0; testbed.add_callback(move |_, physics, _, run_state| { - count += 1; - if count % 100 > 50 { - return; - } - let velocity = vector![ 0.0, - (run_state.time * 5.0).sin(), - run_state.time.sin() * 5.0 + (run_state.time * 2.0).sin(), + run_state.time.sin() * 2.0 ]; // Update the velocity-based kinematic body by setting its velocity. if let Some(platform) = physics.bodies.get_mut(velocity_based_platform_handle) { platform.set_linvel(velocity, true); + platform.set_angvel(vector![0.0, 0.2, 0.0], true); } // Update the position-based kinematic body by setting its next position. diff --git a/examples3d/rope_joints3.rs b/examples3d/rope_joints3.rs index 329364b..9696682 100644 --- a/examples3d/rope_joints3.rs +++ b/examples3d/rope_joints3.rs @@ -80,9 +80,7 @@ pub fn init_world(testbed: &mut Testbed) { let collider = ColliderBuilder::ball(rad); colliders.insert_with_parent(collider, child_handle, &mut bodies); - let joint = RopeJointBuilder::new() - .local_anchor2(point![0.0, 0.0, 0.0]) - .limits([2.0, 2.0]); + let joint = RopeJointBuilder::new(2.0); impulse_joints.insert(character_handle, child_handle, joint, true); /* diff --git a/examples3d/spring_joints3.rs b/examples3d/spring_joints3.rs new file mode 100644 index 0000000..3336402 --- /dev/null +++ b/examples3d/spring_joints3.rs @@ -0,0 +1,64 @@ +use rapier3d::prelude::*; +use rapier_testbed3d::Testbed; + +pub fn init_world(testbed: &mut Testbed) { + /* + * World + */ + let mut bodies = RigidBodySet::new(); + let mut colliders = ColliderSet::new(); + let mut impulse_joints = ImpulseJointSet::new(); + let multibody_joints = MultibodyJointSet::new(); + + /* + * Fixed ground to attach one end of the joints. + */ + let rigid_body = RigidBodyBuilder::fixed(); + let ground_handle = bodies.insert(rigid_body); + + /* + * Spring joints with a variety of spring parameters. + * The middle one has uses critical damping. + */ + let num = 30; + let radius = 0.5; + let mass = Ball::new(radius).mass_properties(1.0).mass(); + let stiffness = 1.0e3; + let critical_damping = 2.0 * (stiffness * mass).sqrt(); + for i in 0..=num { + let x_pos = -6.0 + 1.5 * i as f32; + let ball_pos = point![x_pos, 4.5, 0.0]; + let rigid_body = RigidBodyBuilder::dynamic() + .translation(ball_pos.coords) + .can_sleep(false); + let handle = bodies.insert(rigid_body); + let collider = ColliderBuilder::ball(radius); + colliders.insert_with_parent(collider, handle, &mut bodies); + + let damping_ratio = i as f32 / (num as f32 / 2.0); + let damping = damping_ratio * critical_damping; + let joint = SpringJointBuilder::new(0.0, stiffness, damping) + .local_anchor1(ball_pos - Vector::y() * 3.0); + impulse_joints.insert(ground_handle, handle, joint, true); + + // Box that will fall on to of the springed balls, makes the simulation funier to watch. + let rigid_body = + RigidBodyBuilder::dynamic().translation(ball_pos.coords + Vector::y() * 5.0); + let handle = bodies.insert(rigid_body); + let collider = ColliderBuilder::cuboid(radius, radius, radius).density(100.0); + colliders.insert_with_parent(collider, handle, &mut bodies); + } + + /* + * Set up the testbed. + */ + testbed.set_world_with_params( + bodies, + colliders, + impulse_joints, + multibody_joints, + vector![0.0, -9.81, 0.0], + (), + ); + testbed.look_at(point![15.0, 5.0, 42.0], point![13.0, 1.0, 1.0]); +} diff --git a/examples3d/vehicle_joints3.rs b/examples3d/vehicle_joints3.rs new file mode 100644 index 0000000..ddd672c --- /dev/null +++ b/examples3d/vehicle_joints3.rs @@ -0,0 +1,227 @@ +use rapier3d::prelude::*; +use rapier_testbed3d::{KeyCode, Testbed}; + +pub fn init_world(testbed: &mut Testbed) { + /* + * World + */ + let mut bodies = RigidBodySet::new(); + let mut colliders = ColliderSet::new(); + let mut impulse_joints = ImpulseJointSet::new(); + let multibody_joints = MultibodyJointSet::new(); + + /* + * Ground. + */ + let ground_size = Vector::new(60.0, 0.4, 60.0); + let nsubdivs = 100; + + let heights = DMatrix::from_fn(nsubdivs + 1, nsubdivs + 1, |i, j| { + -(i as f32 * ground_size.x / (nsubdivs as f32) / 2.0).cos() + - (j as f32 * ground_size.z / (nsubdivs as f32) / 2.0).cos() + }); + + let collider = ColliderBuilder::heightfield(heights, ground_size) + .translation(vector![-7.0, 0.0, 0.0]) + .friction(1.0); + colliders.insert(collider); + + /* + * Vehicle we will control manually, simulated using joints. + * Strongly inspired from https://github.com/h3r2tic/cornell-mcray/blob/main/src/car.rs (MIT/Apache license). + */ + const CAR_GROUP: Group = Group::GROUP_1; + + let wheel_params = [ + vector![0.6874, 0.2783, -0.7802], + vector![-0.6874, 0.2783, -0.7802], + vector![0.64, 0.2783, 1.0254], + vector![-0.64, 0.2783, 1.0254], + ]; + // TODO: lower center of mass? + // let mut center_of_mass = wheel_params.iter().sum().unwrap() / 4.0; + // center_of_mass.y = 0.0; + + let suspension_height = 0.12; + let max_steering_angle = 35.0f32.to_radians(); + let drive_strength = 1.0; + let wheel_radius = 0.28; + let car_position = point![0.0, wheel_radius + suspension_height, 0.0]; + let body_position_in_car_space = vector![0.0, 0.4739, 0.0]; + + let body_position = car_position + body_position_in_car_space; + + let body_co = ColliderBuilder::cuboid(0.65, 0.3, 0.9) + .density(100.0) + .collision_groups(InteractionGroups::new(CAR_GROUP, !CAR_GROUP)); + let body_rb = RigidBodyBuilder::dynamic() + .position(body_position.into()) + .build(); + let body_handle = bodies.insert(body_rb); + colliders.insert_with_parent(body_co, body_handle, &mut bodies); + + let mut steering_joints = vec![]; + let mut motor_joints = vec![]; + + for (wheel_id, wheel_pos_in_car_space) in wheel_params.into_iter().enumerate() { + let is_front = wheel_id >= 2; + let wheel_center = car_position + wheel_pos_in_car_space; + + let axle_mass_props = MassProperties::from_ball(100.0, wheel_radius); + let axle_rb = RigidBodyBuilder::dynamic() + .position(wheel_center.into()) + .additional_mass_properties(axle_mass_props); + let axle_handle = bodies.insert(axle_rb); + + // This is a fake cylinder collider that we add only because our testbed can + // only render colliders. Setting it as sensor makes it show up as wireframe. + let wheel_fake_co = ColliderBuilder::cylinder(wheel_radius / 2.0, wheel_radius) + .rotation(Vector::z() * std::f32::consts::FRAC_PI_2) + .sensor(true) + .density(0.0) + .collision_groups(InteractionGroups::none()); + + // The actual wheel collider. Simulating the wheel as a ball is interesting as it + // is mathematically simpler than a cylinder and cheaper to compute for collision-detection. + let wheel_co = ColliderBuilder::ball(wheel_radius) + .density(100.0) + .collision_groups(InteractionGroups::new(CAR_GROUP, !CAR_GROUP)) + .friction(1.0); + let wheel_rb = RigidBodyBuilder::dynamic().position(wheel_center.into()); + let wheel_handle = bodies.insert(wheel_rb); + colliders.insert_with_parent(wheel_co, wheel_handle, &mut bodies); + colliders.insert_with_parent(wheel_fake_co, wheel_handle, &mut bodies); + + let suspension_attachment_in_body_space = + wheel_pos_in_car_space - body_position_in_car_space; + + // Suspension between the body and the axle. + let mut locked_axes = + JointAxesMask::X | JointAxesMask::Z | JointAxesMask::ANG_X | JointAxesMask::ANG_Z; + if !is_front { + locked_axes |= JointAxesMask::ANG_Y; + } + + let mut suspension_joint = GenericJointBuilder::new(locked_axes) + .limits(JointAxis::Y, [0.0, suspension_height]) + .motor_position(JointAxis::Y, 0.0, 1.0e4, 1.0e3) + .local_anchor1(suspension_attachment_in_body_space.into()); + + if is_front { + suspension_joint = + suspension_joint.limits(JointAxis::AngY, [-max_steering_angle, max_steering_angle]); + } + + let body_axle_joint_handle = + impulse_joints.insert(body_handle, axle_handle, suspension_joint, true); + + // Joint between the axle and the wheel. + let wheel_joint = RevoluteJointBuilder::new(Vector::x_axis()); + let wheel_joint_handle = + impulse_joints.insert(axle_handle, wheel_handle, wheel_joint, true); + + if is_front { + steering_joints.push(body_axle_joint_handle); + motor_joints.push(wheel_joint_handle); + } + } + + /* + * Callback to control the wheels motors. + */ + testbed.add_callback(move |gfx, physics, _, _| { + let Some(gfx) = gfx else { return }; + + let mut thrust = 0.0; + let mut steering = 0.0; + let mut boost = 1.0; + + for key in gfx.keys().get_pressed() { + match *key { + KeyCode::Right => { + steering = -1.0; + } + KeyCode::Left => { + steering = 1.0; + } + KeyCode::Up => { + thrust = -drive_strength; + } + KeyCode::Down => { + thrust = drive_strength; + } + KeyCode::ShiftRight => { + boost = 1.5; + } + _ => {} + } + } + + if thrust != 0.0 || steering != 0.0 { + physics.bodies.get_mut(body_handle).unwrap().wake_up(true); + } + + // Apply steering to the axles. + for steering_handle in &steering_joints { + let steering_joint = physics.impulse_joints.get_mut(*steering_handle).unwrap(); + steering_joint.data.set_motor_position( + JointAxis::AngY, + max_steering_angle * steering, + 1.0e4, + 1.0e3, + ); + } + + // Apply thrust. + // Pseudo-differential adjusting speed of engines depending on steering arc + // Higher values result in more drifty behavior. + let differential_strength = 0.5; + let sideways_shift = (max_steering_angle * steering).sin() * differential_strength; + let speed_diff = if sideways_shift > 0.0 { + f32::hypot(1.0, sideways_shift) + } else { + 1.0 / f32::hypot(1.0, sideways_shift) + }; + + let ms = [1.0 / speed_diff, speed_diff]; + for (motor_handle, &ms) in motor_joints.iter().copied().zip(ms.iter()) { + let motor_joint = physics.impulse_joints.get_mut(motor_handle).unwrap(); + motor_joint.data.set_motor_velocity( + JointAxis::AngX, + -30.0 * thrust * ms * boost, + 1.0e2, + ); + } + }); + + /* + * Create some cubes on the ground. + */ + // let num = 8; + // let rad = 0.1; + // + // let shift = rad * 2.0; + // let centerx = shift * (num / 2) as f32; + // let centery = rad; + // + // for j in 0usize..1 { + // for k in 0usize..4 { + // for i in 0..num { + // let x = i as f32 * shift - centerx; + // let y = j as f32 * shift + centery; + // let z = k as f32 * shift + centerx; + // + // let rigid_body = RigidBodyBuilder::dynamic().translation(vector![x, y, z]); + // let handle = bodies.insert(rigid_body); + // let collider = ColliderBuilder::cuboid(rad, rad, rad); + // colliders.insert_with_parent(collider, handle, &mut bodies); + // } + // } + // } + + /* + * Set up the testbed. + */ + testbed.set_world(bodies, colliders, impulse_joints, multibody_joints); + testbed.look_at(point!(10.0, 10.0, 10.0), Point::origin()); +} |
