aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md23
-rw-r--r--benchmarks3d/all_benchmarks3.rs2
-rw-r--r--benchmarks3d/many_static3.rs52
-rw-r--r--crates/rapier2d-f64/Cargo.toml2
-rw-r--r--crates/rapier2d/Cargo.toml2
-rw-r--r--crates/rapier3d-f64/Cargo.toml2
-rw-r--r--crates/rapier3d/Cargo.toml2
-rw-r--r--examples3d/all_examples3.rs2
-rw-r--r--examples3d/vehicle_controller3.rs160
-rw-r--r--src/control/character_controller.rs2
-rw-r--r--src/control/mod.rs6
-rw-r--r--src/control/ray_cast_vehicle_controller.rs808
-rw-r--r--src/dynamics/ccd/ccd_solver.rs2
-rw-r--r--src/dynamics/island_manager.rs23
-rw-r--r--src/dynamics/joint/generic_joint.rs37
-rw-r--r--src/dynamics/joint/impulse_joint/impulse_joint_set.rs46
-rw-r--r--src/dynamics/joint/multibody_joint/multibody_joint_set.rs17
-rw-r--r--src/dynamics/rigid_body.rs46
-rw-r--r--src/dynamics/rigid_body_components.rs16
-rw-r--r--src/dynamics/solver/velocity_constraint.rs1
-rw-r--r--src/dynamics/solver/velocity_ground_constraint.rs1
-rw-r--r--src/geometry/broad_phase_multi_sap/broad_phase.rs16
-rw-r--r--src/geometry/broad_phase_qbvh.rs88
-rw-r--r--src/geometry/collider.rs41
-rw-r--r--src/geometry/collider_components.rs18
-rw-r--r--src/geometry/collider_set.rs26
-rw-r--r--src/geometry/mod.rs6
-rw-r--r--src/geometry/narrow_phase.rs10
-rw-r--r--src/pipeline/collision_pipeline.rs20
-rw-r--r--src/pipeline/physics_pipeline.rs57
-rw-r--r--src/pipeline/query_pipeline.rs129
-rw-r--r--src/pipeline/user_changes.rs224
-rw-r--r--src_testbed/harness/mod.rs8
-rw-r--r--src_testbed/testbed.rs73
34 files changed, 1742 insertions, 226 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5d31a08..72c99a1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,8 +1,29 @@
+
+## Unreleased
+### Added
+- Add `RigidBody::set_enabled`, `RigidBody::is_enabled`, `RigidBodyBuilder::enabled` to enable/disable a rigid-body
+ without having to delete it. Disabling a rigid-body attached to a multibody joint isn’t supported yet.
+- Add `Collider::set_enabled`, `Collider::is_enabled`, `ColliderBuilder::enabled` to enable/disable a collider
+ without having to delete it.
+- Add `GenericJoint::set_enabled`, `GenericJoint::is_enabled` to enable/disable a joint without having to delete it.
+ Disabling a multibody joint isn’t supported yet.
+- Add `DynamicRayCastVehicleController`, a vehicle controller based on ray-casting and dynamic rigid-bodies (mostly
+ a port of the vehicle controller from Bullet physics).
+
+### Modified
+- Add the `QueryPipeline` as an optional argument to `PhysicsPipeline::step` and `CollisionPipeline::step`. If this
+ argument is specified, then the query pipeline will be incrementally (i.e. more efficiently) update at the same time as
+ these other pipelines. In that case, calling `QueryPipeline::update` a `PhysicsPipeline::step` isn’t needed.
+- `RigidBody::set_body_type` now takes an extra boolean argument indicating if the rigid-body should be woken-up
+ (if it becomes dynamic).
+
+### Fix
+- Fix bug resulting in rigid-bodies being awakened after they are created, even if they are created sleeping.
+
## v0.16.1 (10 Nov. 2022)
### Fix
- Fixed docs build on `docs.rs`.
-
## v0.16.0 (30 Oct. 2022)
### Added
- Implement `Copy` for `CharacterCollision`.
diff --git a/benchmarks3d/all_benchmarks3.rs b/benchmarks3d/all_benchmarks3.rs
index 6a3f756..bca730b 100644
--- a/benchmarks3d/all_benchmarks3.rs
+++ b/benchmarks3d/all_benchmarks3.rs
@@ -20,6 +20,7 @@ mod joint_fixed3;
mod joint_prismatic3;
mod joint_revolute3;
mod keva3;
+mod many_static3;
mod pyramid3;
mod stacks3;
mod trimesh3;
@@ -54,6 +55,7 @@ pub fn main() {
("CCD", ccd3::init_world),
("Compound", compound3::init_world),
("Convex polyhedron", convex_polyhedron3::init_world),
+ ("Many static", many_static3::init_world),
("Heightfield", heightfield3::init_world),
("Stacks", stacks3::init_world),
("Pyramid", pyramid3::init_world),
diff --git a/benchmarks3d/many_static3.rs b/benchmarks3d/many_static3.rs
new file mode 100644
index 0000000..3a0dbea
--- /dev/null
+++ b/benchmarks3d/many_static3.rs
@@ -0,0 +1,52 @@
+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();
+
+ /*
+ * Create the balls
+ */
+ let num = 50;
+ let rad = 1.0;
+
+ let shift = rad * 2.0 + 1.0;
+ let centerx = shift * (num as f32) / 2.0;
+ let centery = shift / 2.0;
+ let centerz = shift * (num as f32) / 2.0;
+
+ for i in 0..num {
+ for j in 0usize..num {
+ for k in 0..num {
+ let x = i as f32 * shift - centerx;
+ let y = j as f32 * shift + centery;
+ let z = k as f32 * shift - centerz;
+
+ let status = if j < num - 1 {
+ RigidBodyType::Fixed
+ } else {
+ RigidBodyType::Dynamic
+ };
+ let density = 0.477;
+
+ // Build the rigid body.
+ let rigid_body = RigidBodyBuilder::new(status).translation(vector![x, y, z]);
+ let handle = bodies.insert(rigid_body);
+ let collider = ColliderBuilder::ball(rad).density(density);
+ 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![100.0, 100.0, 100.0], Point::origin());
+}
diff --git a/crates/rapier2d-f64/Cargo.toml b/crates/rapier2d-f64/Cargo.toml
index b949095..60ea169 100644
--- a/crates/rapier2d-f64/Cargo.toml
+++ b/crates/rapier2d-f64/Cargo.toml
@@ -52,7 +52,7 @@ vec_map = { version = "0.8", optional = true }
instant = { version = "0.1", features = [ "now" ], optional = true }
num-traits = "0.2"
nalgebra = "0.31"
-parry2d-f64 = "^0.11.1"
+parry2d-f64 = "0.12"
simba = "0.7"
approx = "0.5"
rayon = { version = "1", optional = true }
diff --git a/crates/rapier2d/Cargo.toml b/crates/rapier2d/Cargo.toml
index 2cc9004..115069c 100644
--- a/crates/rapier2d/Cargo.toml
+++ b/crates/rapier2d/Cargo.toml
@@ -52,7 +52,7 @@ vec_map = { version = "0.8", optional = true }
instant = { version = "0.1", features = [ "now" ], optional = true }
num-traits = "0.2"
nalgebra = "0.31"
-parry2d = "^0.11.1"
+parry2d = "0.12"
simba = "0.7"
approx = "0.5"
rayon = { version = "1", optional = true }
diff --git a/crates/rapier3d-f64/Cargo.toml b/crates/rapier3d-f64/Cargo.toml
index 929e688..79c2783 100644
--- a/crates/rapier3d-f64/Cargo.toml
+++ b/crates/rapier3d-f64/Cargo.toml
@@ -52,7 +52,7 @@ vec_map = { version = "0.8", optional = true }
instant = { version = "0.1", features = [ "now" ], optional = true }
num-traits = "0.2"
nalgebra = "0.31"
-parry3d-f64 = "^0.11.1"
+parry3d-f64 = "0.12"
simba = "0.7"
approx = "0.5"
rayon = { version = "1", optional = true }
diff --git a/crates/rapier3d/Cargo.toml b/crates/rapier3d/Cargo.toml
index 942e618..88c5281 100644
--- a/crates/rapier3d/Cargo.toml
+++ b/crates/rapier3d/Cargo.toml
@@ -52,7 +52,7 @@ vec_map = { version = "0.8", optional = true }
instant = { version = "0.1", features = [ "now" ], optional = true }
num-traits = "0.2"
nalgebra = "0.31"
-parry3d = "^0.11.1"
+parry3d = "0.12"
simba = "0.7"
approx = "0.5"
rayon = { version = "1", optional = true }
diff --git a/examples3d/all_examples3.rs b/examples3d/all_examples3.rs
index 749cec0..194d7cb 100644
--- a/examples3d/all_examples3.rs
+++ b/examples3d/all_examples3.rs
@@ -44,6 +44,7 @@ mod primitives3;
mod restitution3;
mod sensor3;
mod trimesh3;
+mod vehicle_controller3;
fn demo_name_from_command_line() -> Option<String> {
let mut args = std::env::args();
@@ -101,6 +102,7 @@ pub fn main() {
("Restitution", restitution3::init_world),
("Sensor", sensor3::init_world),
("TriMesh", trimesh3::init_world),
+ ("Vehicle controller", vehicle_controller3::init_world),
("Keva tower", keva3::init_world),
("Newton cradle", newton_cradle3::init_world),
("(Debug) multibody_joints", debug_articulations3::init_world),
diff --git a/examples3d/vehicle_controller3.rs b/examples3d/vehicle_controller3.rs
new file mode 100644
index 0000000..1228c82
--- /dev/null
+++ b/examples3d/vehicle_controller3.rs
@@ -0,0 +1,160 @@
+use rapier3d::control::{DynamicRayCastVehicleController, WheelTuning};
+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();
+
+ /*
+ * Ground
+ */
+ let ground_size = 5.0;
+ let ground_height = 0.1;
+
+ let rigid_body = RigidBodyBuilder::fixed().translation(vector![0.0, -ground_height, 0.0]);
+ let floor_handle = bodies.insert(rigid_body);
+ let collider = ColliderBuilder::cuboid(ground_size, ground_height, ground_size);
+ colliders.insert_with_parent(collider, floor_handle, &mut bodies);
+
+ /*
+ * Vehicle we will control manually.
+ */
+ let hw = 0.3;
+ let hh = 0.15;
+ let rigid_body = RigidBodyBuilder::dynamic().translation(vector![0.0, 1.0, 0.0]);
+ let vehicle_handle = bodies.insert(rigid_body);
+ let collider = ColliderBuilder::cuboid(hw * 2.0, hh, hw).density(100.0);
+ colliders.insert_with_parent(collider, vehicle_handle, &mut bodies);
+
+ let mut tuning = WheelTuning::default();
+ tuning.suspension_stiffness = 100.0;
+ tuning.suspension_damping = 10.0;
+ let mut vehicle = DynamicRayCastVehicleController::new(vehicle_handle);
+ let wheel_positions = [
+ point![hw * 1.5, -hh, hw],
+ point![hw * 1.5, -hh, -hw],
+ point![-hw * 1.5, -hh, hw],
+ point![-hw * 1.5, -hh, -hw],
+ ];
+
+ for pos in wheel_positions {
+ vehicle.add_wheel(pos, -Vector::y(), Vector::z(), hh, hh / 4.0, &tuning);
+ }
+
+ /*
+ * Create the cubes
+ */
+ 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);
+ }
+ }
+ }
+
+ /*
+ * Create a slope we can climb.
+ */
+ let slope_angle = 0.2;
+ let slope_size = 2.0;
+ let collider = ColliderBuilder::cuboid(slope_size, ground_height, ground_size)
+ .translation(vector![ground_size + slope_size, -ground_height + 0.4, 0.0])
+ .rotation(Vector::z() * slope_angle);
+ colliders.insert(collider);
+
+ /*
+ * Create a slope we can’t climb.
+ */
+ let impossible_slope_angle = 0.9;
+ let impossible_slope_size = 2.0;
+ let collider = ColliderBuilder::cuboid(slope_size, ground_height, ground_size)
+ .translation(vector![
+ ground_size + slope_size * 2.0 + impossible_slope_size - 0.9,
+ -ground_height + 2.3,
+ 0.0
+ ])
+ .rotation(Vector::z() * impossible_slope_angle);
+ colliders.insert(collider);
+
+ // /*
+ // * Create a moving platform.
+ // */
+ // let body = RigidBodyBuilder::kinematic_velocity_based().translation(vector![-8.0, 1.5, 0.0]);
+ // // .rotation(-0.3);
+ // let platform_handle = bodies.insert(body);
+ // let collider = ColliderBuilder::cuboid(2.0, ground_height, 2.0);
+ // colliders.insert_with_parent(collider, platform_handle, &mut bodies);
+
+ /*
+ * More complex ground.
+ */
+ let ground_size = Vector::new(10.0, 0.4, 10.0);
+ let nsubdivs = 20;
+
+ 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]);
+ colliders.insert(collider);
+
+ // /*
+ // * A tilting dynamic body with a limited joint.
+ // */
+ // let ground = RigidBodyBuilder::fixed().translation(vector![0.0, 5.0, 0.0]);
+ // let ground_handle = bodies.insert(ground);
+ // let body = RigidBodyBuilder::dynamic().translation(vector![0.0, 5.0, 0.0]);
+ // let handle = bodies.insert(body);
+ // let collider = ColliderBuilder::cuboid(1.0, 0.1, 2.0);
+ // colliders.insert_with_parent(collider, handle, &mut bodies);
+ // let joint = RevoluteJointBuilder::new(Vector::z_axis()).limits([-0.3, 0.3]);
+ // impulse_joints.insert(ground_handle, handle, joint, true);
+
+ // /*
+ // * Setup a callback to move the platform.
+ // */
+ // testbed.add_callback(move |_, physics, _, run_state| {
+ // let linvel = vector![
+ // (run_state.time * 2.0).sin() * 2.0,
+ // (run_state.time * 5.0).sin() * 1.5,
+ // 0.0
+ // ];
+ // // let angvel = run_state.time.sin() * 0.5;
+ //
+ // // Update the velocity-based kinematic body by setting its velocity.
+ // if let Some(platform) = physics.bodies.get_mut(platform_handle) {
+ // platform.set_linvel(linvel, true);
+ // // NOTE: interaction with rotating platforms isn’t handled very well yet.
+ // // platform.set_angvel(angvel, true);
+ // }
+ // });
+
+ /*
+ * Set up the testbed.
+ */
+ testbed.set_world(bodies, colliders, impulse_joints, multibody_joints);
+ testbed.set_vehicle_controller(vehicle);
+ testbed.look_at(point!(10.0, 10.0, 10.0), Point::origin());
+}
diff --git a/src/control/character_controller.rs b/src/control/character_controller.rs
index ebed952..28ba475 100644
--- a/src/control/character_controller.rs
+++ b/src/control/character_controller.rs
@@ -133,7 +133,7 @@ impl Default for KinematicCharacterController {
pub struct EffectiveCharacterMovement {
/// The movement to apply.
pub translation: Vector<Real>,
- /// Is the character touching the ground after applying `EffictiveKineamticMovement::translation`?
+ /// Is the character touching the ground after applying `EffectiveKineamticMovement::translation`?
pub grounded: bool,
}
diff --git a/src/control/mod.rs b/src/control/mod.rs
index 0290f2c..3f7dea1 100644
--- a/src/control/mod.rs
+++ b/src/control/mod.rs
@@ -5,4 +5,10 @@ pub use self::character_controller::{
KinematicCharacterController,
};
+#[cfg(feature = "dim3")]
+pub use self::ray_cast_vehicle_controller::{DynamicRayCastVehicleController, Wheel, WheelTuning};
+
mod character_controller;
+
+#[cfg(feature = "dim3")]
+mod ray_cast_vehicle_controller;
diff --git a/src/control/ray_cast_vehicle_controller.rs b/src/control/ray_cast_vehicle_controller.rs
new file mode 100644
index 0000000..42bb112
--- /dev/null
+++ b/src/control/ray_cast_vehicle_controller.rs
@@ -0,0 +1,808 @@
+//! A vehicle controller based on ray-casting, ported and modified from Bullet’s `btRaycastVehicle`.
+
+use crate::dynamics::{RigidBody, RigidBodyHandle, RigidBodySet};
+use crate::geometry::{ColliderHandle, ColliderSet, Ray};
+use crate::math::{Point, Real, Rotation, Vector};
+use crate::pipeline::{QueryFilter, QueryPipeline};
+use crate::utils::{WCross, WDot};
+
+/// A character controller to simulate vehicles using ray-casting for the wheels.
+pub struct DynamicRayCastVehicleController {
+ wheels: Vec<Wheel>,
+ forward_ws: Vec<Vector<Real>>,
+ axle: Vec<Vector<Real>>,
+ /// The current forward speed of the vehicle.
+ pub current_vehicle_speed: Real,
+
+ /// Handle of the vehicle’s chassis.
+ pub chassis: RigidBodyHandle,
+ /// The chassis’ local _up_ direction (`0 = x, 1 = y, 2 = z`)
+ pub index_up_axis: usize,
+ /// The chassis’ local _forward_ direction (`0 = x, 1 = y, 2 = z`)
+ pub index_forward_axis: usize,
+}
+
+#[derive(Copy, Clone, Debug, PartialEq)]
+/// Parameters affecting the physical behavior of a wheel.
+pub struct WheelTuning {
+ /// The suspension stiffness.
+ ///
+ /// Increase this value if the suspension appears to not push the vehicle strong enough.
+ pub suspension_stiffness: Real,
+ /// The suspension’s damping when it is being compressed.
+ pub suspension_compression: Real,
+ /// The suspension’s damping when it is being released.
+ ///
+ /// Increase this value if the suspension appears to overshoot.
+ pub suspension_damping: Real,
+ /// The maximum distance the suspension can travel before and after its resting length.
+ pub max_suspension_travel: Real,
+ /// Parameter controlling how much traction the tire his.
+ ///
+ /// The larger the value, the more instantaneous braking will happen (with the risk of
+ /// causing the vehicle to flip if it’s too strong).
+ pub friction_slip: Real,
+ /// The maximum force applied by the suspension.
+ pub max_suspension_force: Real,
+}
+
+impl Default for WheelTuning {
+ fn default() -> Self {
+ Self {
+ suspension_stiffness: 5.88,
+ suspension_compression: 0.83,
+ suspension_damping: 0.88,
+ max_suspension_travel: 5.0,
+ friction_slip: 10.5,
+ max_suspension_force: 6000.0,
+ }
+ }
+}
+
+/// Objects used to initialize a wheel.
+struct WheelDesc {
+ /// The position of the wheel, relative to the chassis.
+ pub chassis_connection_cs: Point<Real>,
+ /// The direction of the wheel’s suspension, relative to the chassis.
+ ///
+ /// The ray-casting will happen following this direction to detect the ground.
+ pub direction_cs: Vector<Real>,
+ /// The wheel’s axle axis, relative to the chassis.
+ pub axle_cs: Vector<Real>,
+ /// The rest length of the wheel’s suspension spring.
+ pub suspension_rest_length: Real,
+ /// The maximum distance the suspension can travel before and after its resting length.
+ pub max_suspension_travel: Real,
+ /// The wheel’s radius.
+ pub radius: Real,
+
+ /// The suspension stiffness.
+ ///
+ /// Increase this value if the suspension appears to not push the vehicle strong enough.
+ pub suspension_stiffness: Real,
+ /// The suspension’s damping when it is being compressed.
+ pub damping_compression: Real,
+ /// The suspension’s damping when it is being released.
+ ///
+ /// Increase this value if the suspension appears to overshoot.
+ pub damping_relaxation: Real,
+ /// Parameter controlling how much traction the tire his.
+ ///
+ /// The larger the value, the more instantaneous braking will happen (with the risk of
+ /// causing the vehicle to flip if it’s too strong).
+ pub friction_slip: Real,
+ /// The maximum force applied by the suspension.
+ pub max_suspension_force: Real,
+}
+
+#[derive(Copy, Clone, Debug, PartialEq)]
+/// A wheel attached to a vehicle.
+pub struct Wheel {
+ raycast_info: RayCastInfo,
+
+ center: Point<Real>,
+ wheel_direction_ws: Vector<Real>,
+ wheel_axle_ws: Vector<Real>,
+
+ /// The position of the wheel, relative to the chassis.
+ pub chassis_connection_point_cs: Point<Real>,
+ /// The direction of the wheel’s suspension, relative to the chassis.
+ ///
+ /// The ray-casting will happen following this direction to detect the ground.
+ pub direction_cs: Vector<Real>,
+ /// The wheel’s axle axis, relative to the chassis.
+ pub axle_cs: Vector<Real>,
+ /// The rest length of the wheel’s suspension spring.
+ pub suspension_rest_length: Real,
+ /// The maximum distance the suspension can travel before and after its resting length.
+ pub max_suspension_travel: Real,
+ /// The wheel’s radius.
+ pub radius: Real,
+ /// The suspension stiffness.
+ ///
+ /// Increase this value if the suspension appears to not push the vehicle strong enough.
+ pub suspension_stiffness: Real,
+ /// The suspension’s damping when it is being compressed.
+ pub damping_compression: Real,
+ /// The suspension’s damping when it is being released.
+ ///
+ /// Increase this value if the suspension appears to overshoot.
+ pub damping_relaxation: Real,
+ /// Parameter controlling how much traction the tire his.
+ ///
+ /// The larger the value, the more instantaneous braking will happen (with the risk of
+ /// causing the vehicle to flip if it’s too strong).
+ friction_slip: Real,
+ /// The wheel’s current rotation on its axle.
+ pub rotation: Real,
+ delta_rotation: Real,
+ roll_influence: Real, // TODO: make this public?
+ /// The maximum force applied by the suspension.
+ pub max_suspension_force: Real,
+
+ /// The forward impulses applied by the wheel on the chassis.
+ pub forward_impulse: Real,
+ /// The side impulses applied by the wheel on the chassis.
+ pub side_impulse: Real,
+
+ /// The steering angle for this wheel.
+ pub steering: Real,
+ /// The forward force applied by this wheel on the chassis.
+ pub engine_force: Real,
+ /// The maximum amount of braking impulse applied to slow down the vehicle.
+ pub brake: Real,
+
+ clipped_inv_contact_dot_suspension: Real,
+ suspension_relative_velocity: Real,
+ /// The force applied by the suspension.
+ pub wheel_suspension_force: Real,
+ skid_info: Real,
+}
+
+impl Wheel {
+ fn new(info: WheelDesc) -> Self {
+ Self {
+ raycast_info: RayCastInfo::default(),
+ suspension_rest_length: info.suspension_rest_length,
+ max_suspension_travel: info.max_suspension_travel,
+ radius: info.radius,
+ suspension_stiffness: info.suspension_stiffness,
+ damping_compression: info.damping_compression,
+ damping_relaxation: info.damping_relaxation,
+ chassis_connection_point_cs: info.chassis_connection_cs,
+ direction_cs: info.direction_cs,
+ axle_cs: info.axle_cs,
+ wheel_direction_ws: info.direction_cs,
+ wheel_axle_ws: info.axle_cs,
+ center: Point::origin(),
+ friction_slip: info.friction_slip,
+ steering: 0.0,
+ engine_force: 0.0,
+ rotation: 0.0,
+ delta_rotation: 0.0,
+ brake: 0.0,
+ roll_influence: 0.1,
+ clipped_inv_contact_dot_suspension: 0.0,
+ suspension_relative_velocity: 0.0,
+ wheel_suspension_force: 0.0,
+ max_suspension_force: info.max_suspension_force,
+ skid_info: 0.0,
+ side_impulse: 0.0,
+ forward_impulse: 0.0,
+ }
+ }
+
+ /// The world-space center of the wheel.
+ pub fn center(&self) -> Point<Real> {
+ self.center
+ }
+
+ /// The world-space direction of the wheel’s suspension.
+ pub fn suspension(&self) -> Vector<Real> {
+ self.wheel_direction_ws
+ }
+
+ /// The world-space direction of the wheel’s axle.
+ pub fn axle(&self) -> Vector<Real> {
+ self.wheel_axle_ws
+ }
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Default)]
+struct RayCastInfo {
+ // set by raycaster
+ contact_normal_ws: Vector<Real>, //contact normal
+ contact_point_ws: Point<Real>, //raycast hitpoint
+ suspension_length: Real,
+ hard_point_ws: Point<Real>, //raycast starting point
+ is_in_contact: bool,
+ ground_object: Option<ColliderHandle>,
+}
+
+impl DynamicRayCastVehicleController {
+ /// Creates a new vehicle represented by the given rigid-body.
+ ///
+ /// Wheels have to be attached afterwards calling [`Self::add_wheel`].
+ pub fn new(chassis: RigidBodyHandle) -> Self {
+ Self {
+ wheels: vec![],
+ forward_ws: vec![],
+ axle: vec![],
+ current_vehicle_speed: 0.0,
+ chassis,
+ index_up_axis: 1,
+ index_forward_axis: 0,
+ }
+ }
+
+ //
+ // basically most of the code is general for 2 or 4 wheel vehicles, but some of it needs to be reviewed
+ //
+ /// Adds a wheel to this vehicle.
+ pub fn add_wheel(
+ &mut self,
+ chassis_connection_cs: Point<Real>,
+ direction_cs: Vector<Real>,
+ axle_cs: Vector<Real>,
+ suspension_rest_length: Real,
+ radius: Real,
+ tuning: &WheelTuning,
+ ) -> &mut Wheel {
+ let ci = WheelDesc {
+ chassis_connection_cs,
+ direction_cs,
+ axle_cs,
+ suspension_rest_length,
+ radius,
+ suspension_stiffness: tuning.suspension_stiffness,
+ damping_compression: tuning.suspension_compression,
+ damping_relaxation: tuning.suspension_damping,
+ friction_slip: tuning.friction_slip,
+ max_suspension_travel: tuning.max_suspension_travel,
+ max_suspension_force: tuning.max_suspension_force,
+ };
+
+ let wheel_id = self.wheels.len();
+ self.wheels.push(Wheel::new(ci));
+
+ &mut self.wheels[wheel_id]
+ }
+
+ #[cfg(feature = "dim2")]
+ fn update_wheel_transform(&mut self, chassis: &RigidBody, wheel_index: usize) {
+ self.update_wheel_transforms_ws(chassis, wheel_index);
+ let wheel = &mut self.wheels[wheel_index];
+ wheel.center = (wheel.raycast_info.hard_point_ws
+ + wheel.wheel_direction_ws * wheel.raycast_info.suspension_length)
+ .coords;
+ }
+
+ #[cfg(feature = "dim3")]
+ fn update_wheel_transform(&mut self, chassis: &RigidBody, wheel_index: usize) {
+ self.update_wheel_transforms_ws(chassis, wheel_index);
+ let wheel = &mut self.wheels[wheel_index];
+
+ let steering_orn = Rotation::new(-wheel.wheel_direction_ws * wheel.steering);
+ wheel.wheel_axle_ws = steering_orn * (chassis.position() * wheel.axle_cs);
+ wheel.center = wheel.raycast_info.hard_point_ws
+ + wheel.wheel_direction_ws * wheel.raycast_info.suspension_length;
+ }
+
+ fn update_wheel_transforms_ws(&mut self, chassis: &RigidBody, wheel_id: usize) {
+ let wheel = &mut self.wheels[wheel_id];
+ wheel.raycast_info.is_in_contact = false;
+
+ let chassis_transform = chassis.position();
+
+ wheel.raycast_info.hard_point_ws = chassis_transform * wheel.chassis_connection_point_cs;
+ wheel.wheel_direction_ws = chassis_transform * wheel.direction_cs;
+ wheel.wheel_axle_ws = chassis_transform * wheel.axle_cs;
+ }
+
+ fn ray_cast(
+ &mut self,
+ bodies: &RigidBodySet,
+ colliders: &ColliderSet,
+ queries: &QueryPipeline,
+ filter: QueryFilter,
+ chassis: &RigidBody,
+ wheel_id: usize,
+ ) {
+ let wheel = &mut self.wheels[wheel_id];
+ let raylen = wheel.suspension_rest_length + wheel.radius;
+ let rayvector = wheel.wheel_direction_ws * raylen;
+ let source = wheel.raycast_info.hard_point_ws;
+ wheel.raycast_info.contact_point_ws = source + rayvector;
+ let ray = Ray::new(source, rayvector);
+ let hit = queries.cast_ray_and_get_normal(bodies, colliders, &ray, 1.0, true, filter);
+
+ wheel.raycast_info.ground_object = None;
+
+ if let Some((collider_hit, mut hit)) = hit {
+ if hit.toi == 0.0 {
+ let collider = &colliders[collider_hit];
+ let up_ray = Ray::new(source + rayvector, -rayvector);
+ if let Some(hit2) =
+ collider
+ .shape
+ .cast_ray_and_get_normal(collider.position(), &up_ray, 1.0, false)
+ {
+ hit.normal = -hit2.normal;
+ }
+
+ if hit.normal == Vector::zeros() {
+ // If the hit is still not defined, set the normal.
+ hit.normal = -wheel.wheel_direction_ws;
+ }
+ }
+
+ wheel.raycast_info.contact_normal_ws = hit.normal;
+ wheel.raycast_info.is_in_contact = true;
+ wheel.raycast_info.ground_object = Some(collider_hit);
+
+ let hit_distance = hit.toi * raylen;
+ wheel.raycast_info.suspension_length = hit_distance - wheel.radius;
+
+ // clamp on max suspension travel
+ let min_suspension_length = wheel.suspension_rest_length - wheel.max_suspension_travel;
+ let max_suspension_length = wheel.suspension_rest_length + wheel.max_suspension_travel;
+ wheel.raycast_info.suspension_length = wheel
+ .raycast_info
+ .suspension_length
+ .clam